diff --git a/.gitignore b/.gitignore index 32ac2c7f..dd6b98c0 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,8 @@ backend/uv-bin/ # Pre-built webapp-template node_modules archive (generated by # scripts/build-template-archive.sh, optionally bundled into the DMG). backend/apps/outputs/webapp_template_cache/ -# Backend Python venv (created by run.ps1 / backend/run.sh) +# Python virtualenvs anywhere (a root .venv was accidentally committed once) +.venv/ backend/.venv/ .account-factory openswarm-cloud diff --git a/.venv/bin/Activate.ps1 b/.venv/bin/Activate.ps1 deleted file mode 100644 index b49d77ba..00000000 --- a/.venv/bin/Activate.ps1 +++ /dev/null @@ -1,247 +0,0 @@ -<# -.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 deleted file mode 100644 index 88183299..00000000 --- a/.venv/bin/activate +++ /dev/null @@ -1,70 +0,0 @@ -# This file must be used with "source bin/activate" *from bash* -# You cannot run it directly - -deactivate () { - # reset old environment variables - if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then - PATH="${_OLD_VIRTUAL_PATH:-}" - export PATH - unset _OLD_VIRTUAL_PATH - fi - if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then - PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" - export PYTHONHOME - unset _OLD_VIRTUAL_PYTHONHOME - fi - - # Call hash to forget past commands. Without forgetting - # past commands the $PATH changes we made may not be respected - hash -r 2> /dev/null - - if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then - PS1="${_OLD_VIRTUAL_PS1:-}" - export PS1 - unset _OLD_VIRTUAL_PS1 - fi - - unset VIRTUAL_ENV - unset VIRTUAL_ENV_PROMPT - if [ ! "${1:-}" = "nondestructive" ] ; then - # Self destruct! - unset -f deactivate - fi -} - -# unset irrelevant variables -deactivate nondestructive - -# on Windows, a path can contain colons and backslashes and has to be converted: -if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then - # transform D:\path\to\venv to /d/path/to/venv on MSYS - # and to /cygdrive/d/path/to/venv on Cygwin - export VIRTUAL_ENV=$(cygpath "/Users/ericzeng/openswarm/.venv") -else - # use the path as-is - export VIRTUAL_ENV="/Users/ericzeng/openswarm/.venv" -fi - -_OLD_VIRTUAL_PATH="$PATH" -PATH="$VIRTUAL_ENV/bin:$PATH" -export PATH - -# unset PYTHONHOME if set -# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) -# could use `if (set -u; : $PYTHONHOME) ;` in bash -if [ -n "${PYTHONHOME:-}" ] ; then - _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" - unset PYTHONHOME -fi - -if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then - _OLD_VIRTUAL_PS1="${PS1:-}" - PS1="(.venv) ${PS1:-}" - export PS1 - VIRTUAL_ENV_PROMPT="(.venv) " - export VIRTUAL_ENV_PROMPT -fi - -# Call hash to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected -hash -r 2> /dev/null diff --git a/.venv/bin/activate.csh b/.venv/bin/activate.csh deleted file mode 100644 index d9cb39f9..00000000 --- a/.venv/bin/activate.csh +++ /dev/null @@ -1,27 +0,0 @@ -# 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/ericzeng/openswarm/.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 deleted file mode 100644 index 9bf93585..00000000 --- a/.venv/bin/activate.fish +++ /dev/null @@ -1,69 +0,0 @@ -# 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/ericzeng/openswarm/.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/courlan b/.venv/bin/courlan deleted file mode 100755 index 8bb4a412..00000000 --- a/.venv/bin/courlan +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from courlan.cli 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/dateparser-download b/.venv/bin/dateparser-download deleted file mode 100755 index 769dcef8..00000000 --- a/.venv/bin/dateparser-download +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from dateparser_cli.cli import entrance -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(entrance()) diff --git a/.venv/bin/distro b/.venv/bin/distro deleted file mode 100755 index d70fcc3e..00000000 --- a/.venv/bin/distro +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from distro.distro 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/dotenv b/.venv/bin/dotenv deleted file mode 100755 index 40ceffec..00000000 --- a/.venv/bin/dotenv +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from dotenv.__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/email_validator b/.venv/bin/email_validator deleted file mode 100755 index c7383a84..00000000 --- a/.venv/bin/email_validator +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from email_validator.__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/fastapi b/.venv/bin/fastapi deleted file mode 100755 index 26c252f5..00000000 --- a/.venv/bin/fastapi +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from fastapi.cli 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/htmldate b/.venv/bin/htmldate deleted file mode 100755 index d82d1016..00000000 --- a/.venv/bin/htmldate +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from htmldate.cli 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/httpx b/.venv/bin/httpx deleted file mode 100755 index d31434c8..00000000 --- a/.venv/bin/httpx +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from httpx 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/idna b/.venv/bin/idna deleted file mode 100755 index 059db7c3..00000000 --- a/.venv/bin/idna +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from idna.cli 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/jsonschema b/.venv/bin/jsonschema deleted file mode 100755 index b95dc277..00000000 --- a/.venv/bin/jsonschema +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from jsonschema.cli 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-it b/.venv/bin/markdown-it deleted file mode 100755 index 340c7d73..00000000 --- a/.venv/bin/markdown-it +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from markdown_it.cli.parse 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/mcp b/.venv/bin/mcp deleted file mode 100755 index d8d0cb0e..00000000 --- a/.venv/bin/mcp +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from mcp.cli import app -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(app()) diff --git a/.venv/bin/normalizer b/.venv/bin/normalizer deleted file mode 100755 index 6f2289dd..00000000 --- a/.venv/bin/normalizer +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from charset_normalizer.cli 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 deleted file mode 100755 index 8970ac02..00000000 --- a/.venv/bin/pip +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv/bin/pip3 b/.venv/bin/pip3 deleted file mode 100755 index 8970ac02..00000000 --- a/.venv/bin/pip3 +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv/bin/pip3.12 b/.venv/bin/pip3.12 deleted file mode 100755 index 8970ac02..00000000 --- a/.venv/bin/pip3.12 +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv/bin/py.test b/.venv/bin/py.test deleted file mode 100755 index ff9bd7e0..00000000 --- a/.venv/bin/py.test +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from pytest import console_main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(console_main()) diff --git a/.venv/bin/pybabel b/.venv/bin/pybabel deleted file mode 100755 index 7d955d23..00000000 --- a/.venv/bin/pybabel +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from babel.messages.frontend import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv/bin/pygmentize b/.venv/bin/pygmentize deleted file mode 100755 index cd49d78c..00000000 --- a/.venv/bin/pygmentize +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from pygments.cmdline import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv/bin/pytest b/.venv/bin/pytest deleted file mode 100755 index ff9bd7e0..00000000 --- a/.venv/bin/pytest +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from pytest import console_main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(console_main()) diff --git a/.venv/bin/python b/.venv/bin/python deleted file mode 120000 index 11b9d885..00000000 --- a/.venv/bin/python +++ /dev/null @@ -1 +0,0 @@ -python3.12 \ No newline at end of file diff --git a/.venv/bin/python3 b/.venv/bin/python3 deleted file mode 120000 index 11b9d885..00000000 --- a/.venv/bin/python3 +++ /dev/null @@ -1 +0,0 @@ -python3.12 \ No newline at end of file diff --git a/.venv/bin/python3.12 b/.venv/bin/python3.12 deleted file mode 120000 index a3f05084..00000000 --- a/.venv/bin/python3.12 +++ /dev/null @@ -1 +0,0 @@ -/opt/homebrew/opt/python@3.12/bin/python3.12 \ No newline at end of file diff --git a/.venv/bin/trafilatura b/.venv/bin/trafilatura deleted file mode 100755 index 5b9f7166..00000000 --- a/.venv/bin/trafilatura +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from trafilatura.cli 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/typer b/.venv/bin/typer deleted file mode 100755 index e7ca23da..00000000 --- a/.venv/bin/typer +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from typer.cli 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/update-tld-names b/.venv/bin/update-tld-names deleted file mode 100755 index ac860ef8..00000000 --- a/.venv/bin/update-tld-names +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from tld.utils import update_tld_names_cli -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(update_tld_names_cli()) diff --git a/.venv/bin/uvicorn b/.venv/bin/uvicorn deleted file mode 100755 index 4bda0ffb..00000000 --- a/.venv/bin/uvicorn +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from uvicorn.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/vulture b/.venv/bin/vulture deleted file mode 100755 index aee68253..00000000 --- a/.venv/bin/vulture +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from vulture.core 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/watchfiles b/.venv/bin/watchfiles deleted file mode 100755 index ec44ebe0..00000000 --- a/.venv/bin/watchfiles +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from watchfiles.cli 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/websockets b/.venv/bin/websockets deleted file mode 100755 index 46b4ece9..00000000 --- a/.venv/bin/websockets +++ /dev/null @@ -1,8 +0,0 @@ -#!/Users/ericzeng/openswarm/.venv/bin/python3.12 -# -*- coding: utf-8 -*- -import re -import sys -from websockets.cli import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv/lib/python3.12/site-packages/81d243bd2c585b0f4821__mypyc.cpython-312-darwin.so b/.venv/lib/python3.12/site-packages/81d243bd2c585b0f4821__mypyc.cpython-312-darwin.so deleted file mode 100755 index 964d3f83..00000000 Binary files a/.venv/lib/python3.12/site-packages/81d243bd2c585b0f4821__mypyc.cpython-312-darwin.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libXau.6.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libXau.6.dylib deleted file mode 100755 index b7bb8511..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libXau.6.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libavif.16.4.1.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libavif.16.4.1.dylib deleted file mode 100755 index 24cfb56e..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libavif.16.4.1.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libbrotlicommon.1.2.0.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libbrotlicommon.1.2.0.dylib deleted file mode 100755 index 8909b55c..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libbrotlicommon.1.2.0.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libbrotlidec.1.2.0.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libbrotlidec.1.2.0.dylib deleted file mode 100755 index e5cee172..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libbrotlidec.1.2.0.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libfreetype.6.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libfreetype.6.dylib deleted file mode 100755 index 3b90914f..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libfreetype.6.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libharfbuzz.0.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libharfbuzz.0.dylib deleted file mode 100755 index 70818d84..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libharfbuzz.0.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libjpeg.62.4.0.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libjpeg.62.4.0.dylib deleted file mode 100755 index dae71529..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libjpeg.62.4.0.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/liblcms2.2.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/liblcms2.2.dylib deleted file mode 100755 index 4faf7ae0..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/liblcms2.2.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/liblzma.5.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/liblzma.5.dylib deleted file mode 100755 index de9e7315..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/liblzma.5.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libopenjp2.2.5.4.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libopenjp2.2.5.4.dylib deleted file mode 100755 index d936940d..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libopenjp2.2.5.4.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libpng16.16.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libpng16.16.dylib deleted file mode 100755 index 5bc8c19b..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libpng16.16.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libsharpyuv.0.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libsharpyuv.0.dylib deleted file mode 100755 index 16a92e44..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libsharpyuv.0.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libtiff.6.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libtiff.6.dylib deleted file mode 100755 index 7ee56f0e..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libtiff.6.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libwebp.7.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libwebp.7.dylib deleted file mode 100755 index 7251fd20..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libwebp.7.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libwebpdemux.2.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libwebpdemux.2.dylib deleted file mode 100755 index 4fdae2b6..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libwebpdemux.2.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libwebpmux.3.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libwebpmux.3.dylib deleted file mode 100755 index 80631df6..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libwebpmux.3.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libxcb.1.1.0.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libxcb.1.1.0.dylib deleted file mode 100755 index 95f3b14d..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libxcb.1.1.0.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libz.1.3.1.zlib-ng.dylib b/.venv/lib/python3.12/site-packages/PIL/.dylibs/libz.1.3.1.zlib-ng.dylib deleted file mode 100755 index 9c49d1b6..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/.dylibs/libz.1.3.1.zlib-ng.dylib and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/AvifImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/AvifImagePlugin.py deleted file mode 100644 index 43c39a9f..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/AvifImagePlugin.py +++ /dev/null @@ -1,293 +0,0 @@ -from __future__ import annotations - -import os -from io import BytesIO -from typing import IO - -from . import ExifTags, Image, ImageFile - -try: - from . import _avif - - SUPPORTED = True -except ImportError: - SUPPORTED = False - -# Decoder options as module globals, until there is a way to pass parameters -# to Image.open (see https://github.com/python-pillow/Pillow/issues/569) -DECODE_CODEC_CHOICE = "auto" -DEFAULT_MAX_THREADS = 0 - - -def get_codec_version(codec_name: str) -> str | None: - versions = _avif.codec_versions() - for version in versions.split(", "): - if version.split(" [")[0] == codec_name: - return version.split(":")[-1].split(" ")[0] - return None - - -def _accept(prefix: bytes) -> bool | str: - if prefix[4:8] != b"ftyp": - return False - major_brand = prefix[8:12] - if major_brand in ( - # coding brands - b"avif", - b"avis", - # We accept files with AVIF container brands; we can't yet know if - # the ftyp box has the correct compatible brands, but if it doesn't - # then the plugin will raise a SyntaxError which Pillow will catch - # before moving on to the next plugin that accepts the file. - # - # Also, because this file might not actually be an AVIF file, we - # don't raise an error if AVIF support isn't properly compiled. - b"mif1", - b"msf1", - ): - if not SUPPORTED: - return ( - "image file could not be identified because AVIF support not installed" - ) - return True - return False - - -def _get_default_max_threads() -> int: - if DEFAULT_MAX_THREADS: - return DEFAULT_MAX_THREADS - if hasattr(os, "sched_getaffinity"): - return len(os.sched_getaffinity(0)) - else: - return os.cpu_count() or 1 - - -class AvifImageFile(ImageFile.ImageFile): - format = "AVIF" - format_description = "AVIF image" - __frame = -1 - - def _open(self) -> None: - if not SUPPORTED: - msg = "image file could not be opened because AVIF support not installed" - raise SyntaxError(msg) - - if DECODE_CODEC_CHOICE != "auto" and not _avif.decoder_codec_available( - DECODE_CODEC_CHOICE - ): - msg = "Invalid opening codec" - raise ValueError(msg) - - assert self.fp is not None - self._decoder = _avif.AvifDecoder( - self.fp.read(), - DECODE_CODEC_CHOICE, - _get_default_max_threads(), - ) - - # Get info from decoder - self._size, self.n_frames, self._mode, icc, exif, exif_orientation, xmp = ( - self._decoder.get_info() - ) - self.is_animated = self.n_frames > 1 - - if icc: - self.info["icc_profile"] = icc - if xmp: - self.info["xmp"] = xmp - - if exif_orientation != 1 or exif: - exif_data = Image.Exif() - if exif: - exif_data.load(exif) - original_orientation = exif_data.get(ExifTags.Base.Orientation, 1) - else: - original_orientation = 1 - if exif_orientation != original_orientation: - exif_data[ExifTags.Base.Orientation] = exif_orientation - exif = exif_data.tobytes() - if exif: - self.info["exif"] = exif - self.seek(0) - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - - # Set tile - self.__frame = frame - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)] - - def load(self) -> Image.core.PixelAccess | None: - if self.tile: - # We need to load the image data for this frame - data, timescale, pts_in_timescales, duration_in_timescales = ( - self._decoder.get_frame(self.__frame) - ) - self.info["timestamp"] = round(1000 * (pts_in_timescales / timescale)) - self.info["duration"] = round(1000 * (duration_in_timescales / timescale)) - - if self.fp and self._exclusive_fp: - self.fp.close() - self.fp = BytesIO(data) - - return super().load() - - def load_seek(self, pos: int) -> None: - pass - - def tell(self) -> int: - return self.__frame - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, save_all=True) - - -def _save( - im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False -) -> None: - info = im.encoderinfo.copy() - if save_all: - append_images = list(info.get("append_images", [])) - else: - append_images = [] - - total = 0 - for ims in [im] + append_images: - total += getattr(ims, "n_frames", 1) - - quality = info.get("quality", 75) - if not isinstance(quality, int) or quality < 0 or quality > 100: - msg = "Invalid quality setting" - raise ValueError(msg) - - duration = info.get("duration", 0) - subsampling = info.get("subsampling", "4:2:0") - speed = info.get("speed", 6) - max_threads = info.get("max_threads", _get_default_max_threads()) - codec = info.get("codec", "auto") - if codec != "auto" and not _avif.encoder_codec_available(codec): - msg = "Invalid saving codec" - raise ValueError(msg) - range_ = info.get("range", "full") - tile_rows_log2 = info.get("tile_rows", 0) - tile_cols_log2 = info.get("tile_cols", 0) - alpha_premultiplied = bool(info.get("alpha_premultiplied", False)) - autotiling = bool(info.get("autotiling", tile_rows_log2 == tile_cols_log2 == 0)) - - icc_profile = info.get("icc_profile", im.info.get("icc_profile")) - exif_orientation = 1 - if exif := info.get("exif"): - if isinstance(exif, Image.Exif): - exif_data = exif - else: - exif_data = Image.Exif() - exif_data.load(exif) - if ExifTags.Base.Orientation in exif_data: - exif_orientation = exif_data.pop(ExifTags.Base.Orientation) - exif = exif_data.tobytes() if exif_data else b"" - elif isinstance(exif, Image.Exif): - exif = exif_data.tobytes() - - xmp = info.get("xmp") - - if isinstance(xmp, str): - xmp = xmp.encode("utf-8") - - advanced = info.get("advanced") - if advanced is not None: - if isinstance(advanced, dict): - advanced = advanced.items() - try: - advanced = tuple(advanced) - except TypeError: - invalid = True - else: - invalid = any(not isinstance(v, tuple) or len(v) != 2 for v in advanced) - if invalid: - msg = ( - "advanced codec options must be a dict of key-value string " - "pairs or a series of key-value two-tuples" - ) - raise ValueError(msg) - - # Setup the AVIF encoder - enc = _avif.AvifEncoder( - im.size, - subsampling, - quality, - speed, - max_threads, - codec, - range_, - tile_rows_log2, - tile_cols_log2, - alpha_premultiplied, - autotiling, - icc_profile or b"", - exif or b"", - exif_orientation, - xmp or b"", - advanced, - ) - - # Add each frame - frame_idx = 0 - frame_duration = 0 - cur_idx = im.tell() - is_single_frame = total == 1 - try: - for ims in [im] + append_images: - # Get number of frames in this image - nfr = getattr(ims, "n_frames", 1) - - for idx in range(nfr): - ims.seek(idx) - - # Make sure image mode is supported - frame = ims - rawmode = ims.mode - if ims.mode not in {"RGB", "RGBA"}: - rawmode = "RGBA" if ims.has_transparency_data else "RGB" - frame = ims.convert(rawmode) - - # Update frame duration - if isinstance(duration, (list, tuple)): - frame_duration = duration[frame_idx] - else: - frame_duration = duration - - # Append the frame to the animation encoder - enc.add( - frame.tobytes("raw", rawmode), - frame_duration, - frame.size, - rawmode, - is_single_frame, - ) - - # Update frame index - frame_idx += 1 - - if not save_all: - break - - finally: - im.seek(cur_idx) - - # Get the final output from the encoder - data = enc.finish() - if data is None: - msg = "cannot write file as AVIF (encoder returned None)" - raise OSError(msg) - - fp.write(data) - - -Image.register_open(AvifImageFile.format, AvifImageFile, _accept) -if SUPPORTED: - Image.register_save(AvifImageFile.format, _save) - Image.register_save_all(AvifImageFile.format, _save_all) - Image.register_extensions(AvifImageFile.format, [".avif", ".avifs"]) - Image.register_mime(AvifImageFile.format, "image/avif") diff --git a/.venv/lib/python3.12/site-packages/PIL/BdfFontFile.py b/.venv/lib/python3.12/site-packages/PIL/BdfFontFile.py deleted file mode 100644 index 1c8c28ff..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/BdfFontFile.py +++ /dev/null @@ -1,123 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# bitmap distribution font (bdf) file parser -# -# history: -# 1996-05-16 fl created (as bdf2pil) -# 1997-08-25 fl converted to FontFile driver -# 2001-05-25 fl removed bogus __init__ call -# 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev) -# 2003-04-22 fl more robustification (from Graham Dumpleton) -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1997-2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -""" -Parse X Bitmap Distribution Format (BDF) -""" - -from __future__ import annotations - -from typing import BinaryIO - -from . import FontFile, Image - - -def bdf_char( - f: BinaryIO, -) -> ( - tuple[ - str, - int, - tuple[tuple[int, int], tuple[int, int, int, int], tuple[int, int, int, int]], - Image.Image, - ] - | None -): - # skip to STARTCHAR - while True: - s = f.readline() - if not s: - return None - if s.startswith(b"STARTCHAR"): - break - id = s[9:].strip().decode("ascii") - - # load symbol properties - props = {} - while True: - s = f.readline() - if not s or s.startswith(b"BITMAP"): - break - i = s.find(b" ") - props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii") - - # load bitmap - bitmap = bytearray() - while True: - s = f.readline() - if not s or s.startswith(b"ENDCHAR"): - break - bitmap += s[:-1] - - # The word BBX - # followed by the width in x (BBw), height in y (BBh), - # and x and y displacement (BBxoff0, BByoff0) - # of the lower left corner from the origin of the character. - width, height, x_disp, y_disp = (int(p) for p in props["BBX"].split()) - - # The word DWIDTH - # followed by the width in x and y of the character in device pixels. - dwx, dwy = (int(p) for p in props["DWIDTH"].split()) - - bbox = ( - (dwx, dwy), - (x_disp, -y_disp - height, width + x_disp, -y_disp), - (0, 0, width, height), - ) - - try: - im = Image.frombytes("1", (width, height), bitmap, "hex", "1") - except ValueError: - # deal with zero-width characters - im = Image.new("1", (width, height)) - - return id, int(props["ENCODING"]), bbox, im - - -class BdfFontFile(FontFile.FontFile): - """Font file plugin for the X11 BDF format.""" - - def __init__(self, fp: BinaryIO) -> None: - super().__init__() - - s = fp.readline() - if not s.startswith(b"STARTFONT 2.1"): - msg = "not a valid BDF file" - raise SyntaxError(msg) - - props = {} - comments = [] - - while True: - s = fp.readline() - if not s or s.startswith(b"ENDPROPERTIES"): - break - i = s.find(b" ") - props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii") - if s[:i] in [b"COMMENT", b"COPYRIGHT"]: - if s.find(b"LogicalFontDescription") < 0: - comments.append(s[i + 1 : -1].decode("ascii")) - - while True: - c = bdf_char(fp) - if not c: - break - id, ch, (xy, dst, src), im = c - if 0 <= ch < len(self.glyph): - self.glyph[ch] = xy, dst, src, im diff --git a/.venv/lib/python3.12/site-packages/PIL/BlpImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/BlpImagePlugin.py deleted file mode 100644 index 6bb92edf..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/BlpImagePlugin.py +++ /dev/null @@ -1,498 +0,0 @@ -""" -Blizzard Mipmap Format (.blp) -Jerome Leclanche - -The contents of this file are hereby released in the public domain (CC0) -Full text of the CC0 license: - https://creativecommons.org/publicdomain/zero/1.0/ - -BLP1 files, used mostly in Warcraft III, are not fully supported. -All types of BLP2 files used in World of Warcraft are supported. - -The BLP file structure consists of a header, up to 16 mipmaps of the -texture - -Texture sizes must be powers of two, though the two dimensions do -not have to be equal; 512x256 is valid, but 512x200 is not. -The first mipmap (mipmap #0) is the full size image; each subsequent -mipmap halves both dimensions. The final mipmap should be 1x1. - -BLP files come in many different flavours: -* JPEG-compressed (type == 0) - only supported for BLP1. -* RAW images (type == 1, encoding == 1). Each mipmap is stored as an - array of 8-bit values, one per pixel, left to right, top to bottom. - Each value is an index to the palette. -* DXT-compressed (type == 1, encoding == 2): -- DXT1 compression is used if alpha_encoding == 0. - - An additional alpha bit is used if alpha_depth == 1. - - DXT3 compression is used if alpha_encoding == 1. - - DXT5 compression is used if alpha_encoding == 7. -""" - -from __future__ import annotations - -import abc -import os -import struct -from enum import IntEnum -from io import BytesIO -from typing import IO - -from . import Image, ImageFile - - -class Format(IntEnum): - JPEG = 0 - - -class Encoding(IntEnum): - UNCOMPRESSED = 1 - DXT = 2 - UNCOMPRESSED_RAW_BGRA = 3 - - -class AlphaEncoding(IntEnum): - DXT1 = 0 - DXT3 = 1 - DXT5 = 7 - - -def unpack_565(i: int) -> tuple[int, int, int]: - return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3 - - -def decode_dxt1( - data: bytes, alpha: bool = False -) -> tuple[bytearray, bytearray, bytearray, bytearray]: - """ - input: one "row" of data (i.e. will produce 4*width pixels) - """ - - blocks = len(data) // 8 # number of blocks in row - ret = (bytearray(), bytearray(), bytearray(), bytearray()) - - for block_index in range(blocks): - # Decode next 8-byte block. - idx = block_index * 8 - color0, color1, bits = struct.unpack_from("> 2 - - a = 0xFF - if control == 0: - r, g, b = r0, g0, b0 - elif control == 1: - r, g, b = r1, g1, b1 - elif control == 2: - if color0 > color1: - r = (2 * r0 + r1) // 3 - g = (2 * g0 + g1) // 3 - b = (2 * b0 + b1) // 3 - else: - r = (r0 + r1) // 2 - g = (g0 + g1) // 2 - b = (b0 + b1) // 2 - elif control == 3: - if color0 > color1: - r = (2 * r1 + r0) // 3 - g = (2 * g1 + g0) // 3 - b = (2 * b1 + b0) // 3 - else: - r, g, b, a = 0, 0, 0, 0 - - if alpha: - ret[j].extend([r, g, b, a]) - else: - ret[j].extend([r, g, b]) - - return ret - - -def decode_dxt3(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]: - """ - input: one "row" of data (i.e. will produce 4*width pixels) - """ - - blocks = len(data) // 16 # number of blocks in row - ret = (bytearray(), bytearray(), bytearray(), bytearray()) - - for block_index in range(blocks): - idx = block_index * 16 - block = data[idx : idx + 16] - # Decode next 16-byte block. - bits = struct.unpack_from("<8B", block) - color0, color1 = struct.unpack_from(">= 4 - else: - high = True - a &= 0xF - a *= 17 # We get a value between 0 and 15 - - color_code = (code >> 2 * (4 * j + i)) & 0x03 - - if color_code == 0: - r, g, b = r0, g0, b0 - elif color_code == 1: - r, g, b = r1, g1, b1 - elif color_code == 2: - r = (2 * r0 + r1) // 3 - g = (2 * g0 + g1) // 3 - b = (2 * b0 + b1) // 3 - elif color_code == 3: - r = (2 * r1 + r0) // 3 - g = (2 * g1 + g0) // 3 - b = (2 * b1 + b0) // 3 - - ret[j].extend([r, g, b, a]) - - return ret - - -def decode_dxt5(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]: - """ - input: one "row" of data (i.e. will produce 4 * width pixels) - """ - - blocks = len(data) // 16 # number of blocks in row - ret = (bytearray(), bytearray(), bytearray(), bytearray()) - - for block_index in range(blocks): - idx = block_index * 16 - block = data[idx : idx + 16] - # Decode next 16-byte block. - a0, a1 = struct.unpack_from("> alphacode_index) & 0x07 - elif alphacode_index == 15: - alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06) - else: # alphacode_index >= 18 and alphacode_index <= 45 - alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07 - - if alphacode == 0: - a = a0 - elif alphacode == 1: - a = a1 - elif a0 > a1: - a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7 - elif alphacode == 6: - a = 0 - elif alphacode == 7: - a = 255 - else: - a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5 - - color_code = (code >> 2 * (4 * j + i)) & 0x03 - - if color_code == 0: - r, g, b = r0, g0, b0 - elif color_code == 1: - r, g, b = r1, g1, b1 - elif color_code == 2: - r = (2 * r0 + r1) // 3 - g = (2 * g0 + g1) // 3 - b = (2 * b0 + b1) // 3 - elif color_code == 3: - r = (2 * r1 + r0) // 3 - g = (2 * g1 + g0) // 3 - b = (2 * b1 + b0) // 3 - - ret[j].extend([r, g, b, a]) - - return ret - - -class BLPFormatError(NotImplementedError): - pass - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith((b"BLP1", b"BLP2")) - - -class BlpImageFile(ImageFile.ImageFile): - """ - Blizzard Mipmap Format - """ - - format = "BLP" - format_description = "Blizzard Mipmap Format" - - def _open(self) -> None: - assert self.fp is not None - self.magic = self.fp.read(4) - if not _accept(self.magic): - msg = f"Bad BLP magic {repr(self.magic)}" - raise BLPFormatError(msg) - - compression = struct.unpack(" tuple[int, int]: - try: - self._read_header() - self._load() - except struct.error as e: - msg = "Truncated BLP file" - raise OSError(msg) from e - return -1, 0 - - @abc.abstractmethod - def _load(self) -> None: - pass - - def _read_header(self) -> None: - self._offsets = struct.unpack("<16I", self._safe_read(16 * 4)) - self._lengths = struct.unpack("<16I", self._safe_read(16 * 4)) - - def _safe_read(self, length: int) -> bytes: - assert self.fd is not None - return ImageFile._safe_read(self.fd, length) - - def _read_palette(self) -> list[tuple[int, int, int, int]]: - ret = [] - for i in range(256): - try: - b, g, r, a = struct.unpack("<4B", self._safe_read(4)) - except struct.error: - break - ret.append((b, g, r, a)) - return ret - - def _read_bgra( - self, palette: list[tuple[int, int, int, int]], alpha: bool - ) -> bytearray: - data = bytearray() - _data = BytesIO(self._safe_read(self._lengths[0])) - while True: - try: - (offset,) = struct.unpack(" None: - self._compression, self._encoding, alpha = self.args - - if self._compression == Format.JPEG: - self._decode_jpeg_stream() - - elif self._compression == 1: - if self._encoding in (4, 5): - palette = self._read_palette() - data = self._read_bgra(palette, alpha) - self.set_as_raw(data) - else: - msg = f"Unsupported BLP encoding {repr(self._encoding)}" - raise BLPFormatError(msg) - else: - msg = f"Unsupported BLP compression {repr(self._encoding)}" - raise BLPFormatError(msg) - - def _decode_jpeg_stream(self) -> None: - from .JpegImagePlugin import JpegImageFile - - (jpeg_header_size,) = struct.unpack(" None: - self._compression, self._encoding, alpha, self._alpha_encoding = self.args - - palette = self._read_palette() - - assert self.fd is not None - self.fd.seek(self._offsets[0]) - - if self._compression == 1: - # Uncompressed or DirectX compression - - if self._encoding == Encoding.UNCOMPRESSED: - data = self._read_bgra(palette, alpha) - - elif self._encoding == Encoding.DXT: - data = bytearray() - if self._alpha_encoding == AlphaEncoding.DXT1: - linesize = (self.state.xsize + 3) // 4 * 8 - for yb in range((self.state.ysize + 3) // 4): - for d in decode_dxt1(self._safe_read(linesize), alpha): - data += d - - elif self._alpha_encoding == AlphaEncoding.DXT3: - linesize = (self.state.xsize + 3) // 4 * 16 - for yb in range((self.state.ysize + 3) // 4): - for d in decode_dxt3(self._safe_read(linesize)): - data += d - - elif self._alpha_encoding == AlphaEncoding.DXT5: - linesize = (self.state.xsize + 3) // 4 * 16 - for yb in range((self.state.ysize + 3) // 4): - for d in decode_dxt5(self._safe_read(linesize)): - data += d - else: - msg = f"Unsupported alpha encoding {repr(self._alpha_encoding)}" - raise BLPFormatError(msg) - else: - msg = f"Unknown BLP encoding {repr(self._encoding)}" - raise BLPFormatError(msg) - - else: - msg = f"Unknown BLP compression {repr(self._compression)}" - raise BLPFormatError(msg) - - self.set_as_raw(data) - - -class BLPEncoder(ImageFile.PyEncoder): - _pushes_fd = True - - def _write_palette(self) -> bytes: - data = b"" - assert self.im is not None - palette = self.im.getpalette("RGBA", "RGBA") - for i in range(len(palette) // 4): - r, g, b, a = palette[i * 4 : (i + 1) * 4] - data += struct.pack("<4B", b, g, r, a) - while len(data) < 256 * 4: - data += b"\x00" * 4 - return data - - def encode(self, bufsize: int) -> tuple[int, int, bytes]: - palette_data = self._write_palette() - - offset = 20 + 16 * 4 * 2 + len(palette_data) - data = struct.pack("<16I", offset, *((0,) * 15)) - - assert self.im is not None - w, h = self.im.size - data += struct.pack("<16I", w * h, *((0,) * 15)) - - data += palette_data - - for y in range(h): - for x in range(w): - data += struct.pack(" None: - if im.mode != "P": - msg = "Unsupported BLP image mode" - raise ValueError(msg) - - magic = b"BLP1" if im.encoderinfo.get("blp_version") == "BLP1" else b"BLP2" - fp.write(magic) - - assert im.palette is not None - fp.write(struct.pack(" mode, rawmode - 1: ("P", "P;1"), - 4: ("P", "P;4"), - 8: ("P", "P"), - 16: ("RGB", "BGR;15"), - 24: ("RGB", "BGR"), - 32: ("RGB", "BGRX"), -} - -USE_RAW_ALPHA = False - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"BM") - - -def _dib_accept(prefix: bytes) -> bool: - return i32(prefix) in [12, 40, 52, 56, 64, 108, 124] - - -# ============================================================================= -# Image plugin for the Windows BMP format. -# ============================================================================= -class BmpImageFile(ImageFile.ImageFile): - """Image plugin for the Windows Bitmap format (BMP)""" - - # ------------------------------------------------------------- Description - format_description = "Windows Bitmap" - format = "BMP" - - # -------------------------------------------------- BMP Compression values - COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5} - for k, v in COMPRESSIONS.items(): - vars()[k] = v - - def _bitmap(self, header: int = 0, offset: int = 0) -> None: - """Read relevant info about the BMP""" - assert self.fp is not None - read, seek = self.fp.read, self.fp.seek - if header: - seek(header) - # read bmp header size @offset 14 (this is part of the header size) - file_info: dict[str, bool | int | tuple[int, ...]] = { - "header_size": i32(read(4)), - "direction": -1, - } - - # -------------------- If requested, read header at a specific position - # read the rest of the bmp header, without its size - assert isinstance(file_info["header_size"], int) - header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4) - - # ------------------------------- Windows Bitmap v2, IBM OS/2 Bitmap v1 - # ----- This format has different offsets because of width/height types - # 12: BITMAPCOREHEADER/OS21XBITMAPHEADER - if file_info["header_size"] == 12: - file_info["width"] = i16(header_data, 0) - file_info["height"] = i16(header_data, 2) - file_info["planes"] = i16(header_data, 4) - file_info["bits"] = i16(header_data, 6) - file_info["compression"] = self.COMPRESSIONS["RAW"] - file_info["palette_padding"] = 3 - - # --------------------------------------------- Windows Bitmap v3 to v5 - # 40: BITMAPINFOHEADER - # 52: BITMAPV2HEADER - # 56: BITMAPV3HEADER - # 64: BITMAPCOREHEADER2/OS22XBITMAPHEADER - # 108: BITMAPV4HEADER - # 124: BITMAPV5HEADER - elif file_info["header_size"] in (40, 52, 56, 64, 108, 124): - file_info["y_flip"] = header_data[7] == 0xFF - file_info["direction"] = 1 if file_info["y_flip"] else -1 - file_info["width"] = i32(header_data, 0) - file_info["height"] = ( - i32(header_data, 4) - if not file_info["y_flip"] - else 2**32 - i32(header_data, 4) - ) - file_info["planes"] = i16(header_data, 8) - file_info["bits"] = i16(header_data, 10) - file_info["compression"] = i32(header_data, 12) - # byte size of pixel data - file_info["data_size"] = i32(header_data, 16) - file_info["pixels_per_meter"] = ( - i32(header_data, 20), - i32(header_data, 24), - ) - file_info["colors"] = i32(header_data, 28) - file_info["palette_padding"] = 4 - assert isinstance(file_info["pixels_per_meter"], tuple) - self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"]) - if file_info["compression"] == self.COMPRESSIONS["BITFIELDS"]: - masks = ["r_mask", "g_mask", "b_mask"] - if len(header_data) >= 48: - if len(header_data) >= 52: - masks.append("a_mask") - else: - file_info["a_mask"] = 0x0 - for idx, mask in enumerate(masks): - file_info[mask] = i32(header_data, 36 + idx * 4) - else: - # 40 byte headers only have the three components in the - # bitfields masks, ref: - # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx - # See also - # https://github.com/python-pillow/Pillow/issues/1293 - # There is a 4th component in the RGBQuad, in the alpha - # location, but it is listed as a reserved component, - # and it is not generally an alpha channel - file_info["a_mask"] = 0x0 - for mask in masks: - file_info[mask] = i32(read(4)) - assert isinstance(file_info["r_mask"], int) - assert isinstance(file_info["g_mask"], int) - assert isinstance(file_info["b_mask"], int) - assert isinstance(file_info["a_mask"], int) - file_info["rgb_mask"] = ( - file_info["r_mask"], - file_info["g_mask"], - file_info["b_mask"], - ) - file_info["rgba_mask"] = ( - file_info["r_mask"], - file_info["g_mask"], - file_info["b_mask"], - file_info["a_mask"], - ) - else: - msg = f"Unsupported BMP header type ({file_info['header_size']})" - raise OSError(msg) - - # ------------------ Special case : header is reported 40, which - # ---------------------- is shorter than real size for bpp >= 16 - assert isinstance(file_info["width"], int) - assert isinstance(file_info["height"], int) - self._size = file_info["width"], file_info["height"] - - # ------- If color count was not found in the header, compute from bits - assert isinstance(file_info["bits"], int) - if not file_info.get("colors", 0): - file_info["colors"] = 1 << file_info["bits"] - assert isinstance(file_info["palette_padding"], int) - assert isinstance(file_info["colors"], int) - if offset == 14 + file_info["header_size"] and file_info["bits"] <= 8: - offset += file_info["palette_padding"] * file_info["colors"] - - # ---------------------- Check bit depth for unusual unsupported values - self._mode, raw_mode = BIT2MODE.get(file_info["bits"], ("", "")) - if not self.mode: - msg = f"Unsupported BMP pixel depth ({file_info['bits']})" - raise OSError(msg) - - # ---------------- Process BMP with Bitfields compression (not palette) - decoder_name = "raw" - if file_info["compression"] == self.COMPRESSIONS["BITFIELDS"]: - SUPPORTED: dict[int, list[tuple[int, ...]]] = { - 32: [ - (0xFF0000, 0xFF00, 0xFF, 0x0), - (0xFF000000, 0xFF0000, 0xFF00, 0x0), - (0xFF000000, 0xFF00, 0xFF, 0x0), - (0xFF000000, 0xFF0000, 0xFF00, 0xFF), - (0xFF, 0xFF00, 0xFF0000, 0xFF000000), - (0xFF0000, 0xFF00, 0xFF, 0xFF000000), - (0xFF000000, 0xFF00, 0xFF, 0xFF0000), - (0x0, 0x0, 0x0, 0x0), - ], - 24: [(0xFF0000, 0xFF00, 0xFF)], - 16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)], - } - MASK_MODES = { - (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX", - (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR", - (32, (0xFF000000, 0xFF00, 0xFF, 0x0)): "BGXR", - (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): "ABGR", - (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA", - (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA", - (32, (0xFF000000, 0xFF00, 0xFF, 0xFF0000)): "BGAR", - (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", - (24, (0xFF0000, 0xFF00, 0xFF)): "BGR", - (16, (0xF800, 0x7E0, 0x1F)): "BGR;16", - (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15", - } - if file_info["bits"] in SUPPORTED: - if ( - file_info["bits"] == 32 - and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]] - ): - assert isinstance(file_info["rgba_mask"], tuple) - raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])] - self._mode = "RGBA" if "A" in raw_mode else self.mode - elif ( - file_info["bits"] in (24, 16) - and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]] - ): - assert isinstance(file_info["rgb_mask"], tuple) - raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])] - else: - msg = "Unsupported BMP bitfields layout" - raise OSError(msg) - else: - msg = "Unsupported BMP bitfields layout" - raise OSError(msg) - elif file_info["compression"] == self.COMPRESSIONS["RAW"]: - if file_info["bits"] == 32 and ( - header == 22 or USE_RAW_ALPHA # 32-bit .cur offset - ): - raw_mode, self._mode = "BGRA", "RGBA" - elif file_info["compression"] in ( - self.COMPRESSIONS["RLE8"], - self.COMPRESSIONS["RLE4"], - ): - decoder_name = "bmp_rle" - else: - msg = f"Unsupported BMP compression ({file_info['compression']})" - raise OSError(msg) - - # --------------- Once the header is processed, process the palette/LUT - if self.mode == "P": # Paletted for 1, 4 and 8 bit images - # ---------------------------------------------------- 1-bit images - if not (0 < file_info["colors"] <= 65536): - msg = f"Unsupported BMP Palette size ({file_info['colors']})" - raise OSError(msg) - else: - padding = file_info["palette_padding"] - palette = read(padding * file_info["colors"]) - grayscale = True - indices = ( - (0, 255) - if file_info["colors"] == 2 - else list(range(file_info["colors"])) - ) - - # ----------------- Check if grayscale and ignore palette if so - for ind, val in enumerate(indices): - rgb = palette[ind * padding : ind * padding + 3] - if rgb != o8(val) * 3: - grayscale = False - - # ------- If all colors are gray, white or black, ditch palette - if grayscale: - self._mode = "1" if file_info["colors"] == 2 else "L" - raw_mode = self.mode - else: - self._mode = "P" - self.palette = ImagePalette.raw( - "BGRX" if padding == 4 else "BGR", palette - ) - - # ---------------------------- Finally set the tile data for the plugin - self.info["compression"] = file_info["compression"] - args: list[Any] = [raw_mode] - if decoder_name == "bmp_rle": - args.append(file_info["compression"] == self.COMPRESSIONS["RLE4"]) - else: - assert isinstance(file_info["width"], int) - args.append(((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3)) - args.append(file_info["direction"]) - self.tile = [ - ImageFile._Tile( - decoder_name, - (0, 0, file_info["width"], file_info["height"]), - offset or self.fp.tell(), - tuple(args), - ) - ] - - def _open(self) -> None: - """Open file, check magic number and read header""" - # read 14 bytes: magic number, filesize, reserved, header final offset - assert self.fp is not None - head_data = self.fp.read(14) - # choke if the file does not have the required magic bytes - if not _accept(head_data): - msg = "Not a BMP file" - raise SyntaxError(msg) - # read the start position of the BMP image data (u32) - offset = i32(head_data, 10) - # load bitmap information (offset=raster info) - self._bitmap(offset=offset) - - -class BmpRleDecoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - rle4 = self.args[1] - data = bytearray() - x = 0 - dest_length = self.state.xsize * self.state.ysize - while len(data) < dest_length: - pixels = self.fd.read(1) - byte = self.fd.read(1) - if not pixels or not byte: - break - num_pixels = pixels[0] - if num_pixels: - # encoded mode - if x + num_pixels > self.state.xsize: - # Too much data for row - num_pixels = max(0, self.state.xsize - x) - if rle4: - first_pixel = o8(byte[0] >> 4) - second_pixel = o8(byte[0] & 0x0F) - for index in range(num_pixels): - if index % 2 == 0: - data += first_pixel - else: - data += second_pixel - else: - data += byte * num_pixels - x += num_pixels - else: - if byte[0] == 0: - # end of line - while len(data) % self.state.xsize != 0: - data += b"\x00" - x = 0 - elif byte[0] == 1: - # end of bitmap - break - elif byte[0] == 2: - # delta - bytes_read = self.fd.read(2) - if len(bytes_read) < 2: - break - right, up = bytes_read - data += b"\x00" * (right + up * self.state.xsize) - x = len(data) % self.state.xsize - else: - # absolute mode - if rle4: - # 2 pixels per byte - byte_count = byte[0] // 2 - bytes_read = self.fd.read(byte_count) - for byte_read in bytes_read: - data += o8(byte_read >> 4) - data += o8(byte_read & 0x0F) - else: - byte_count = byte[0] - bytes_read = self.fd.read(byte_count) - data += bytes_read - if len(bytes_read) < byte_count: - break - x += byte[0] - - # align to 16-bit word boundary - if self.fd.tell() % 2 != 0: - self.fd.seek(1, os.SEEK_CUR) - rawmode = "L" if self.mode == "L" else "P" - self.set_as_raw(bytes(data), rawmode, (0, self.args[-1])) - return -1, 0 - - -# ============================================================================= -# Image plugin for the DIB format (BMP alias) -# ============================================================================= -class DibImageFile(BmpImageFile): - format = "DIB" - format_description = "Windows Bitmap" - - def _open(self) -> None: - self._bitmap() - - -# -# -------------------------------------------------------------------- -# Write BMP file - - -SAVE = { - "1": ("1", 1, 2), - "L": ("L", 8, 256), - "P": ("P", 8, 256), - "RGB": ("BGR", 24, 0), - "RGBA": ("BGRA", 32, 0), -} - - -def _dib_save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, False) - - -def _save( - im: Image.Image, fp: IO[bytes], filename: str | bytes, bitmap_header: bool = True -) -> None: - try: - rawmode, bits, colors = SAVE[im.mode] - except KeyError as e: - msg = f"cannot write mode {im.mode} as BMP" - raise OSError(msg) from e - - info = im.encoderinfo - - dpi = info.get("dpi", (96, 96)) - - # 1 meter == 39.3701 inches - ppm = tuple(int(x * 39.3701 + 0.5) for x in dpi) - - stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3) - header = 40 # or 64 for OS/2 version 2 - image = stride * im.size[1] - - if im.mode == "1": - palette = b"".join(o8(i) * 3 + b"\x00" for i in (0, 255)) - elif im.mode == "L": - palette = b"".join(o8(i) * 3 + b"\x00" for i in range(256)) - elif im.mode == "P": - palette = im.im.getpalette("RGB", "BGRX") - colors = len(palette) // 4 - else: - palette = None - - # bitmap header - if bitmap_header: - offset = 14 + header + colors * 4 - file_size = offset + image - if file_size > 2**32 - 1: - msg = "File size is too large for the BMP format" - raise ValueError(msg) - fp.write( - b"BM" # file type (magic) - + o32(file_size) # file size - + o32(0) # reserved - + o32(offset) # image data offset - ) - - # bitmap info header - fp.write( - o32(header) # info header size - + o32(im.size[0]) # width - + o32(im.size[1]) # height - + o16(1) # planes - + o16(bits) # depth - + o32(0) # compression (0=uncompressed) - + o32(image) # size of bitmap - + o32(ppm[0]) # resolution - + o32(ppm[1]) # resolution - + o32(colors) # colors used - + o32(colors) # colors important - ) - - fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) - - if palette: - fp.write(palette) - - ImageFile._save( - im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))] - ) - - -# -# -------------------------------------------------------------------- -# Registry - - -Image.register_open(BmpImageFile.format, BmpImageFile, _accept) -Image.register_save(BmpImageFile.format, _save) - -Image.register_extension(BmpImageFile.format, ".bmp") - -Image.register_mime(BmpImageFile.format, "image/bmp") - -Image.register_decoder("bmp_rle", BmpRleDecoder) - -Image.register_open(DibImageFile.format, DibImageFile, _dib_accept) -Image.register_save(DibImageFile.format, _dib_save) - -Image.register_extension(DibImageFile.format, ".dib") - -Image.register_mime(DibImageFile.format, "image/bmp") diff --git a/.venv/lib/python3.12/site-packages/PIL/BufrStubImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/BufrStubImagePlugin.py deleted file mode 100644 index d82c4c74..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/BufrStubImagePlugin.py +++ /dev/null @@ -1,72 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# BUFR stub adapter -# -# Copyright (c) 1996-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import IO - -from . import Image, ImageFile - -_handler = None - - -def register_handler(handler: ImageFile.StubHandler | None) -> None: - """ - Install application-specific BUFR image handler. - - :param handler: Handler object. - """ - global _handler - _handler = handler - - -# -------------------------------------------------------------------- -# Image adapter - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith((b"BUFR", b"ZCZC")) - - -class BufrStubImageFile(ImageFile.StubImageFile): - format = "BUFR" - format_description = "BUFR" - - def _open(self) -> None: - assert self.fp is not None - if not _accept(self.fp.read(4)): - msg = "Not a BUFR file" - raise SyntaxError(msg) - - self.fp.seek(-4, os.SEEK_CUR) - - # make something up - self._mode = "F" - self._size = 1, 1 - - def _load(self) -> ImageFile.StubHandler | None: - return _handler - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if _handler is None or not hasattr(_handler, "save"): - msg = "BUFR save handler not installed" - raise OSError(msg) - _handler.save(im, fp, filename) - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(BufrStubImageFile.format, BufrStubImageFile, _accept) -Image.register_save(BufrStubImageFile.format, _save) - -Image.register_extension(BufrStubImageFile.format, ".bufr") diff --git a/.venv/lib/python3.12/site-packages/PIL/ContainerIO.py b/.venv/lib/python3.12/site-packages/PIL/ContainerIO.py deleted file mode 100644 index ec9e66c7..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ContainerIO.py +++ /dev/null @@ -1,173 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a class to read from a container file -# -# History: -# 1995-06-18 fl Created -# 1995-09-07 fl Added readline(), readlines() -# -# Copyright (c) 1997-2001 by Secret Labs AB -# Copyright (c) 1995 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -from collections.abc import Iterable -from typing import IO, AnyStr, NoReturn - - -class ContainerIO(IO[AnyStr]): - """ - A file object that provides read access to a part of an existing - file (for example a TAR file). - """ - - def __init__(self, file: IO[AnyStr], offset: int, length: int) -> None: - """ - Create file object. - - :param file: Existing file. - :param offset: Start of region, in bytes. - :param length: Size of region, in bytes. - """ - self.fh: IO[AnyStr] = file - self.pos = 0 - self.offset = offset - self.length = length - self.fh.seek(offset) - - ## - # Always false. - - def isatty(self) -> bool: - return False - - def seekable(self) -> bool: - return True - - def seek(self, offset: int, mode: int = io.SEEK_SET) -> int: - """ - Move file pointer. - - :param offset: Offset in bytes. - :param mode: Starting position. Use 0 for beginning of region, 1 - for current offset, and 2 for end of region. You cannot move - the pointer outside the defined region. - :returns: Offset from start of region, in bytes. - """ - if mode == 1: - self.pos = self.pos + offset - elif mode == 2: - self.pos = self.length + offset - else: - self.pos = offset - # clamp - self.pos = max(0, min(self.pos, self.length)) - self.fh.seek(self.offset + self.pos) - return self.pos - - def tell(self) -> int: - """ - Get current file pointer. - - :returns: Offset from start of region, in bytes. - """ - return self.pos - - def readable(self) -> bool: - return True - - def read(self, n: int = -1) -> AnyStr: - """ - Read data. - - :param n: Number of bytes to read. If omitted, zero or negative, - read until end of region. - :returns: An 8-bit string. - """ - if n > 0: - n = min(n, self.length - self.pos) - else: - n = self.length - self.pos - if n <= 0: # EOF - return b"" if "b" in self.fh.mode else "" # type: ignore[return-value] - self.pos = self.pos + n - return self.fh.read(n) - - def readline(self, n: int = -1) -> AnyStr: - """ - Read a line of text. - - :param n: Number of bytes to read. If omitted, zero or negative, - read until end of line. - :returns: An 8-bit string. - """ - s: AnyStr = b"" if "b" in self.fh.mode else "" # type: ignore[assignment] - newline_character = b"\n" if "b" in self.fh.mode else "\n" - while True: - c = self.read(1) - if not c: - break - s = s + c - if c == newline_character or len(s) == n: - break - return s - - def readlines(self, n: int | None = -1) -> list[AnyStr]: - """ - Read multiple lines of text. - - :param n: Number of lines to read. If omitted, zero, negative or None, - read until end of region. - :returns: A list of 8-bit strings. - """ - lines = [] - while True: - s = self.readline() - if not s: - break - lines.append(s) - if len(lines) == n: - break - return lines - - def writable(self) -> bool: - return False - - def write(self, b: AnyStr) -> NoReturn: - raise NotImplementedError() - - def writelines(self, lines: Iterable[AnyStr]) -> NoReturn: - raise NotImplementedError() - - def truncate(self, size: int | None = None) -> int: - raise NotImplementedError() - - def __enter__(self) -> ContainerIO[AnyStr]: - return self - - def __exit__(self, *args: object) -> None: - self.close() - - def __iter__(self) -> ContainerIO[AnyStr]: - return self - - def __next__(self) -> AnyStr: - line = self.readline() - if not line: - msg = "end of region" - raise StopIteration(msg) - return line - - def fileno(self) -> int: - return self.fh.fileno() - - def flush(self) -> None: - self.fh.flush() - - def close(self) -> None: - self.fh.close() diff --git a/.venv/lib/python3.12/site-packages/PIL/CurImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/CurImagePlugin.py deleted file mode 100644 index 9c188e08..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/CurImagePlugin.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Windows Cursor support for PIL -# -# notes: -# uses BmpImagePlugin.py to read the bitmap data. -# -# history: -# 96-05-27 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import BmpImagePlugin, Image -from ._binary import i16le as i16 -from ._binary import i32le as i32 - -# -# -------------------------------------------------------------------- - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"\0\0\2\0") - - -## -# Image plugin for Windows Cursor files. - - -class CurImageFile(BmpImagePlugin.BmpImageFile): - format = "CUR" - format_description = "Windows Cursor" - - def _open(self) -> None: - assert self.fp is not None - offset = self.fp.tell() - - # check magic - s = self.fp.read(6) - if not _accept(s): - msg = "not a CUR file" - raise SyntaxError(msg) - - # pick the largest cursor in the file - m = b"" - for i in range(i16(s, 4)): - s = self.fp.read(16) - if not m: - m = s - elif s[0] > m[0] and s[1] > m[1]: - m = s - if not m: - msg = "No cursors were found" - raise TypeError(msg) - - # load as bitmap - self._bitmap(i32(m, 12) + offset) - - # patch up the bitmap height - self._size = self.size[0], self.size[1] // 2 - self.tile = [self.tile[0]._replace(extents=(0, 0) + self.size)] - - -# -# -------------------------------------------------------------------- - -Image.register_open(CurImageFile.format, CurImageFile, _accept) - -Image.register_extension(CurImageFile.format, ".cur") diff --git a/.venv/lib/python3.12/site-packages/PIL/DcxImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/DcxImagePlugin.py deleted file mode 100644 index d3f456dd..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/DcxImagePlugin.py +++ /dev/null @@ -1,84 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# DCX file handling -# -# DCX is a container file format defined by Intel, commonly used -# for fax applications. Each DCX file consists of a directory -# (a list of file offsets) followed by a set of (usually 1-bit) -# PCX files. -# -# History: -# 1995-09-09 fl Created -# 1996-03-20 fl Properly derived from PcxImageFile. -# 1998-07-15 fl Renamed offset attribute to avoid name clash -# 2002-07-30 fl Fixed file handling -# -# Copyright (c) 1997-98 by Secret Labs AB. -# Copyright (c) 1995-96 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image -from ._binary import i32le as i32 -from ._util import DeferredError -from .PcxImagePlugin import PcxImageFile - -MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 4 and i32(prefix) == MAGIC - - -## -# Image plugin for the Intel DCX format. - - -class DcxImageFile(PcxImageFile): - format = "DCX" - format_description = "Intel DCX" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - # Header - assert self.fp is not None - s = self.fp.read(4) - if not _accept(s): - msg = "not a DCX file" - raise SyntaxError(msg) - - # Component directory - self._offset = [] - for i in range(1024): - offset = i32(self.fp.read(4)) - if not offset: - break - self._offset.append(offset) - - self._fp = self.fp - self.frame = -1 - self.n_frames = len(self._offset) - self.is_animated = self.n_frames > 1 - self.seek(0) - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self.frame = frame - self.fp = self._fp - self.fp.seek(self._offset[frame]) - PcxImageFile._open(self) - - def tell(self) -> int: - return self.frame - - -Image.register_open(DcxImageFile.format, DcxImageFile, _accept) - -Image.register_extension(DcxImageFile.format, ".dcx") diff --git a/.venv/lib/python3.12/site-packages/PIL/DdsImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/DdsImagePlugin.py deleted file mode 100644 index 312f602a..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/DdsImagePlugin.py +++ /dev/null @@ -1,625 +0,0 @@ -""" -A Pillow plugin for .dds files (S3TC-compressed aka DXTC) -Jerome Leclanche - -Documentation: -https://web.archive.org/web/20170802060935/http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt - -The contents of this file are hereby released in the public domain (CC0) -Full text of the CC0 license: -https://creativecommons.org/publicdomain/zero/1.0/ -""" - -from __future__ import annotations - -import struct -import sys -from enum import IntEnum, IntFlag -from typing import IO - -from . import Image, ImageFile, ImagePalette -from ._binary import i32le as i32 -from ._binary import o8 -from ._binary import o32le as o32 - -# Magic ("DDS ") -DDS_MAGIC = 0x20534444 - - -# DDS flags -class DDSD(IntFlag): - CAPS = 0x1 - HEIGHT = 0x2 - WIDTH = 0x4 - PITCH = 0x8 - PIXELFORMAT = 0x1000 - MIPMAPCOUNT = 0x20000 - LINEARSIZE = 0x80000 - DEPTH = 0x800000 - - -# DDS caps -class DDSCAPS(IntFlag): - COMPLEX = 0x8 - TEXTURE = 0x1000 - MIPMAP = 0x400000 - - -class DDSCAPS2(IntFlag): - CUBEMAP = 0x200 - CUBEMAP_POSITIVEX = 0x400 - CUBEMAP_NEGATIVEX = 0x800 - CUBEMAP_POSITIVEY = 0x1000 - CUBEMAP_NEGATIVEY = 0x2000 - CUBEMAP_POSITIVEZ = 0x4000 - CUBEMAP_NEGATIVEZ = 0x8000 - VOLUME = 0x200000 - - -# Pixel Format -class DDPF(IntFlag): - ALPHAPIXELS = 0x1 - ALPHA = 0x2 - FOURCC = 0x4 - PALETTEINDEXED8 = 0x20 - RGB = 0x40 - LUMINANCE = 0x20000 - - -# dxgiformat.h -class DXGI_FORMAT(IntEnum): - UNKNOWN = 0 - R32G32B32A32_TYPELESS = 1 - R32G32B32A32_FLOAT = 2 - R32G32B32A32_UINT = 3 - R32G32B32A32_SINT = 4 - R32G32B32_TYPELESS = 5 - R32G32B32_FLOAT = 6 - R32G32B32_UINT = 7 - R32G32B32_SINT = 8 - R16G16B16A16_TYPELESS = 9 - R16G16B16A16_FLOAT = 10 - R16G16B16A16_UNORM = 11 - R16G16B16A16_UINT = 12 - R16G16B16A16_SNORM = 13 - R16G16B16A16_SINT = 14 - R32G32_TYPELESS = 15 - R32G32_FLOAT = 16 - R32G32_UINT = 17 - R32G32_SINT = 18 - R32G8X24_TYPELESS = 19 - D32_FLOAT_S8X24_UINT = 20 - R32_FLOAT_X8X24_TYPELESS = 21 - X32_TYPELESS_G8X24_UINT = 22 - R10G10B10A2_TYPELESS = 23 - R10G10B10A2_UNORM = 24 - R10G10B10A2_UINT = 25 - R11G11B10_FLOAT = 26 - R8G8B8A8_TYPELESS = 27 - R8G8B8A8_UNORM = 28 - R8G8B8A8_UNORM_SRGB = 29 - R8G8B8A8_UINT = 30 - R8G8B8A8_SNORM = 31 - R8G8B8A8_SINT = 32 - R16G16_TYPELESS = 33 - R16G16_FLOAT = 34 - R16G16_UNORM = 35 - R16G16_UINT = 36 - R16G16_SNORM = 37 - R16G16_SINT = 38 - R32_TYPELESS = 39 - D32_FLOAT = 40 - R32_FLOAT = 41 - R32_UINT = 42 - R32_SINT = 43 - R24G8_TYPELESS = 44 - D24_UNORM_S8_UINT = 45 - R24_UNORM_X8_TYPELESS = 46 - X24_TYPELESS_G8_UINT = 47 - R8G8_TYPELESS = 48 - R8G8_UNORM = 49 - R8G8_UINT = 50 - R8G8_SNORM = 51 - R8G8_SINT = 52 - R16_TYPELESS = 53 - R16_FLOAT = 54 - D16_UNORM = 55 - R16_UNORM = 56 - R16_UINT = 57 - R16_SNORM = 58 - R16_SINT = 59 - R8_TYPELESS = 60 - R8_UNORM = 61 - R8_UINT = 62 - R8_SNORM = 63 - R8_SINT = 64 - A8_UNORM = 65 - R1_UNORM = 66 - R9G9B9E5_SHAREDEXP = 67 - R8G8_B8G8_UNORM = 68 - G8R8_G8B8_UNORM = 69 - BC1_TYPELESS = 70 - BC1_UNORM = 71 - BC1_UNORM_SRGB = 72 - BC2_TYPELESS = 73 - BC2_UNORM = 74 - BC2_UNORM_SRGB = 75 - BC3_TYPELESS = 76 - BC3_UNORM = 77 - BC3_UNORM_SRGB = 78 - BC4_TYPELESS = 79 - BC4_UNORM = 80 - BC4_SNORM = 81 - BC5_TYPELESS = 82 - BC5_UNORM = 83 - BC5_SNORM = 84 - B5G6R5_UNORM = 85 - B5G5R5A1_UNORM = 86 - B8G8R8A8_UNORM = 87 - B8G8R8X8_UNORM = 88 - R10G10B10_XR_BIAS_A2_UNORM = 89 - B8G8R8A8_TYPELESS = 90 - B8G8R8A8_UNORM_SRGB = 91 - B8G8R8X8_TYPELESS = 92 - B8G8R8X8_UNORM_SRGB = 93 - BC6H_TYPELESS = 94 - BC6H_UF16 = 95 - BC6H_SF16 = 96 - BC7_TYPELESS = 97 - BC7_UNORM = 98 - BC7_UNORM_SRGB = 99 - AYUV = 100 - Y410 = 101 - Y416 = 102 - NV12 = 103 - P010 = 104 - P016 = 105 - OPAQUE_420 = 106 - YUY2 = 107 - Y210 = 108 - Y216 = 109 - NV11 = 110 - AI44 = 111 - IA44 = 112 - P8 = 113 - A8P8 = 114 - B4G4R4A4_UNORM = 115 - P208 = 130 - V208 = 131 - V408 = 132 - SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 189 - SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 190 - - -class D3DFMT(IntEnum): - UNKNOWN = 0 - R8G8B8 = 20 - A8R8G8B8 = 21 - X8R8G8B8 = 22 - R5G6B5 = 23 - X1R5G5B5 = 24 - A1R5G5B5 = 25 - A4R4G4B4 = 26 - R3G3B2 = 27 - A8 = 28 - A8R3G3B2 = 29 - X4R4G4B4 = 30 - A2B10G10R10 = 31 - A8B8G8R8 = 32 - X8B8G8R8 = 33 - G16R16 = 34 - A2R10G10B10 = 35 - A16B16G16R16 = 36 - A8P8 = 40 - P8 = 41 - L8 = 50 - A8L8 = 51 - A4L4 = 52 - V8U8 = 60 - L6V5U5 = 61 - X8L8V8U8 = 62 - Q8W8V8U8 = 63 - V16U16 = 64 - A2W10V10U10 = 67 - D16_LOCKABLE = 70 - D32 = 71 - D15S1 = 73 - D24S8 = 75 - D24X8 = 77 - D24X4S4 = 79 - D16 = 80 - D32F_LOCKABLE = 82 - D24FS8 = 83 - D32_LOCKABLE = 84 - S8_LOCKABLE = 85 - L16 = 81 - VERTEXDATA = 100 - INDEX16 = 101 - INDEX32 = 102 - Q16W16V16U16 = 110 - R16F = 111 - G16R16F = 112 - A16B16G16R16F = 113 - R32F = 114 - G32R32F = 115 - A32B32G32R32F = 116 - CxV8U8 = 117 - A1 = 118 - A2B10G10R10_XR_BIAS = 119 - BINARYBUFFER = 199 - - UYVY = i32(b"UYVY") - R8G8_B8G8 = i32(b"RGBG") - YUY2 = i32(b"YUY2") - G8R8_G8B8 = i32(b"GRGB") - DXT1 = i32(b"DXT1") - DXT2 = i32(b"DXT2") - DXT3 = i32(b"DXT3") - DXT4 = i32(b"DXT4") - DXT5 = i32(b"DXT5") - DX10 = i32(b"DX10") - BC4S = i32(b"BC4S") - BC4U = i32(b"BC4U") - BC5S = i32(b"BC5S") - BC5U = i32(b"BC5U") - ATI1 = i32(b"ATI1") - ATI2 = i32(b"ATI2") - MULTI2_ARGB8 = i32(b"MET1") - - -# Backward compatibility layer -module = sys.modules[__name__] -for item in DDSD: - assert item.name is not None - setattr(module, f"DDSD_{item.name}", item.value) -for item1 in DDSCAPS: - assert item1.name is not None - setattr(module, f"DDSCAPS_{item1.name}", item1.value) -for item2 in DDSCAPS2: - assert item2.name is not None - setattr(module, f"DDSCAPS2_{item2.name}", item2.value) -for item3 in DDPF: - assert item3.name is not None - setattr(module, f"DDPF_{item3.name}", item3.value) - -DDS_FOURCC = DDPF.FOURCC -DDS_RGB = DDPF.RGB -DDS_RGBA = DDPF.RGB | DDPF.ALPHAPIXELS -DDS_LUMINANCE = DDPF.LUMINANCE -DDS_LUMINANCEA = DDPF.LUMINANCE | DDPF.ALPHAPIXELS -DDS_ALPHA = DDPF.ALPHA -DDS_PAL8 = DDPF.PALETTEINDEXED8 - -DDS_HEADER_FLAGS_TEXTURE = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT -DDS_HEADER_FLAGS_MIPMAP = DDSD.MIPMAPCOUNT -DDS_HEADER_FLAGS_VOLUME = DDSD.DEPTH -DDS_HEADER_FLAGS_PITCH = DDSD.PITCH -DDS_HEADER_FLAGS_LINEARSIZE = DDSD.LINEARSIZE - -DDS_HEIGHT = DDSD.HEIGHT -DDS_WIDTH = DDSD.WIDTH - -DDS_SURFACE_FLAGS_TEXTURE = DDSCAPS.TEXTURE -DDS_SURFACE_FLAGS_MIPMAP = DDSCAPS.COMPLEX | DDSCAPS.MIPMAP -DDS_SURFACE_FLAGS_CUBEMAP = DDSCAPS.COMPLEX - -DDS_CUBEMAP_POSITIVEX = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEX -DDS_CUBEMAP_NEGATIVEX = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEX -DDS_CUBEMAP_POSITIVEY = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEY -DDS_CUBEMAP_NEGATIVEY = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEY -DDS_CUBEMAP_POSITIVEZ = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEZ -DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEZ - -DXT1_FOURCC = D3DFMT.DXT1 -DXT3_FOURCC = D3DFMT.DXT3 -DXT5_FOURCC = D3DFMT.DXT5 - -DXGI_FORMAT_R8G8B8A8_TYPELESS = DXGI_FORMAT.R8G8B8A8_TYPELESS -DXGI_FORMAT_R8G8B8A8_UNORM = DXGI_FORMAT.R8G8B8A8_UNORM -DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = DXGI_FORMAT.R8G8B8A8_UNORM_SRGB -DXGI_FORMAT_BC5_TYPELESS = DXGI_FORMAT.BC5_TYPELESS -DXGI_FORMAT_BC5_UNORM = DXGI_FORMAT.BC5_UNORM -DXGI_FORMAT_BC5_SNORM = DXGI_FORMAT.BC5_SNORM -DXGI_FORMAT_BC6H_UF16 = DXGI_FORMAT.BC6H_UF16 -DXGI_FORMAT_BC6H_SF16 = DXGI_FORMAT.BC6H_SF16 -DXGI_FORMAT_BC7_TYPELESS = DXGI_FORMAT.BC7_TYPELESS -DXGI_FORMAT_BC7_UNORM = DXGI_FORMAT.BC7_UNORM -DXGI_FORMAT_BC7_UNORM_SRGB = DXGI_FORMAT.BC7_UNORM_SRGB - - -class DdsImageFile(ImageFile.ImageFile): - format = "DDS" - format_description = "DirectDraw Surface" - - def _open(self) -> None: - assert self.fp is not None - if not _accept(self.fp.read(4)): - msg = "not a DDS file" - raise SyntaxError(msg) - (header_size,) = struct.unpack(" None: - pass - - -class DdsRgbDecoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - bitcount, masks = self.args - - # Some masks will be padded with zeros, e.g. R 0b11 G 0b1100 - # Calculate how many zeros each mask is padded with - mask_offsets = [] - # And the maximum value of each channel without the padding - mask_totals = [] - for mask in masks: - offset = 0 - if mask != 0: - while mask >> (offset + 1) << (offset + 1) == mask: - offset += 1 - mask_offsets.append(offset) - mask_totals.append(mask >> offset) - - data = bytearray() - bytecount = bitcount // 8 - dest_length = self.state.xsize * self.state.ysize * len(masks) - while len(data) < dest_length: - value = int.from_bytes(self.fd.read(bytecount), "little") - for i, mask in enumerate(masks): - masked_value = value & mask - # Remove the zero padding, and scale it to 8 bits - data += o8( - int(((masked_value >> mask_offsets[i]) / mask_totals[i]) * 255) - if mask_totals[i] - else 0 - ) - self.set_as_raw(data) - return -1, 0 - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode not in ("RGB", "RGBA", "L", "LA"): - msg = f"cannot write mode {im.mode} as DDS" - raise OSError(msg) - - flags = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT - bitcount = len(im.getbands()) * 8 - pixel_format = im.encoderinfo.get("pixel_format") - args: tuple[int] | str - if pixel_format: - codec_name = "bcn" - flags |= DDSD.LINEARSIZE - pitch = (im.width + 3) * 4 - rgba_mask = [0, 0, 0, 0] - pixel_flags = DDPF.FOURCC - if pixel_format == "DXT1": - fourcc = D3DFMT.DXT1 - args = (1,) - elif pixel_format == "DXT3": - fourcc = D3DFMT.DXT3 - args = (2,) - elif pixel_format == "DXT5": - fourcc = D3DFMT.DXT5 - args = (3,) - else: - fourcc = D3DFMT.DX10 - if pixel_format == "BC2": - args = (2,) - dxgi_format = DXGI_FORMAT.BC2_TYPELESS - elif pixel_format == "BC3": - args = (3,) - dxgi_format = DXGI_FORMAT.BC3_TYPELESS - elif pixel_format == "BC5": - args = (5,) - dxgi_format = DXGI_FORMAT.BC5_TYPELESS - if im.mode != "RGB": - msg = "only RGB mode can be written as BC5" - raise OSError(msg) - else: - msg = f"cannot write pixel format {pixel_format}" - raise OSError(msg) - else: - codec_name = "raw" - flags |= DDSD.PITCH - pitch = (im.width * bitcount + 7) // 8 - - alpha = im.mode[-1] == "A" - if im.mode[0] == "L": - pixel_flags = DDPF.LUMINANCE - args = im.mode - if alpha: - rgba_mask = [0x000000FF, 0x000000FF, 0x000000FF] - else: - rgba_mask = [0xFF000000, 0xFF000000, 0xFF000000] - else: - pixel_flags = DDPF.RGB - args = im.mode[::-1] - rgba_mask = [0x00FF0000, 0x0000FF00, 0x000000FF] - - if alpha: - r, g, b, a = im.split() - im = Image.merge("RGBA", (a, r, g, b)) - if alpha: - pixel_flags |= DDPF.ALPHAPIXELS - rgba_mask.append(0xFF000000 if alpha else 0) - - fourcc = D3DFMT.UNKNOWN - fp.write( - o32(DDS_MAGIC) - + struct.pack( - "<7I", - 124, # header size - flags, # flags - im.height, - im.width, - pitch, - 0, # depth - 0, # mipmaps - ) - + struct.pack("11I", *((0,) * 11)) # reserved - # pfsize, pfflags, fourcc, bitcount - + struct.pack("<4I", 32, pixel_flags, fourcc, bitcount) - + struct.pack("<4I", *rgba_mask) # dwRGBABitMask - + struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0) - ) - if fourcc == D3DFMT.DX10: - fp.write( - # dxgi_format, 2D resource, misc, array size, straight alpha - struct.pack("<5I", dxgi_format, 3, 0, 0, 1) - ) - ImageFile._save(im, fp, [ImageFile._Tile(codec_name, (0, 0) + im.size, 0, args)]) - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"DDS ") - - -Image.register_open(DdsImageFile.format, DdsImageFile, _accept) -Image.register_decoder("dds_rgb", DdsRgbDecoder) -Image.register_save(DdsImageFile.format, _save) -Image.register_extension(DdsImageFile.format, ".dds") diff --git a/.venv/lib/python3.12/site-packages/PIL/EpsImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/EpsImagePlugin.py deleted file mode 100644 index aeb7b0c9..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/EpsImagePlugin.py +++ /dev/null @@ -1,481 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# EPS file handling -# -# History: -# 1995-09-01 fl Created (0.1) -# 1996-05-18 fl Don't choke on "atend" fields, Ghostscript interface (0.2) -# 1996-08-22 fl Don't choke on floating point BoundingBox values -# 1996-08-23 fl Handle files from Macintosh (0.3) -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) -# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5) -# 2014-05-07 e Handling of EPS with binary preview and fixed resolution -# resizing -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import re -import subprocess -import sys -import tempfile -from typing import IO - -from . import Image, ImageFile -from ._binary import i32le as i32 - -# -------------------------------------------------------------------- - - -split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") -field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") - -gs_binary: str | bool | None = None -gs_windows_binary = None - - -def has_ghostscript() -> bool: - global gs_binary, gs_windows_binary - if gs_binary is None: - if sys.platform.startswith("win"): - if gs_windows_binary is None: - import shutil - - for binary in ("gswin32c", "gswin64c", "gs"): - if shutil.which(binary) is not None: - gs_windows_binary = binary - break - else: - gs_windows_binary = False - gs_binary = gs_windows_binary - else: - try: - subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL) - gs_binary = "gs" - except OSError: - gs_binary = False - return gs_binary is not False - - -def Ghostscript( - tile: list[ImageFile._Tile], - size: tuple[int, int], - fp: IO[bytes], - scale: int = 1, - transparency: bool = False, -) -> Image.core.ImagingCore: - """Render an image using Ghostscript""" - global gs_binary - if not has_ghostscript(): - msg = "Unable to locate Ghostscript on paths" - raise OSError(msg) - assert isinstance(gs_binary, str) - - # Unpack decoder tile - args = tile[0].args - assert isinstance(args, tuple) - length, bbox = args - - # Hack to support hi-res rendering - scale = int(scale) or 1 - width = size[0] * scale - height = size[1] * scale - # resolution is dependent on bbox and size - res_x = 72.0 * width / (bbox[2] - bbox[0]) - res_y = 72.0 * height / (bbox[3] - bbox[1]) - - out_fd, outfile = tempfile.mkstemp() - os.close(out_fd) - - infile_temp = None - if hasattr(fp, "name") and os.path.exists(fp.name): - infile = fp.name - else: - in_fd, infile_temp = tempfile.mkstemp() - os.close(in_fd) - infile = infile_temp - - # Ignore length and offset! - # Ghostscript can read it - # Copy whole file to read in Ghostscript - with open(infile_temp, "wb") as f: - # fetch length of fp - fp.seek(0, io.SEEK_END) - fsize = fp.tell() - # ensure start position - # go back - fp.seek(0) - lengthfile = fsize - while lengthfile > 0: - s = fp.read(min(lengthfile, 100 * 1024)) - if not s: - break - lengthfile -= len(s) - f.write(s) - - if transparency: - # "RGBA" - device = "pngalpha" - else: - # "pnmraw" automatically chooses between - # PBM ("1"), PGM ("L"), and PPM ("RGB"). - device = "pnmraw" - - # Build Ghostscript command - command = [ - gs_binary, - "-q", # quiet mode - f"-g{width:d}x{height:d}", # set output geometry (pixels) - f"-r{res_x:f}x{res_y:f}", # set input DPI (dots per inch) - "-dBATCH", # exit after processing - "-dNOPAUSE", # don't pause between pages - "-dSAFER", # safe mode - f"-sDEVICE={device}", - f"-sOutputFile={outfile}", # output file - # adjust for image origin - "-c", - f"{-bbox[0]} {-bbox[1]} translate", - "-f", - infile, # input file - # showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272) - "-c", - "showpage", - ] - - # push data through Ghostscript - try: - startupinfo = None - if sys.platform.startswith("win"): - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - subprocess.check_call(command, startupinfo=startupinfo) - with Image.open(outfile) as out_im: - out_im.load() - return out_im.im.copy() - finally: - try: - os.unlink(outfile) - if infile_temp: - os.unlink(infile_temp) - except OSError: - pass - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"%!PS") or ( - len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5 - ) - - -## -# Image plugin for Encapsulated PostScript. This plugin supports only -# a few variants of this format. - - -class EpsImageFile(ImageFile.ImageFile): - """EPS File Parser for the Python Imaging Library""" - - format = "EPS" - format_description = "Encapsulated Postscript" - - mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"} - - def _open(self) -> None: - assert self.fp is not None - length, offset = self._find_offset(self.fp) - - # go to offset - start of "%!PS" - self.fp.seek(offset) - - self._mode = "RGB" - - # When reading header comments, the first comment is used. - # When reading trailer comments, the last comment is used. - bounding_box: list[int] | None = None - imagedata_size: tuple[int, int] | None = None - - byte_arr = bytearray(255) - bytes_mv = memoryview(byte_arr) - bytes_read = 0 - reading_header_comments = True - reading_trailer_comments = False - trailer_reached = False - - def check_required_header_comments() -> None: - """ - The EPS specification requires that some headers exist. - This should be checked when the header comments formally end, - when image data starts, or when the file ends, whichever comes first. - """ - if "PS-Adobe" not in self.info: - msg = 'EPS header missing "%!PS-Adobe" comment' - raise SyntaxError(msg) - if "BoundingBox" not in self.info: - msg = 'EPS header missing "%%BoundingBox" comment' - raise SyntaxError(msg) - - def read_comment(s: str) -> bool: - nonlocal bounding_box, reading_trailer_comments - try: - m = split.match(s) - except re.error as e: - msg = "not an EPS file" - raise SyntaxError(msg) from e - - if not m: - return False - - k, v = m.group(1, 2) - self.info[k] = v - if k == "BoundingBox": - if v == "(atend)": - reading_trailer_comments = True - elif not bounding_box or (trailer_reached and reading_trailer_comments): - try: - # Note: The DSC spec says that BoundingBox - # fields should be integers, but some drivers - # put floating point values there anyway. - bounding_box = [int(float(i)) for i in v.split()] - except Exception: - pass - return True - - while True: - byte = self.fp.read(1) - if byte == b"": - # if we didn't read a byte we must be at the end of the file - if bytes_read == 0: - if reading_header_comments: - check_required_header_comments() - break - elif byte in b"\r\n": - # if we read a line ending character, ignore it and parse what - # we have already read. if we haven't read any other characters, - # continue reading - if bytes_read == 0: - continue - else: - # ASCII/hexadecimal lines in an EPS file must not exceed - # 255 characters, not including line ending characters - if bytes_read >= 255: - # only enforce this for lines starting with a "%", - # otherwise assume it's binary data - if byte_arr[0] == ord("%"): - msg = "not an EPS file" - raise SyntaxError(msg) - else: - if reading_header_comments: - check_required_header_comments() - reading_header_comments = False - # reset bytes_read so we can keep reading - # data until the end of the line - bytes_read = 0 - byte_arr[bytes_read] = byte[0] - bytes_read += 1 - continue - - if reading_header_comments: - # Load EPS header - - # if this line doesn't start with a "%", - # or does start with "%%EndComments", - # then we've reached the end of the header/comments - if byte_arr[0] != ord("%") or bytes_mv[:13] == b"%%EndComments": - check_required_header_comments() - reading_header_comments = False - continue - - s = str(bytes_mv[:bytes_read], "latin-1") - if not read_comment(s): - m = field.match(s) - if m: - k = m.group(1) - if k.startswith("PS-Adobe"): - self.info["PS-Adobe"] = k[9:] - else: - self.info[k] = "" - elif s[0] == "%": - # handle non-DSC PostScript comments that some - # tools mistakenly put in the Comments section - pass - else: - msg = "bad EPS header" - raise OSError(msg) - elif bytes_mv[:11] == b"%ImageData:": - # Check for an "ImageData" descriptor - # https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577413_pgfId-1035096 - - # If we've already read an "ImageData" descriptor, - # don't read another one. - if imagedata_size: - bytes_read = 0 - continue - - # Values: - # columns - # rows - # bit depth (1 or 8) - # mode (1: L, 2: LAB, 3: RGB, 4: CMYK) - # number of padding channels - # block size (number of bytes per row per channel) - # binary/ascii (1: binary, 2: ascii) - # data start identifier (the image data follows after a single line - # consisting only of this quoted value) - image_data_values = byte_arr[11:bytes_read].split(None, 7) - columns, rows, bit_depth, mode_id = ( - int(value) for value in image_data_values[:4] - ) - - if bit_depth == 1: - self._mode = "1" - elif bit_depth == 8: - try: - self._mode = self.mode_map[mode_id] - except ValueError: - break - else: - break - - # Parse the columns and rows after checking the bit depth and mode - # in case the bit depth and/or mode are invalid. - imagedata_size = columns, rows - elif bytes_mv[:5] == b"%%EOF": - break - elif trailer_reached and reading_trailer_comments: - # Load EPS trailer - s = str(bytes_mv[:bytes_read], "latin-1") - read_comment(s) - elif bytes_mv[:9] == b"%%Trailer": - trailer_reached = True - elif bytes_mv[:14] == b"%%BeginBinary:": - bytecount = int(byte_arr[14:bytes_read]) - self.fp.seek(bytecount, os.SEEK_CUR) - bytes_read = 0 - - # A "BoundingBox" is always required, - # even if an "ImageData" descriptor size exists. - if not bounding_box: - msg = "cannot determine EPS bounding box" - raise OSError(msg) - - # An "ImageData" size takes precedence over the "BoundingBox". - self._size = imagedata_size or ( - bounding_box[2] - bounding_box[0], - bounding_box[3] - bounding_box[1], - ) - - self.tile = [ - ImageFile._Tile("eps", (0, 0) + self.size, offset, (length, bounding_box)) - ] - - def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]: - s = fp.read(4) - - if s == b"%!PS": - # for HEAD without binary preview - fp.seek(0, io.SEEK_END) - length = fp.tell() - offset = 0 - elif i32(s) == 0xC6D3D0C5: - # FIX for: Some EPS file not handled correctly / issue #302 - # EPS can contain binary data - # or start directly with latin coding - # more info see: - # https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf - s = fp.read(8) - offset = i32(s) - length = i32(s, 4) - else: - msg = "not an EPS file" - raise SyntaxError(msg) - - return length, offset - - def load( - self, scale: int = 1, transparency: bool = False - ) -> Image.core.PixelAccess | None: - # Load EPS via Ghostscript - if self.tile: - assert self.fp is not None - self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency) - self._mode = self.im.mode - self._size = self.im.size - self.tile = [] - return Image.Image.load(self) - - def load_seek(self, pos: int) -> None: - # we can't incrementally load, so force ImageFile.parser to - # use our custom load method by defining this method. - pass - - -# -------------------------------------------------------------------- - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes, eps: int = 1) -> None: - """EPS Writer for the Python Imaging Library.""" - - # make sure image data is available - im.load() - - # determine PostScript image mode - if im.mode == "L": - operator = (8, 1, b"image") - elif im.mode == "RGB": - operator = (8, 3, b"false 3 colorimage") - elif im.mode == "CMYK": - operator = (8, 4, b"false 4 colorimage") - else: - msg = "image mode is not supported" - raise ValueError(msg) - - if eps: - # write EPS header - fp.write(b"%!PS-Adobe-3.0 EPSF-3.0\n") - fp.write(b"%%Creator: PIL 0.1 EpsEncode\n") - # fp.write("%%CreationDate: %s"...) - fp.write(b"%%%%BoundingBox: 0 0 %d %d\n" % im.size) - fp.write(b"%%Pages: 1\n") - fp.write(b"%%EndComments\n") - fp.write(b"%%Page: 1 1\n") - fp.write(b"%%ImageData: %d %d " % im.size) - fp.write(b'%d %d 0 1 1 "%s"\n' % operator) - - # image header - fp.write(b"gsave\n") - fp.write(b"10 dict begin\n") - fp.write(b"/buf %d string def\n" % (im.size[0] * operator[1])) - fp.write(b"%d %d scale\n" % im.size) - fp.write(b"%d %d 8\n" % im.size) # <= bits - fp.write(b"[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) - fp.write(b"{ currentfile buf readhexstring pop } bind\n") - fp.write(operator[2] + b"\n") - if hasattr(fp, "flush"): - fp.flush() - - ImageFile._save(im, fp, [ImageFile._Tile("eps", (0, 0) + im.size)]) - - fp.write(b"\n%%%%EndBinary\n") - fp.write(b"grestore end\n") - if hasattr(fp, "flush"): - fp.flush() - - -# -------------------------------------------------------------------- - - -Image.register_open(EpsImageFile.format, EpsImageFile, _accept) - -Image.register_save(EpsImageFile.format, _save) - -Image.register_extensions(EpsImageFile.format, [".ps", ".eps"]) - -Image.register_mime(EpsImageFile.format, "application/postscript") diff --git a/.venv/lib/python3.12/site-packages/PIL/ExifTags.py b/.venv/lib/python3.12/site-packages/PIL/ExifTags.py deleted file mode 100644 index a9522e76..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ExifTags.py +++ /dev/null @@ -1,384 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# EXIF tags -# -# Copyright (c) 2003 by Secret Labs AB -# -# See the README file for information on usage and redistribution. -# - -""" -This module provides constants and clear-text names for various -well-known EXIF tags. -""" - -from __future__ import annotations - -from enum import IntEnum - - -class Base(IntEnum): - # possibly incomplete - InteropIndex = 0x0001 - ProcessingSoftware = 0x000B - NewSubfileType = 0x00FE - SubfileType = 0x00FF - ImageWidth = 0x0100 - ImageLength = 0x0101 - BitsPerSample = 0x0102 - Compression = 0x0103 - PhotometricInterpretation = 0x0106 - Thresholding = 0x0107 - CellWidth = 0x0108 - CellLength = 0x0109 - FillOrder = 0x010A - DocumentName = 0x010D - ImageDescription = 0x010E - Make = 0x010F - Model = 0x0110 - StripOffsets = 0x0111 - Orientation = 0x0112 - SamplesPerPixel = 0x0115 - RowsPerStrip = 0x0116 - StripByteCounts = 0x0117 - MinSampleValue = 0x0118 - MaxSampleValue = 0x0119 - XResolution = 0x011A - YResolution = 0x011B - PlanarConfiguration = 0x011C - PageName = 0x011D - FreeOffsets = 0x0120 - FreeByteCounts = 0x0121 - GrayResponseUnit = 0x0122 - GrayResponseCurve = 0x0123 - T4Options = 0x0124 - T6Options = 0x0125 - ResolutionUnit = 0x0128 - PageNumber = 0x0129 - TransferFunction = 0x012D - Software = 0x0131 - DateTime = 0x0132 - Artist = 0x013B - HostComputer = 0x013C - Predictor = 0x013D - WhitePoint = 0x013E - PrimaryChromaticities = 0x013F - ColorMap = 0x0140 - HalftoneHints = 0x0141 - TileWidth = 0x0142 - TileLength = 0x0143 - TileOffsets = 0x0144 - TileByteCounts = 0x0145 - SubIFDs = 0x014A - InkSet = 0x014C - InkNames = 0x014D - NumberOfInks = 0x014E - DotRange = 0x0150 - TargetPrinter = 0x0151 - ExtraSamples = 0x0152 - SampleFormat = 0x0153 - SMinSampleValue = 0x0154 - SMaxSampleValue = 0x0155 - TransferRange = 0x0156 - ClipPath = 0x0157 - XClipPathUnits = 0x0158 - YClipPathUnits = 0x0159 - Indexed = 0x015A - JPEGTables = 0x015B - OPIProxy = 0x015F - JPEGProc = 0x0200 - JpegIFOffset = 0x0201 - JpegIFByteCount = 0x0202 - JpegRestartInterval = 0x0203 - JpegLosslessPredictors = 0x0205 - JpegPointTransforms = 0x0206 - JpegQTables = 0x0207 - JpegDCTables = 0x0208 - JpegACTables = 0x0209 - YCbCrCoefficients = 0x0211 - YCbCrSubSampling = 0x0212 - YCbCrPositioning = 0x0213 - ReferenceBlackWhite = 0x0214 - XMLPacket = 0x02BC - RelatedImageFileFormat = 0x1000 - RelatedImageWidth = 0x1001 - RelatedImageLength = 0x1002 - Rating = 0x4746 - RatingPercent = 0x4749 - ImageID = 0x800D - CFARepeatPatternDim = 0x828D - BatteryLevel = 0x828F - Copyright = 0x8298 - ExposureTime = 0x829A - FNumber = 0x829D - IPTCNAA = 0x83BB - ImageResources = 0x8649 - ExifOffset = 0x8769 - InterColorProfile = 0x8773 - ExposureProgram = 0x8822 - SpectralSensitivity = 0x8824 - GPSInfo = 0x8825 - ISOSpeedRatings = 0x8827 - OECF = 0x8828 - Interlace = 0x8829 - TimeZoneOffset = 0x882A - SelfTimerMode = 0x882B - SensitivityType = 0x8830 - StandardOutputSensitivity = 0x8831 - RecommendedExposureIndex = 0x8832 - ISOSpeed = 0x8833 - ISOSpeedLatitudeyyy = 0x8834 - ISOSpeedLatitudezzz = 0x8835 - ExifVersion = 0x9000 - DateTimeOriginal = 0x9003 - DateTimeDigitized = 0x9004 - OffsetTime = 0x9010 - OffsetTimeOriginal = 0x9011 - OffsetTimeDigitized = 0x9012 - ComponentsConfiguration = 0x9101 - CompressedBitsPerPixel = 0x9102 - ShutterSpeedValue = 0x9201 - ApertureValue = 0x9202 - BrightnessValue = 0x9203 - ExposureBiasValue = 0x9204 - MaxApertureValue = 0x9205 - SubjectDistance = 0x9206 - MeteringMode = 0x9207 - LightSource = 0x9208 - Flash = 0x9209 - FocalLength = 0x920A - Noise = 0x920D - ImageNumber = 0x9211 - SecurityClassification = 0x9212 - ImageHistory = 0x9213 - TIFFEPStandardID = 0x9216 - MakerNote = 0x927C - UserComment = 0x9286 - SubsecTime = 0x9290 - SubsecTimeOriginal = 0x9291 - SubsecTimeDigitized = 0x9292 - AmbientTemperature = 0x9400 - Humidity = 0x9401 - Pressure = 0x9402 - WaterDepth = 0x9403 - Acceleration = 0x9404 - CameraElevationAngle = 0x9405 - XPTitle = 0x9C9B - XPComment = 0x9C9C - XPAuthor = 0x9C9D - XPKeywords = 0x9C9E - XPSubject = 0x9C9F - FlashPixVersion = 0xA000 - ColorSpace = 0xA001 - ExifImageWidth = 0xA002 - ExifImageHeight = 0xA003 - RelatedSoundFile = 0xA004 - ExifInteroperabilityOffset = 0xA005 - FlashEnergy = 0xA20B - SpatialFrequencyResponse = 0xA20C - FocalPlaneXResolution = 0xA20E - FocalPlaneYResolution = 0xA20F - FocalPlaneResolutionUnit = 0xA210 - SubjectLocation = 0xA214 - ExposureIndex = 0xA215 - SensingMethod = 0xA217 - FileSource = 0xA300 - SceneType = 0xA301 - CFAPattern = 0xA302 - CustomRendered = 0xA401 - ExposureMode = 0xA402 - WhiteBalance = 0xA403 - DigitalZoomRatio = 0xA404 - FocalLengthIn35mmFilm = 0xA405 - SceneCaptureType = 0xA406 - GainControl = 0xA407 - Contrast = 0xA408 - Saturation = 0xA409 - Sharpness = 0xA40A - DeviceSettingDescription = 0xA40B - SubjectDistanceRange = 0xA40C - ImageUniqueID = 0xA420 - CameraOwnerName = 0xA430 - BodySerialNumber = 0xA431 - LensSpecification = 0xA432 - LensMake = 0xA433 - LensModel = 0xA434 - LensSerialNumber = 0xA435 - CompositeImage = 0xA460 - CompositeImageCount = 0xA461 - CompositeImageExposureTimes = 0xA462 - Gamma = 0xA500 - PrintImageMatching = 0xC4A5 - DNGVersion = 0xC612 - DNGBackwardVersion = 0xC613 - UniqueCameraModel = 0xC614 - LocalizedCameraModel = 0xC615 - CFAPlaneColor = 0xC616 - CFALayout = 0xC617 - LinearizationTable = 0xC618 - BlackLevelRepeatDim = 0xC619 - BlackLevel = 0xC61A - BlackLevelDeltaH = 0xC61B - BlackLevelDeltaV = 0xC61C - WhiteLevel = 0xC61D - DefaultScale = 0xC61E - DefaultCropOrigin = 0xC61F - DefaultCropSize = 0xC620 - ColorMatrix1 = 0xC621 - ColorMatrix2 = 0xC622 - CameraCalibration1 = 0xC623 - CameraCalibration2 = 0xC624 - ReductionMatrix1 = 0xC625 - ReductionMatrix2 = 0xC626 - AnalogBalance = 0xC627 - AsShotNeutral = 0xC628 - AsShotWhiteXY = 0xC629 - BaselineExposure = 0xC62A - BaselineNoise = 0xC62B - BaselineSharpness = 0xC62C - BayerGreenSplit = 0xC62D - LinearResponseLimit = 0xC62E - CameraSerialNumber = 0xC62F - LensInfo = 0xC630 - ChromaBlurRadius = 0xC631 - AntiAliasStrength = 0xC632 - ShadowScale = 0xC633 - DNGPrivateData = 0xC634 - MakerNoteSafety = 0xC635 - CalibrationIlluminant1 = 0xC65A - CalibrationIlluminant2 = 0xC65B - BestQualityScale = 0xC65C - RawDataUniqueID = 0xC65D - OriginalRawFileName = 0xC68B - OriginalRawFileData = 0xC68C - ActiveArea = 0xC68D - MaskedAreas = 0xC68E - AsShotICCProfile = 0xC68F - AsShotPreProfileMatrix = 0xC690 - CurrentICCProfile = 0xC691 - CurrentPreProfileMatrix = 0xC692 - ColorimetricReference = 0xC6BF - CameraCalibrationSignature = 0xC6F3 - ProfileCalibrationSignature = 0xC6F4 - AsShotProfileName = 0xC6F6 - NoiseReductionApplied = 0xC6F7 - ProfileName = 0xC6F8 - ProfileHueSatMapDims = 0xC6F9 - ProfileHueSatMapData1 = 0xC6FA - ProfileHueSatMapData2 = 0xC6FB - ProfileToneCurve = 0xC6FC - ProfileEmbedPolicy = 0xC6FD - ProfileCopyright = 0xC6FE - ForwardMatrix1 = 0xC714 - ForwardMatrix2 = 0xC715 - PreviewApplicationName = 0xC716 - PreviewApplicationVersion = 0xC717 - PreviewSettingsName = 0xC718 - PreviewSettingsDigest = 0xC719 - PreviewColorSpace = 0xC71A - PreviewDateTime = 0xC71B - RawImageDigest = 0xC71C - OriginalRawFileDigest = 0xC71D - SubTileBlockSize = 0xC71E - RowInterleaveFactor = 0xC71F - ProfileLookTableDims = 0xC725 - ProfileLookTableData = 0xC726 - OpcodeList1 = 0xC740 - OpcodeList2 = 0xC741 - OpcodeList3 = 0xC74E - NoiseProfile = 0xC761 - FrameRate = 0xC764 - - -"""Maps EXIF tags to tag names.""" -TAGS = { - **{i.value: i.name for i in Base}, - 0x920C: "SpatialFrequencyResponse", - 0x9214: "SubjectLocation", - 0x9215: "ExposureIndex", - 0x828E: "CFAPattern", - 0x920B: "FlashEnergy", - 0x9216: "TIFF/EPStandardID", -} - - -class GPS(IntEnum): - GPSVersionID = 0x00 - GPSLatitudeRef = 0x01 - GPSLatitude = 0x02 - GPSLongitudeRef = 0x03 - GPSLongitude = 0x04 - GPSAltitudeRef = 0x05 - GPSAltitude = 0x06 - GPSTimeStamp = 0x07 - GPSSatellites = 0x08 - GPSStatus = 0x09 - GPSMeasureMode = 0x0A - GPSDOP = 0x0B - GPSSpeedRef = 0x0C - GPSSpeed = 0x0D - GPSTrackRef = 0x0E - GPSTrack = 0x0F - GPSImgDirectionRef = 0x10 - GPSImgDirection = 0x11 - GPSMapDatum = 0x12 - GPSDestLatitudeRef = 0x13 - GPSDestLatitude = 0x14 - GPSDestLongitudeRef = 0x15 - GPSDestLongitude = 0x16 - GPSDestBearingRef = 0x17 - GPSDestBearing = 0x18 - GPSDestDistanceRef = 0x19 - GPSDestDistance = 0x1A - GPSProcessingMethod = 0x1B - GPSAreaInformation = 0x1C - GPSDateStamp = 0x1D - GPSDifferential = 0x1E - GPSHPositioningError = 0x1F - - -"""Maps EXIF GPS tags to tag names.""" -GPSTAGS = {i.value: i.name for i in GPS} - - -class Interop(IntEnum): - InteropIndex = 0x0001 - InteropVersion = 0x0002 - RelatedImageFileFormat = 0x1000 - RelatedImageWidth = 0x1001 - RelatedImageHeight = 0x1002 - - -class IFD(IntEnum): - Exif = 0x8769 - GPSInfo = 0x8825 - MakerNote = 0x927C - Makernote = 0x927C # Deprecated - Interop = 0xA005 - IFD1 = -1 - - -class LightSource(IntEnum): - Unknown = 0x00 - Daylight = 0x01 - Fluorescent = 0x02 - Tungsten = 0x03 - Flash = 0x04 - Fine = 0x09 - Cloudy = 0x0A - Shade = 0x0B - DaylightFluorescent = 0x0C - DayWhiteFluorescent = 0x0D - CoolWhiteFluorescent = 0x0E - WhiteFluorescent = 0x0F - StandardLightA = 0x11 - StandardLightB = 0x12 - StandardLightC = 0x13 - D55 = 0x14 - D65 = 0x15 - D75 = 0x16 - D50 = 0x17 - ISO = 0x18 - Other = 0xFF diff --git a/.venv/lib/python3.12/site-packages/PIL/FitsImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/FitsImagePlugin.py deleted file mode 100644 index e9184077..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/FitsImagePlugin.py +++ /dev/null @@ -1,153 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# FITS file handling -# -# Copyright (c) 1998-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import gzip -import math - -from . import Image, ImageFile - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"SIMPLE") - - -class FitsImageFile(ImageFile.ImageFile): - format = "FITS" - format_description = "FITS" - - def _open(self) -> None: - assert self.fp is not None - - headers: dict[bytes, bytes] = {} - header_in_progress = False - decoder_name = "" - while True: - header = self.fp.read(80) - if not header: - msg = "Truncated FITS file" - raise OSError(msg) - keyword = header[:8].strip() - if keyword in (b"SIMPLE", b"XTENSION"): - header_in_progress = True - elif headers and not header_in_progress: - # This is now a data unit - break - elif keyword == b"END": - # Seek to the end of the header unit - self.fp.seek(math.ceil(self.fp.tell() / 2880) * 2880) - if not decoder_name: - decoder_name, offset, args = self._parse_headers(headers) - - header_in_progress = False - continue - - if decoder_name: - # Keep going to read past the headers - continue - - value = header[8:].split(b"/")[0].strip() - if value.startswith(b"="): - value = value[1:].strip() - if not headers and (not _accept(keyword) or value != b"T"): - msg = "Not a FITS file" - raise SyntaxError(msg) - headers[keyword] = value - - if not decoder_name: - msg = "No image data" - raise ValueError(msg) - - offset += self.fp.tell() - 80 - self.tile = [ImageFile._Tile(decoder_name, (0, 0) + self.size, offset, args)] - - def _get_size( - self, headers: dict[bytes, bytes], prefix: bytes - ) -> tuple[int, int] | None: - naxis = int(headers[prefix + b"NAXIS"]) - if naxis == 0: - return None - - if naxis == 1: - return 1, int(headers[prefix + b"NAXIS1"]) - else: - return int(headers[prefix + b"NAXIS1"]), int(headers[prefix + b"NAXIS2"]) - - def _parse_headers( - self, headers: dict[bytes, bytes] - ) -> tuple[str, int, tuple[str | int, ...]]: - prefix = b"" - decoder_name = "raw" - offset = 0 - if ( - headers.get(b"XTENSION") == b"'BINTABLE'" - and headers.get(b"ZIMAGE") == b"T" - and headers[b"ZCMPTYPE"] == b"'GZIP_1 '" - ): - no_prefix_size = self._get_size(headers, prefix) or (0, 0) - number_of_bits = int(headers[b"BITPIX"]) - offset = no_prefix_size[0] * no_prefix_size[1] * (number_of_bits // 8) - - prefix = b"Z" - decoder_name = "fits_gzip" - - size = self._get_size(headers, prefix) - if not size: - return "", 0, () - - self._size = size - - number_of_bits = int(headers[prefix + b"BITPIX"]) - if number_of_bits == 8: - self._mode = "L" - elif number_of_bits == 16: - self._mode = "I;16" - elif number_of_bits == 32: - self._mode = "I" - elif number_of_bits in (-32, -64): - self._mode = "F" - - args: tuple[str | int, ...] - if decoder_name == "raw": - args = (self.mode, 0, -1) - else: - args = (number_of_bits,) - return decoder_name, offset, args - - -class FitsGzipDecoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - with gzip.open(self.fd) as fp: - value = fp.read(self.state.xsize * self.state.ysize * 4) - - rows = [] - offset = 0 - number_of_bits = min(self.args[0] // 8, 4) - for y in range(self.state.ysize): - row = bytearray() - for x in range(self.state.xsize): - row += value[offset + (4 - number_of_bits) : offset + 4] - offset += 4 - rows.append(row) - self.set_as_raw(bytes([pixel for row in rows[::-1] for pixel in row])) - return -1, 0 - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(FitsImageFile.format, FitsImageFile, _accept) -Image.register_decoder("fits_gzip", FitsGzipDecoder) - -Image.register_extensions(FitsImageFile.format, [".fit", ".fits"]) diff --git a/.venv/lib/python3.12/site-packages/PIL/FliImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/FliImagePlugin.py deleted file mode 100644 index da1e8e95..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/FliImagePlugin.py +++ /dev/null @@ -1,184 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# FLI/FLC file handling. -# -# History: -# 95-09-01 fl Created -# 97-01-03 fl Fixed parser, setup decoder tile -# 98-07-15 fl Renamed offset attribute to avoid name clash -# -# Copyright (c) Secret Labs AB 1997-98. -# Copyright (c) Fredrik Lundh 1995-97. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os - -from . import Image, ImageFile, ImagePalette -from ._binary import i16le as i16 -from ._binary import i32le as i32 -from ._binary import o8 -from ._util import DeferredError - -# -# decoder - - -def _accept(prefix: bytes) -> bool: - return ( - len(prefix) >= 16 - and i16(prefix, 4) in [0xAF11, 0xAF12] - and i16(prefix, 14) in [0, 3] # flags - ) - - -## -# Image plugin for the FLI/FLC animation format. Use the seek -# method to load individual frames. - - -class FliImageFile(ImageFile.ImageFile): - format = "FLI" - format_description = "Autodesk FLI/FLC Animation" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - # HEAD - assert self.fp is not None - s = self.fp.read(128) - if not ( - _accept(s) - and s[20:22] == b"\x00" * 2 - and s[42:80] == b"\x00" * 38 - and s[88:] == b"\x00" * 40 - ): - msg = "not an FLI/FLC file" - raise SyntaxError(msg) - - # frames - self.n_frames = i16(s, 6) - self.is_animated = self.n_frames > 1 - - # image characteristics - self._mode = "P" - self._size = i16(s, 8), i16(s, 10) - - # animation speed - duration = i32(s, 16) - magic = i16(s, 4) - if magic == 0xAF11: - duration = (duration * 1000) // 70 - self.info["duration"] = duration - - # look for palette - palette = [(a, a, a) for a in range(256)] - - s = self.fp.read(16) - - self.__offset = 128 - - if i16(s, 4) == 0xF100: - # prefix chunk; ignore it - self.fp.seek(self.__offset + i32(s)) - s = self.fp.read(16) - - if i16(s, 4) == 0xF1FA: - # look for palette chunk - number_of_subchunks = i16(s, 6) - chunk_size: int | None = None - for _ in range(number_of_subchunks): - if chunk_size is not None: - self.fp.seek(chunk_size - 6, os.SEEK_CUR) - s = self.fp.read(6) - chunk_type = i16(s, 4) - if chunk_type in (4, 11): - self._palette(palette, 2 if chunk_type == 11 else 0) - break - chunk_size = i32(s) - if not chunk_size: - break - - self.palette = ImagePalette.raw( - "RGB", b"".join(o8(r) + o8(g) + o8(b) for (r, g, b) in palette) - ) - - # set things up to decode first frame - self.__frame = -1 - self._fp = self.fp - self.__rewind = self.fp.tell() - self.seek(0) - - def _palette(self, palette: list[tuple[int, int, int]], shift: int) -> None: - # load palette - - i = 0 - assert self.fp is not None - for e in range(i16(self.fp.read(2))): - s = self.fp.read(2) - i = i + s[0] - n = s[1] - if n == 0: - n = 256 - s = self.fp.read(n * 3) - for n in range(0, len(s), 3): - r = s[n] << shift - g = s[n + 1] << shift - b = s[n + 2] << shift - palette[i] = (r, g, b) - i += 1 - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if frame < self.__frame: - self._seek(0) - - for f in range(self.__frame + 1, frame + 1): - self._seek(f) - - def _seek(self, frame: int) -> None: - if isinstance(self._fp, DeferredError): - raise self._fp.ex - if frame == 0: - self.__frame = -1 - self._fp.seek(self.__rewind) - self.__offset = 128 - else: - # ensure that the previous frame was loaded - self.load() - - if frame != self.__frame + 1: - msg = f"cannot seek to frame {frame}" - raise ValueError(msg) - self.__frame = frame - - # move to next frame - self.fp = self._fp - self.fp.seek(self.__offset) - - s = self.fp.read(4) - if not s: - msg = "missing frame size" - raise EOFError(msg) - - framesize = i32(s) - - self.decodermaxblock = framesize - self.tile = [ImageFile._Tile("fli", (0, 0) + self.size, self.__offset)] - - self.__offset += framesize - - def tell(self) -> int: - return self.__frame - - -# -# registry - -Image.register_open(FliImageFile.format, FliImageFile, _accept) - -Image.register_extensions(FliImageFile.format, [".fli", ".flc"]) diff --git a/.venv/lib/python3.12/site-packages/PIL/FontFile.py b/.venv/lib/python3.12/site-packages/PIL/FontFile.py deleted file mode 100644 index 341431d3..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/FontFile.py +++ /dev/null @@ -1,159 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# base class for raster font file parsers -# -# history: -# 1997-06-05 fl created -# 1997-08-19 fl restrict image width -# -# Copyright (c) 1997-1998 by Secret Labs AB -# Copyright (c) 1997-1998 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import BinaryIO - -from . import Image, ImageFont, _binary - -WIDTH = 800 - - -def puti16( - fp: BinaryIO, values: tuple[int, int, int, int, int, int, int, int, int, int] -) -> None: - """Write network order (big-endian) 16-bit sequence""" - for v in values: - if v < 0: - v += 65536 - fp.write(_binary.o16be(v)) - - -class FontFile: - """Base class for raster font file handlers.""" - - bitmap: Image.Image | None = None - - def __init__(self) -> None: - self.info: dict[bytes, bytes | int] = {} - self.glyph: list[ - tuple[ - tuple[int, int], - tuple[int, int, int, int], - tuple[int, int, int, int], - Image.Image, - ] - | None - ] = [None] * 256 - - def __getitem__(self, ix: int) -> ( - tuple[ - tuple[int, int], - tuple[int, int, int, int], - tuple[int, int, int, int], - Image.Image, - ] - | None - ): - return self.glyph[ix] - - def compile(self) -> None: - """Create metrics and bitmap""" - - if self.bitmap: - return - - # create bitmap large enough to hold all data - h = w = maxwidth = 0 - lines = 1 - for glyph in self.glyph: - if glyph: - d, dst, src, im = glyph - h = max(h, src[3] - src[1]) - w = w + (src[2] - src[0]) - if w > WIDTH: - lines += 1 - w = src[2] - src[0] - maxwidth = max(maxwidth, w) - - xsize = maxwidth - ysize = lines * h - - if xsize == 0 and ysize == 0: - return - - self.ysize = h - - # paste glyphs into bitmap - self.bitmap = Image.new("1", (xsize, ysize)) - self.metrics: list[ - tuple[tuple[int, int], tuple[int, int, int, int], tuple[int, int, int, int]] - | None - ] = [None] * 256 - x = y = 0 - for i in range(256): - glyph = self[i] - if glyph: - d, dst, src, im = glyph - xx = src[2] - src[0] - x0, y0 = x, y - x = x + xx - if x > WIDTH: - x, y = 0, y + h - x0, y0 = x, y - x = xx - s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0 - self.bitmap.paste(im.crop(src), s) - self.metrics[i] = d, dst, s - - def _encode_metrics(self) -> bytes: - values: list[int] = [] - for i in range(256): - m = self.metrics[i] - if m: - values.extend(m[0] + m[1] + m[2]) - else: - values.extend((0,) * 10) - - data = bytearray() - for v in values: - if v < 0: - v += 65536 - data += _binary.o16be(v) - return bytes(data) - - def save(self, filename: str) -> None: - """Save font""" - - self.compile() - - # font data - if not self.bitmap: - msg = "No bitmap created" - raise ValueError(msg) - self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG") - - # font metrics - with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp: - fp.write(b"PILfont\n") - fp.write(f";;;;;;{self.ysize};\n".encode("ascii")) # HACK!!! - fp.write(b"DATA\n") - fp.write(self._encode_metrics()) - - def to_imagefont(self) -> ImageFont.ImageFont: - """Convert to ImageFont""" - - self.compile() - - # font data - if not self.bitmap: - msg = "No bitmap created" - raise ValueError(msg) - - imagefont = ImageFont.ImageFont() - imagefont._load(self.bitmap, self._encode_metrics()) - return imagefont diff --git a/.venv/lib/python3.12/site-packages/PIL/FpxImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/FpxImagePlugin.py deleted file mode 100644 index 0b06aac9..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/FpxImagePlugin.py +++ /dev/null @@ -1,258 +0,0 @@ -# -# THIS IS WORK IN PROGRESS -# -# The Python Imaging Library. -# $Id$ -# -# FlashPix support for PIL -# -# History: -# 97-01-25 fl Created (reads uncompressed RGB images only) -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import olefile - -from . import Image, ImageFile -from ._binary import i32le as i32 - -# we map from colour field tuples to (mode, rawmode) descriptors -MODES = { - # opacity - (0x00007FFE,): ("A", "L"), - # monochrome - (0x00010000,): ("L", "L"), - (0x00018000, 0x00017FFE): ("RGBA", "LA"), - # photo YCC - (0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"), - (0x00028000, 0x00028001, 0x00028002, 0x00027FFE): ("RGBA", "YCCA;P"), - # standard RGB (NIFRGB) - (0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"), - (0x00038000, 0x00038001, 0x00038002, 0x00037FFE): ("RGBA", "RGBA"), -} - - -# -# -------------------------------------------------------------------- - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(olefile.MAGIC) - - -## -# Image plugin for the FlashPix images. - - -class FpxImageFile(ImageFile.ImageFile): - format = "FPX" - format_description = "FlashPix" - - def _open(self) -> None: - # - # read the OLE directory and see if this is a likely - # to be a FlashPix file - - assert self.fp is not None - try: - self.ole = olefile.OleFileIO(self.fp) - except OSError as e: - msg = "not an FPX file; invalid OLE file" - raise SyntaxError(msg) from e - - root = self.ole.root - if not root or root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": - msg = "not an FPX file; bad root CLSID" - raise SyntaxError(msg) - - self._open_index(1) - - def _open_index(self, index: int = 1) -> None: - # - # get the Image Contents Property Set - - prop = self.ole.getproperties( - [f"Data Object Store {index:06d}", "\005Image Contents"] - ) - - # size (highest resolution) - - assert isinstance(prop[0x1000002], int) - assert isinstance(prop[0x1000003], int) - self._size = prop[0x1000002], prop[0x1000003] - - size = max(self.size) - i = 1 - while size > 64: - size = size // 2 - i += 1 - self.maxid = i - 1 - - # mode. instead of using a single field for this, flashpix - # requires you to specify the mode for each channel in each - # resolution subimage, and leaves it to the decoder to make - # sure that they all match. for now, we'll cheat and assume - # that this is always the case. - - id = self.maxid << 16 - - s = prop[0x2000002 | id] - - if not isinstance(s, bytes) or (bands := i32(s, 4)) > 4: - msg = "Invalid number of bands" - raise OSError(msg) - - # note: for now, we ignore the "uncalibrated" flag - colors = tuple(i32(s, 8 + i * 4) & 0x7FFFFFFF for i in range(bands)) - - self._mode, self.rawmode = MODES[colors] - - # load JPEG tables, if any - self.jpeg = {} - for i in range(256): - id = 0x3000001 | (i << 16) - if id in prop: - self.jpeg[i] = prop[id] - - self._open_subimage(1, self.maxid) - - def _open_subimage(self, index: int = 1, subimage: int = 0) -> None: - # - # setup tile descriptors for a given subimage - - stream = [ - f"Data Object Store {index:06d}", - f"Resolution {subimage:04d}", - "Subimage 0000 Header", - ] - - fp = self.ole.openstream(stream) - - # skip prefix - fp.read(28) - - # header stream - s = fp.read(36) - - size = i32(s, 4), i32(s, 8) - # tilecount = i32(s, 12) - xtile, ytile = i32(s, 16), i32(s, 20) - # channels = i32(s, 24) - offset = i32(s, 28) - length = i32(s, 32) - - if size != self.size: - msg = "subimage mismatch" - raise OSError(msg) - - # get tile descriptors - fp.seek(28 + offset) - s = fp.read(i32(s, 12) * length) - - x = y = 0 - xsize, ysize = size - self.tile = [] - - for i in range(0, len(s), length): - x1 = min(xsize, x + xtile) - y1 = min(ysize, y + ytile) - - compression = i32(s, i + 8) - - if compression == 0: - self.tile.append( - ImageFile._Tile( - "raw", - (x, y, x1, y1), - i32(s, i) + 28, - self.rawmode, - ) - ) - - elif compression == 1: - # FIXME: the fill decoder is not implemented - self.tile.append( - ImageFile._Tile( - "fill", - (x, y, x1, y1), - i32(s, i) + 28, - (self.rawmode, s[12:16]), - ) - ) - - elif compression == 2: - internal_color_conversion = s[14] - jpeg_tables = s[15] - rawmode = self.rawmode - - if internal_color_conversion: - # The image is stored as usual (usually YCbCr). - if rawmode == "RGBA": - # For "RGBA", data is stored as YCbCrA based on - # negative RGB. The following trick works around - # this problem : - jpegmode, rawmode = "YCbCrK", "CMYK" - else: - jpegmode = None # let the decoder decide - - else: - # The image is stored as defined by rawmode - jpegmode = rawmode - - self.tile.append( - ImageFile._Tile( - "jpeg", - (x, y, x1, y1), - i32(s, i) + 28, - (rawmode, jpegmode), - ) - ) - - # FIXME: jpeg tables are tile dependent; the prefix - # data must be placed in the tile descriptor itself! - - if jpeg_tables: - self.tile_prefix = self.jpeg[jpeg_tables] - - else: - msg = "unknown/invalid compression" - raise OSError(msg) - - x += xtile - if x >= xsize: - x, y = 0, y + ytile - if y >= ysize: - break # isn't really required - - assert self.fp is not None - self.stream = stream - self._fp = self.fp - self.fp = None - - def load(self) -> Image.core.PixelAccess | None: - if not self.fp: - self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"]) - - return ImageFile.ImageFile.load(self) - - def close(self) -> None: - self.ole.close() - super().close() - - def __exit__(self, *args: object) -> None: - self.ole.close() - super().__exit__() - - -# -# -------------------------------------------------------------------- - - -Image.register_open(FpxImageFile.format, FpxImageFile, _accept) - -Image.register_extension(FpxImageFile.format, ".fpx") diff --git a/.venv/lib/python3.12/site-packages/PIL/FtexImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/FtexImagePlugin.py deleted file mode 100644 index e4d836cb..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/FtexImagePlugin.py +++ /dev/null @@ -1,115 +0,0 @@ -""" -A Pillow loader for .ftc and .ftu files (FTEX) -Jerome Leclanche - -The contents of this file are hereby released in the public domain (CC0) -Full text of the CC0 license: - https://creativecommons.org/publicdomain/zero/1.0/ - -Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001 - -The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a -packed custom format called FTEX. This file format uses file extensions FTC -and FTU. -* FTC files are compressed textures (using standard texture compression). -* FTU files are not compressed. -Texture File Format -The FTC and FTU texture files both use the same format. This -has the following structure: -{header} -{format_directory} -{data} -Where: -{header} = { - u32:magic, - u32:version, - u32:width, - u32:height, - u32:mipmap_count, - u32:format_count -} - -* The "magic" number is "FTEX". -* "width" and "height" are the dimensions of the texture. -* "mipmap_count" is the number of mipmaps in the texture. -* "format_count" is the number of texture formats (different versions of the -same texture) in this file. - -{format_directory} = format_count * { u32:format, u32:where } - -The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB -uncompressed textures. -The texture data for a format starts at the position "where" in the file. - -Each set of texture data in the file has the following structure: -{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } } -* "mipmap_size" is the number of bytes in that mip level. For compressed -textures this is the size of the texture data compressed with DXT1. For 24 bit -uncompressed textures, this is 3 * width * height. Following this are the image -bytes for that mipmap level. - -Note: All data is stored in little-Endian (Intel) byte order. -""" - -from __future__ import annotations - -import struct -from enum import IntEnum -from io import BytesIO - -from . import Image, ImageFile - -MAGIC = b"FTEX" - - -class Format(IntEnum): - DXT1 = 0 - UNCOMPRESSED = 1 - - -class FtexImageFile(ImageFile.ImageFile): - format = "FTEX" - format_description = "Texture File Format (IW2:EOC)" - - def _open(self) -> None: - assert self.fp is not None - if not _accept(self.fp.read(4)): - msg = "not an FTEX file" - raise SyntaxError(msg) - struct.unpack(" None: - pass - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(MAGIC) - - -Image.register_open(FtexImageFile.format, FtexImageFile, _accept) -Image.register_extensions(FtexImageFile.format, [".ftc", ".ftu"]) diff --git a/.venv/lib/python3.12/site-packages/PIL/GbrImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/GbrImagePlugin.py deleted file mode 100644 index ec666c81..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/GbrImagePlugin.py +++ /dev/null @@ -1,103 +0,0 @@ -# -# The Python Imaging Library -# -# load a GIMP brush file -# -# History: -# 96-03-14 fl Created -# 16-01-08 es Version 2 -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# Copyright (c) Eric Soroos 2016. -# -# See the README file for information on usage and redistribution. -# -# -# See https://github.com/GNOME/gimp/blob/mainline/devel-docs/gbr.txt for -# format documentation. -# -# This code Interprets version 1 and 2 .gbr files. -# Version 1 files are obsolete, and should not be used for new -# brushes. -# Version 2 files are saved by GIMP v2.8 (at least) -# Version 3 files have a format specifier of 18 for 16bit floats in -# the color depth field. This is currently unsupported by Pillow. -from __future__ import annotations - -from . import Image, ImageFile -from ._binary import i32be as i32 - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 8 and i32(prefix, 0) >= 20 and i32(prefix, 4) in (1, 2) - - -## -# Image plugin for the GIMP brush format. - - -class GbrImageFile(ImageFile.ImageFile): - format = "GBR" - format_description = "GIMP brush file" - - def _open(self) -> None: - assert self.fp is not None - header_size = i32(self.fp.read(4)) - if header_size < 20: - msg = "not a GIMP brush" - raise SyntaxError(msg) - version = i32(self.fp.read(4)) - if version not in (1, 2): - msg = f"Unsupported GIMP brush version: {version}" - raise SyntaxError(msg) - - width = i32(self.fp.read(4)) - height = i32(self.fp.read(4)) - color_depth = i32(self.fp.read(4)) - if width == 0 or height == 0: - msg = "not a GIMP brush" - raise SyntaxError(msg) - if color_depth not in (1, 4): - msg = f"Unsupported GIMP brush color depth: {color_depth}" - raise SyntaxError(msg) - - if version == 1: - comment_length = header_size - 20 - else: - comment_length = header_size - 28 - magic_number = self.fp.read(4) - if magic_number != b"GIMP": - msg = "not a GIMP brush, bad magic number" - raise SyntaxError(msg) - self.info["spacing"] = i32(self.fp.read(4)) - - self.info["comment"] = self.fp.read(comment_length)[:-1] - - if color_depth == 1: - self._mode = "L" - else: - self._mode = "RGBA" - - self._size = width, height - - # Image might not be small - Image._decompression_bomb_check(self.size) - - # Data is an uncompressed block of w * h * bytes/pixel - self._data_size = width * height * color_depth - - def load(self) -> Image.core.PixelAccess | None: - if self._im is None: - assert self.fp is not None - self.im = Image.core.new(self.mode, self.size) - self.frombytes(self.fp.read(self._data_size)) - return Image.Image.load(self) - - -# -# registry - - -Image.register_open(GbrImageFile.format, GbrImageFile, _accept) -Image.register_extension(GbrImageFile.format, ".gbr") diff --git a/.venv/lib/python3.12/site-packages/PIL/GdImageFile.py b/.venv/lib/python3.12/site-packages/PIL/GdImageFile.py deleted file mode 100644 index d73bc198..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/GdImageFile.py +++ /dev/null @@ -1,103 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# GD file handling -# -# History: -# 1996-04-12 fl Created -# -# Copyright (c) 1997 by Secret Labs AB. -# Copyright (c) 1996 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - - -""" -.. note:: - This format cannot be automatically recognized, so the - class is not registered for use with :py:func:`PIL.Image.open()`. To open a - gd file, use the :py:func:`PIL.GdImageFile.open()` function instead. - -.. warning:: - THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This - implementation is provided for convenience and demonstrational - purposes only. -""" - -from __future__ import annotations - -from typing import IO - -from . import ImageFile, ImagePalette, UnidentifiedImageError -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._typing import StrOrBytesPath - - -class GdImageFile(ImageFile.ImageFile): - """ - Image plugin for the GD uncompressed format. Note that this format - is not supported by the standard :py:func:`PIL.Image.open()` function. To use - this plugin, you have to import the :py:mod:`PIL.GdImageFile` module and - use the :py:func:`PIL.GdImageFile.open()` function. - """ - - format = "GD" - format_description = "GD uncompressed images" - - def _open(self) -> None: - # Header - assert self.fp is not None - - s = self.fp.read(1037) - - if i16(s) not in [65534, 65535]: - msg = "Not a valid GD 2.x .gd file" - raise SyntaxError(msg) - - self._mode = "P" - self._size = i16(s, 2), i16(s, 4) - - true_color = s[6] - true_color_offset = 2 if true_color else 0 - - # transparency index - tindex = i32(s, 7 + true_color_offset) - if tindex < 256: - self.info["transparency"] = tindex - - self.palette = ImagePalette.raw( - "RGBX", s[7 + true_color_offset + 6 : 7 + true_color_offset + 6 + 256 * 4] - ) - - self.tile = [ - ImageFile._Tile( - "raw", - (0, 0) + self.size, - 7 + true_color_offset + 6 + 256 * 4, - "L", - ) - ] - - -def open(fp: StrOrBytesPath | IO[bytes], mode: str = "r") -> GdImageFile: - """ - Load texture from a GD image file. - - :param fp: GD file name, or an opened file handle. - :param mode: Optional mode. In this version, if the mode argument - is given, it must be "r". - :returns: An image instance. - :raises OSError: If the image could not be read. - """ - if mode != "r": - msg = "bad mode" - raise ValueError(msg) - - try: - return GdImageFile(fp) - except SyntaxError as e: - msg = "cannot identify this image file" - raise UnidentifiedImageError(msg) from e diff --git a/.venv/lib/python3.12/site-packages/PIL/GifImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/GifImagePlugin.py deleted file mode 100644 index b8db5d83..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/GifImagePlugin.py +++ /dev/null @@ -1,1223 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# GIF file handling -# -# History: -# 1995-09-01 fl Created -# 1996-12-14 fl Added interlace support -# 1996-12-30 fl Added animation support -# 1997-01-05 fl Added write support, fixed local colour map bug -# 1997-02-23 fl Make sure to load raster data in getdata() -# 1997-07-05 fl Support external decoder (0.4) -# 1998-07-09 fl Handle all modes when saving (0.5) -# 1998-07-15 fl Renamed offset attribute to avoid name clash -# 2001-04-16 fl Added rewind support (seek to frame 0) (0.6) -# 2001-04-17 fl Added palette optimization (0.7) -# 2002-06-06 fl Added transparency support for save (0.8) -# 2004-02-24 fl Disable interlacing for small images -# -# Copyright (c) 1997-2004 by Secret Labs AB -# Copyright (c) 1995-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import itertools -import math -import os -import subprocess -from enum import IntEnum -from functools import cached_property -from typing import Any, NamedTuple, cast - -from . import ( - Image, - ImageChops, - ImageFile, - ImageMath, - ImageOps, - ImagePalette, - ImageSequence, -) -from ._binary import i16le as i16 -from ._binary import o8 -from ._binary import o16le as o16 -from ._util import DeferredError - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import IO, Literal - - from . import _imaging - from ._typing import Buffer - - -class LoadingStrategy(IntEnum): - """.. versionadded:: 9.1.0""" - - RGB_AFTER_FIRST = 0 - RGB_AFTER_DIFFERENT_PALETTE_ONLY = 1 - RGB_ALWAYS = 2 - - -#: .. versionadded:: 9.1.0 -LOADING_STRATEGY = LoadingStrategy.RGB_AFTER_FIRST - -# -------------------------------------------------------------------- -# Identify/read GIF files - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith((b"GIF87a", b"GIF89a")) - - -## -# Image plugin for GIF images. This plugin supports both GIF87 and -# GIF89 images. - - -class GifImageFile(ImageFile.ImageFile): - format = "GIF" - format_description = "Compuserve GIF" - _close_exclusive_fp_after_loading = False - - global_palette = None - - def data(self) -> bytes | None: - assert self.fp is not None - s = self.fp.read(1) - if s and s[0]: - return self.fp.read(s[0]) - return None - - def _is_palette_needed(self, p: bytes) -> bool: - for i in range(0, len(p), 3): - if not (i // 3 == p[i] == p[i + 1] == p[i + 2]): - return True - return False - - def _open(self) -> None: - # Screen - assert self.fp is not None - s = self.fp.read(13) - if not _accept(s): - msg = "not a GIF file" - raise SyntaxError(msg) - - self.info["version"] = s[:6] - self._size = i16(s, 6), i16(s, 8) - flags = s[10] - bits = (flags & 7) + 1 - - if flags & 128: - # get global palette - self.info["background"] = s[11] - # check if palette contains colour indices - p = self.fp.read(3 << bits) - if self._is_palette_needed(p): - palette = ImagePalette.raw("RGB", p) - self.global_palette = self.palette = palette - - self._fp = self.fp # FIXME: hack - self.__rewind = self.fp.tell() - self._n_frames: int | None = None - self._seek(0) # get ready to read first frame - - @property - def n_frames(self) -> int: - if self._n_frames is None: - current = self.tell() - try: - while True: - self._seek(self.tell() + 1, False) - except EOFError: - self._n_frames = self.tell() + 1 - self.seek(current) - return self._n_frames - - @cached_property - def is_animated(self) -> bool: - if self._n_frames is not None: - return self._n_frames != 1 - - current = self.tell() - if current: - return True - - try: - self._seek(1, False) - is_animated = True - except EOFError: - is_animated = False - - self.seek(current) - return is_animated - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if frame < self.__frame: - self._im = None - self._seek(0) - - last_frame = self.__frame - try: - for f in range(self.__frame + 1, frame + 1): - self._seek(f) - except EOFError as e: - self.seek(last_frame) - msg = "no more images in GIF file" - raise EOFError(msg) from e - - def _seek(self, frame: int, update_image: bool = True) -> None: - if isinstance(self._fp, DeferredError): - raise self._fp.ex - if frame == 0: - # rewind - self.__offset = 0 - self.dispose: _imaging.ImagingCore | None = None - self.__frame = -1 - self._fp.seek(self.__rewind) - self.disposal_method = 0 - if "comment" in self.info: - del self.info["comment"] - else: - # ensure that the previous frame was loaded - if self.tile and update_image: - self.load() - - if frame != self.__frame + 1: - msg = f"cannot seek to frame {frame}" - raise ValueError(msg) - - self.fp = self._fp - if self.__offset: - # backup to last frame - self.fp.seek(self.__offset) - while self.data(): - pass - self.__offset = 0 - - s = self.fp.read(1) - if not s or s == b";": - msg = "no more images in GIF file" - raise EOFError(msg) - - palette: ImagePalette.ImagePalette | Literal[False] | None = None - - info: dict[str, Any] = {} - frame_transparency = None - interlace = None - frame_dispose_extent = None - while True: - if not s: - s = self.fp.read(1) - if not s or s == b";": - break - - elif s == b"!": - # - # extensions - # - s = self.fp.read(1) - block = self.data() - if s[0] == 249 and block is not None: - # - # graphic control extension - # - flags = block[0] - if flags & 1: - frame_transparency = block[3] - info["duration"] = i16(block, 1) * 10 - - # disposal method - find the value of bits 4 - 6 - dispose_bits = 0b00011100 & flags - dispose_bits = dispose_bits >> 2 - if dispose_bits: - # only set the dispose if it is not - # unspecified. I'm not sure if this is - # correct, but it seems to prevent the last - # frame from looking odd for some animations - self.disposal_method = dispose_bits - elif s[0] == 254: - # - # comment extension - # - comment = b"" - - # Read this comment block - while block: - comment += block - block = self.data() - - if "comment" in info: - # If multiple comment blocks in frame, separate with \n - info["comment"] += b"\n" + comment - else: - info["comment"] = comment - s = b"" - continue - elif s[0] == 255 and frame == 0 and block is not None: - # - # application extension - # - info["extension"] = block, self.fp.tell() - if block.startswith(b"NETSCAPE2.0"): - block = self.data() - if block and len(block) >= 3 and block[0] == 1: - self.info["loop"] = i16(block, 1) - while self.data(): - pass - - elif s == b",": - # - # local image - # - s = self.fp.read(9) - - # extent - x0, y0 = i16(s, 0), i16(s, 2) - x1, y1 = x0 + i16(s, 4), y0 + i16(s, 6) - if (x1 > self.size[0] or y1 > self.size[1]) and update_image: - self._size = max(x1, self.size[0]), max(y1, self.size[1]) - Image._decompression_bomb_check(self._size) - frame_dispose_extent = x0, y0, x1, y1 - flags = s[8] - - interlace = (flags & 64) != 0 - - if flags & 128: - bits = (flags & 7) + 1 - p = self.fp.read(3 << bits) - if self._is_palette_needed(p): - palette = ImagePalette.raw("RGB", p) - else: - palette = False - - # image data - bits = self.fp.read(1)[0] - self.__offset = self.fp.tell() - break - s = b"" - - if interlace is None: - msg = "image not found in GIF frame" - raise EOFError(msg) - - self.__frame = frame - if not update_image: - return - - self.tile = [] - - if self.dispose: - self.im.paste(self.dispose, self.dispose_extent) - - self._frame_palette = palette if palette is not None else self.global_palette - self._frame_transparency = frame_transparency - if frame == 0: - if self._frame_palette: - if LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: - self._mode = "RGBA" if frame_transparency is not None else "RGB" - else: - self._mode = "P" - else: - self._mode = "L" - - if palette: - self.palette = palette - elif self.global_palette: - from copy import copy - - self.palette = copy(self.global_palette) - else: - self.palette = None - else: - if self.mode == "P": - if ( - LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY - or palette - ): - if "transparency" in self.info: - self.im.putpalettealpha(self.info["transparency"], 0) - self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG) - self._mode = "RGBA" - del self.info["transparency"] - else: - self._mode = "RGB" - self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) - - def _rgb(color: int) -> tuple[int, int, int]: - if self._frame_palette: - if color * 3 + 3 > len(self._frame_palette.palette): - color = 0 - return cast( - tuple[int, int, int], - tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]), - ) - else: - return (color, color, color) - - self.dispose = None - self.dispose_extent: tuple[int, int, int, int] | None = frame_dispose_extent - if self.dispose_extent and self.disposal_method >= 2: - try: - if self.disposal_method == 2: - # replace with background colour - - # only dispose the extent in this frame - x0, y0, x1, y1 = self.dispose_extent - dispose_size = (x1 - x0, y1 - y0) - - Image._decompression_bomb_check(dispose_size) - - # by convention, attempt to use transparency first - dispose_mode = "P" - color = self.info.get("transparency", frame_transparency) - if color is not None: - if self.mode in ("RGB", "RGBA"): - dispose_mode = "RGBA" - color = _rgb(color) + (0,) - else: - color = self.info.get("background", 0) - if self.mode in ("RGB", "RGBA"): - dispose_mode = "RGB" - color = _rgb(color) - self.dispose = Image.core.fill(dispose_mode, dispose_size, color) - else: - # replace with previous contents - if self._im is not None: - # only dispose the extent in this frame - self.dispose = self._crop(self.im, self.dispose_extent) - elif frame_transparency is not None: - x0, y0, x1, y1 = self.dispose_extent - dispose_size = (x1 - x0, y1 - y0) - - Image._decompression_bomb_check(dispose_size) - dispose_mode = "P" - color = frame_transparency - if self.mode in ("RGB", "RGBA"): - dispose_mode = "RGBA" - color = _rgb(frame_transparency) + (0,) - self.dispose = Image.core.fill( - dispose_mode, dispose_size, color - ) - except AttributeError: - pass - - if interlace is not None: - transparency = -1 - if frame_transparency is not None: - if frame == 0: - if LOADING_STRATEGY != LoadingStrategy.RGB_ALWAYS: - self.info["transparency"] = frame_transparency - elif self.mode not in ("RGB", "RGBA"): - transparency = frame_transparency - self.tile = [ - ImageFile._Tile( - "gif", - (x0, y0, x1, y1), - self.__offset, - (bits, interlace, transparency), - ) - ] - - if info.get("comment"): - self.info["comment"] = info["comment"] - for k in ["duration", "extension"]: - if k in info: - self.info[k] = info[k] - elif k in self.info: - del self.info[k] - - def load_prepare(self) -> None: - temp_mode = "P" if self._frame_palette else "L" - self._prev_im = None - if self.__frame == 0: - if self._frame_transparency is not None: - self.im = Image.core.fill( - temp_mode, self.size, self._frame_transparency - ) - elif self.mode in ("RGB", "RGBA"): - self._prev_im = self.im - if self._frame_palette: - self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) - self.im.putpalette("RGB", *self._frame_palette.getdata()) - else: - self._im = None - if not self._prev_im and self._im is not None and self.size != self.im.size: - expanded_im = Image.core.fill(self.im.mode, self.size) - if self._frame_palette: - expanded_im.putpalette("RGB", *self._frame_palette.getdata()) - expanded_im.paste(self.im, (0, 0) + self.im.size) - - self.im = expanded_im - self._mode = temp_mode - self._frame_palette = None - - super().load_prepare() - - def load_end(self) -> None: - if self.__frame == 0: - if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: - if self._frame_transparency is not None: - self.im.putpalettealpha(self._frame_transparency, 0) - self._mode = "RGBA" - else: - self._mode = "RGB" - self.im = self.im.convert(self.mode, Image.Dither.FLOYDSTEINBERG) - return - if not self._prev_im: - return - if self.size != self._prev_im.size: - if self._frame_transparency is not None: - expanded_im = Image.core.fill("RGBA", self.size) - else: - expanded_im = Image.core.fill("P", self.size) - expanded_im.putpalette("RGB", "RGB", self.im.getpalette()) - expanded_im = expanded_im.convert("RGB") - expanded_im.paste(self._prev_im, (0, 0) + self._prev_im.size) - - self._prev_im = expanded_im - assert self._prev_im is not None - if self._frame_transparency is not None: - if self.mode == "L": - frame_im = self.im.convert_transparent("LA", self._frame_transparency) - else: - self.im.putpalettealpha(self._frame_transparency, 0) - frame_im = self.im.convert("RGBA") - else: - frame_im = self.im.convert("RGB") - - assert self.dispose_extent is not None - frame_im = self._crop(frame_im, self.dispose_extent) - - self.im = self._prev_im - self._mode = self.im.mode - if frame_im.mode in ("LA", "RGBA"): - self.im.paste(frame_im, self.dispose_extent, frame_im) - else: - self.im.paste(frame_im, self.dispose_extent) - - def tell(self) -> int: - return self.__frame - - -# -------------------------------------------------------------------- -# Write GIF files - - -RAWMODE = {"1": "L", "L": "L", "P": "P"} - - -def _normalize_mode(im: Image.Image) -> Image.Image: - """ - Takes an image (or frame), returns an image in a mode that is appropriate - for saving in a Gif. - - It may return the original image, or it may return an image converted to - palette or 'L' mode. - - :param im: Image object - :returns: Image object - """ - if im.mode in RAWMODE: - im.load() - return im - if Image.getmodebase(im.mode) == "RGB": - im = im.convert("P", palette=Image.Palette.ADAPTIVE) - assert im.palette is not None - if im.palette.mode == "RGBA": - for rgba in im.palette.colors: - if rgba[3] == 0: - im.info["transparency"] = im.palette.colors[rgba] - break - return im - return im.convert("L") - - -_Palette = bytes | bytearray | list[int] | ImagePalette.ImagePalette - - -def _normalize_palette( - im: Image.Image, palette: _Palette | None, info: dict[str, Any] -) -> Image.Image: - """ - Normalizes the palette for image. - - Sets the palette to the incoming palette, if provided. - - Ensures that there's a palette for L mode images - - Optimizes the palette if necessary/desired. - - :param im: Image object - :param palette: bytes object containing the source palette, or .... - :param info: encoderinfo - :returns: Image object - """ - source_palette = None - if palette: - # a bytes palette - if isinstance(palette, (bytes, bytearray, list)): - source_palette = bytearray(palette[:768]) - if isinstance(palette, ImagePalette.ImagePalette): - source_palette = bytearray(palette.palette) - - if im.mode == "P": - if not source_palette: - im_palette = im.getpalette(None) - assert im_palette is not None - source_palette = bytearray(im_palette) - else: # L-mode - if not source_palette: - source_palette = bytearray(i // 3 for i in range(768)) - im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette) - assert source_palette is not None - - if palette: - used_palette_colors: list[int | None] = [] - assert im.palette is not None - for i in range(0, len(source_palette), 3): - source_color = tuple(source_palette[i : i + 3]) - index = im.palette.colors.get(source_color) - if index in used_palette_colors: - index = None - used_palette_colors.append(index) - for i, index in enumerate(used_palette_colors): - if index is None: - for j in range(len(used_palette_colors)): - if j not in used_palette_colors: - used_palette_colors[i] = j - break - dest_map: list[int] = [] - for index in used_palette_colors: - assert index is not None - dest_map.append(index) - im = im.remap_palette(dest_map) - else: - optimized_palette_colors = _get_optimize(im, info) - if optimized_palette_colors is not None: - im = im.remap_palette(optimized_palette_colors, source_palette) - if "transparency" in info: - try: - info["transparency"] = optimized_palette_colors.index( - info["transparency"] - ) - except ValueError: - del info["transparency"] - return im - - assert im.palette is not None - im.palette.palette = source_palette - return im - - -def _write_single_frame( - im: Image.Image, - fp: IO[bytes], - palette: _Palette | None, -) -> None: - im_out = _normalize_mode(im) - for k, v in im_out.info.items(): - if isinstance(k, str): - im.encoderinfo.setdefault(k, v) - im_out = _normalize_palette(im_out, palette, im.encoderinfo) - - for s in _get_global_header(im_out, im.encoderinfo): - fp.write(s) - - # local image header - flags = 0 - if get_interlace(im): - flags = flags | 64 - _write_local_header(fp, im, (0, 0), flags) - - im_out.encoderconfig = (8, get_interlace(im)) - ImageFile._save( - im_out, fp, [ImageFile._Tile("gif", (0, 0) + im.size, 0, RAWMODE[im_out.mode])] - ) - - fp.write(b"\0") # end of image data - - -def _getbbox( - base_im: Image.Image, im_frame: Image.Image -) -> tuple[Image.Image, tuple[int, int, int, int] | None]: - palette_bytes = [ - bytes(im.palette.palette) if im.palette else b"" for im in (base_im, im_frame) - ] - if palette_bytes[0] != palette_bytes[1]: - im_frame = im_frame.convert("RGBA") - base_im = base_im.convert("RGBA") - delta = ImageChops.subtract_modulo(im_frame, base_im) - return delta, delta.getbbox(alpha_only=False) - - -class _Frame(NamedTuple): - im: Image.Image - bbox: tuple[int, int, int, int] | None - encoderinfo: dict[str, Any] - - -def _write_multiple_frames( - im: Image.Image, fp: IO[bytes], palette: _Palette | None -) -> bool: - duration = im.encoderinfo.get("duration") - disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) - - im_frames: list[_Frame] = [] - previous_im: Image.Image | None = None - frame_count = 0 - background_im = None - for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])): - for im_frame in ImageSequence.Iterator(imSequence): - # a copy is required here since seek can still mutate the image - im_frame = _normalize_mode(im_frame.copy()) - if frame_count == 0: - for k, v in im_frame.info.items(): - if k == "transparency": - continue - if isinstance(k, str): - im.encoderinfo.setdefault(k, v) - - encoderinfo = im.encoderinfo.copy() - if "transparency" in im_frame.info: - encoderinfo.setdefault("transparency", im_frame.info["transparency"]) - im_frame = _normalize_palette(im_frame, palette, encoderinfo) - if isinstance(duration, (list, tuple)): - encoderinfo["duration"] = duration[frame_count] - elif duration is None and "duration" in im_frame.info: - encoderinfo["duration"] = im_frame.info["duration"] - if isinstance(disposal, (list, tuple)): - encoderinfo["disposal"] = disposal[frame_count] - frame_count += 1 - - diff_frame = None - if im_frames and previous_im: - # delta frame - delta, bbox = _getbbox(previous_im, im_frame) - if not bbox: - # This frame is identical to the previous frame - if encoderinfo.get("duration"): - im_frames[-1].encoderinfo["duration"] += encoderinfo["duration"] - continue - if im_frames[-1].encoderinfo.get("disposal") == 2: - # To appear correctly in viewers using a convention, - # only consider transparency, and not background color - color = im.encoderinfo.get( - "transparency", im.info.get("transparency") - ) - if color is not None: - if background_im is None: - background = _get_background(im_frame, color) - background_im = Image.new("P", im_frame.size, background) - first_palette = im_frames[0].im.palette - assert first_palette is not None - background_im.putpalette(first_palette, first_palette.mode) - bbox = _getbbox(background_im, im_frame)[1] - else: - bbox = (0, 0) + im_frame.size - elif encoderinfo.get("optimize") and im_frame.mode != "1": - if "transparency" not in encoderinfo: - assert im_frame.palette is not None - try: - encoderinfo["transparency"] = ( - im_frame.palette._new_color_index(im_frame) - ) - except ValueError: - pass - if "transparency" in encoderinfo: - # When the delta is zero, fill the image with transparency - diff_frame = im_frame.copy() - fill = Image.new("P", delta.size, encoderinfo["transparency"]) - if delta.mode == "RGBA": - r, g, b, a = delta.split() - mask = ImageMath.lambda_eval( - lambda args: args["convert"]( - args["max"]( - args["max"]( - args["max"](args["r"], args["g"]), args["b"] - ), - args["a"], - ) - * 255, - "1", - ), - r=r, - g=g, - b=b, - a=a, - ) - else: - if delta.mode == "P": - # Convert to L without considering palette - delta_l = Image.new("L", delta.size) - delta_l.putdata(delta.get_flattened_data()) - delta = delta_l - mask = ImageMath.lambda_eval( - lambda args: args["convert"](args["im"] * 255, "1"), - im=delta, - ) - diff_frame.paste(fill, mask=ImageOps.invert(mask)) - else: - bbox = None - previous_im = im_frame - im_frames.append(_Frame(diff_frame or im_frame, bbox, encoderinfo)) - - if len(im_frames) == 1: - if "duration" in im.encoderinfo: - # Since multiple frames will not be written, use the combined duration - im.encoderinfo["duration"] = im_frames[0].encoderinfo["duration"] - return False - - for frame_data in im_frames: - im_frame = frame_data.im - if not frame_data.bbox: - # global header - for s in _get_global_header(im_frame, frame_data.encoderinfo): - fp.write(s) - offset = (0, 0) - else: - # compress difference - if not palette: - frame_data.encoderinfo["include_color_table"] = True - - if frame_data.bbox != (0, 0) + im_frame.size: - im_frame = im_frame.crop(frame_data.bbox) - offset = frame_data.bbox[:2] - _write_frame_data(fp, im_frame, offset, frame_data.encoderinfo) - return True - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, save_all=True) - - -def _save( - im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False -) -> None: - # header - if "palette" in im.encoderinfo or "palette" in im.info: - palette = im.encoderinfo.get("palette", im.info.get("palette")) - else: - palette = None - im.encoderinfo.setdefault("optimize", True) - - if not save_all or not _write_multiple_frames(im, fp, palette): - _write_single_frame(im, fp, palette) - - fp.write(b";") # end of file - - if hasattr(fp, "flush"): - fp.flush() - - -def get_interlace(im: Image.Image) -> int: - interlace = im.encoderinfo.get("interlace", 1) - - # workaround for @PIL153 - if min(im.size) < 16: - interlace = 0 - - return interlace - - -def _write_local_header( - fp: IO[bytes], im: Image.Image, offset: tuple[int, int], flags: int -) -> None: - try: - transparency = im.encoderinfo["transparency"] - except KeyError: - transparency = None - - if "duration" in im.encoderinfo: - duration = int(im.encoderinfo["duration"] / 10) - else: - duration = 0 - - disposal = int(im.encoderinfo.get("disposal", 0)) - - if transparency is not None or duration != 0 or disposal: - packed_flag = 1 if transparency is not None else 0 - packed_flag |= disposal << 2 - - fp.write( - b"!" - + o8(249) # extension intro - + o8(4) # length - + o8(packed_flag) # packed fields - + o16(duration) # duration - + o8(transparency or 0) # transparency index - + o8(0) - ) - - include_color_table = im.encoderinfo.get("include_color_table") - if include_color_table: - palette_bytes = _get_palette_bytes(im) - color_table_size = _get_color_table_size(palette_bytes) - if color_table_size: - flags = flags | 128 # local color table flag - flags = flags | color_table_size - - fp.write( - b"," - + o16(offset[0]) # offset - + o16(offset[1]) - + o16(im.size[0]) # size - + o16(im.size[1]) - + o8(flags) # flags - ) - if include_color_table and color_table_size: - fp.write(_get_header_palette(palette_bytes)) - fp.write(o8(8)) # bits - - -def _save_netpbm(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - # Unused by default. - # To use, uncomment the register_save call at the end of the file. - # - # If you need real GIF compression and/or RGB quantization, you - # can use the external NETPBM/PBMPLUS utilities. See comments - # below for information on how to enable this. - tempfile = im._dump() - - try: - with open(filename, "wb") as f: - if im.mode != "RGB": - subprocess.check_call( - ["ppmtogif", tempfile], stdout=f, stderr=subprocess.DEVNULL - ) - else: - # Pipe ppmquant output into ppmtogif - # "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename) - quant_cmd = ["ppmquant", "256", tempfile] - togif_cmd = ["ppmtogif"] - quant_proc = subprocess.Popen( - quant_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL - ) - togif_proc = subprocess.Popen( - togif_cmd, - stdin=quant_proc.stdout, - stdout=f, - stderr=subprocess.DEVNULL, - ) - - # Allow ppmquant to receive SIGPIPE if ppmtogif exits - assert quant_proc.stdout is not None - quant_proc.stdout.close() - - retcode = quant_proc.wait() - if retcode: - raise subprocess.CalledProcessError(retcode, quant_cmd) - - retcode = togif_proc.wait() - if retcode: - raise subprocess.CalledProcessError(retcode, togif_cmd) - finally: - try: - os.unlink(tempfile) - except OSError: - pass - - -# Force optimization so that we can test performance against -# cases where it took lots of memory and time previously. -_FORCE_OPTIMIZE = False - - -def _get_optimize(im: Image.Image, info: dict[str, Any]) -> list[int] | None: - """ - Palette optimization is a potentially expensive operation. - - This function determines if the palette should be optimized using - some heuristics, then returns the list of palette entries in use. - - :param im: Image object - :param info: encoderinfo - :returns: list of indexes of palette entries in use, or None - """ - if ( - im.mode in ("P", "L") - and info - and info.get("optimize") - and im.width != 0 - and im.height != 0 - ): - # Potentially expensive operation. - - # The palette saves 3 bytes per color not used, but palette - # lengths are restricted to 3*(2**N) bytes. Max saving would - # be 768 -> 6 bytes if we went all the way down to 2 colors. - # * If we're over 128 colors, we can't save any space. - # * If there aren't any holes, it's not worth collapsing. - # * If we have a 'large' image, the palette is in the noise. - - # create the new palette if not every color is used - optimise = _FORCE_OPTIMIZE or im.mode == "L" - if optimise or im.width * im.height < 512 * 512: - # check which colors are used - used_palette_colors = [] - for i, count in enumerate(im.histogram()): - if count: - used_palette_colors.append(i) - - if optimise or max(used_palette_colors) >= len(used_palette_colors): - return used_palette_colors - - assert im.palette is not None - num_palette_colors = len(im.palette.palette) // Image.getmodebands( - im.palette.mode - ) - current_palette_size = 1 << (num_palette_colors - 1).bit_length() - if ( - # check that the palette would become smaller when saved - len(used_palette_colors) <= current_palette_size // 2 - # check that the palette is not already the smallest possible size - and current_palette_size > 2 - ): - return used_palette_colors - return None - - -def _get_color_table_size(palette_bytes: bytes) -> int: - # calculate the palette size for the header - if not palette_bytes: - return 0 - elif len(palette_bytes) < 9: - return 1 - else: - return math.ceil(math.log(len(palette_bytes) // 3, 2)) - 1 - - -def _get_header_palette(palette_bytes: bytes) -> bytes: - """ - Returns the palette, null padded to the next power of 2 (*3) bytes - suitable for direct inclusion in the GIF header - - :param palette_bytes: Unpadded palette bytes, in RGBRGB form - :returns: Null padded palette - """ - color_table_size = _get_color_table_size(palette_bytes) - - # add the missing amount of bytes - # the palette has to be 2< 0: - palette_bytes += o8(0) * 3 * actual_target_size_diff - return palette_bytes - - -def _get_palette_bytes(im: Image.Image) -> bytes: - """ - Gets the palette for inclusion in the gif header - - :param im: Image object - :returns: Bytes, len<=768 suitable for inclusion in gif header - """ - if not im.palette: - return b"" - - palette = bytes(im.palette.palette) - if im.palette.mode == "RGBA": - palette = b"".join(palette[i * 4 : i * 4 + 3] for i in range(len(palette) // 3)) - return palette - - -def _get_background( - im: Image.Image, - info_background: int | tuple[int, int, int] | tuple[int, int, int, int] | None, -) -> int: - background = 0 - if info_background: - if isinstance(info_background, tuple): - # WebPImagePlugin stores an RGBA value in info["background"] - # So it must be converted to the same format as GifImagePlugin's - # info["background"] - a global color table index - assert im.palette is not None - try: - background = im.palette.getcolor(info_background, im) - except ValueError as e: - if str(e) not in ( - # If all 256 colors are in use, - # then there is no need for the background color - "cannot allocate more than 256 colors", - # Ignore non-opaque WebP background - "cannot add non-opaque RGBA color to RGB palette", - ): - raise - else: - background = info_background - return background - - -def _get_global_header(im: Image.Image, info: dict[str, Any]) -> list[bytes]: - """Return a list of strings representing a GIF header""" - - # Header Block - # https://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp - - version = b"87a" - if im.info.get("version") == b"89a" or ( - info - and ( - "transparency" in info - or info.get("loop") is not None - or info.get("duration") - or info.get("comment") - ) - ): - version = b"89a" - - background = _get_background(im, info.get("background")) - - palette_bytes = _get_palette_bytes(im) - color_table_size = _get_color_table_size(palette_bytes) - - header = [ - b"GIF" # signature - + version # version - + o16(im.size[0]) # canvas width - + o16(im.size[1]), # canvas height - # Logical Screen Descriptor - # size of global color table + global color table flag - o8(color_table_size + 128), # packed fields - # background + reserved/aspect - o8(background) + o8(0), - # Global Color Table - _get_header_palette(palette_bytes), - ] - if info.get("loop") is not None: - header.append( - b"!" - + o8(255) # extension intro - + o8(11) - + b"NETSCAPE2.0" - + o8(3) - + o8(1) - + o16(info["loop"]) # number of loops - + o8(0) - ) - if info.get("comment"): - comment_block = b"!" + o8(254) # extension intro - - comment = info["comment"] - if isinstance(comment, str): - comment = comment.encode() - for i in range(0, len(comment), 255): - subblock = comment[i : i + 255] - comment_block += o8(len(subblock)) + subblock - - comment_block += o8(0) - header.append(comment_block) - return header - - -def _write_frame_data( - fp: IO[bytes], - im_frame: Image.Image, - offset: tuple[int, int], - params: dict[str, Any], -) -> None: - try: - im_frame.encoderinfo = params - - # local image header - _write_local_header(fp, im_frame, offset, 0) - - ImageFile._save( - im_frame, - fp, - [ImageFile._Tile("gif", (0, 0) + im_frame.size, 0, RAWMODE[im_frame.mode])], - ) - - fp.write(b"\0") # end of image data - finally: - del im_frame.encoderinfo - - -# -------------------------------------------------------------------- -# Legacy GIF utilities - - -def getheader( - im: Image.Image, palette: _Palette | None = None, info: dict[str, Any] | None = None -) -> tuple[list[bytes], list[int] | None]: - """ - Legacy Method to get Gif data from image. - - Warning:: May modify image data. - - :param im: Image object - :param palette: bytes object containing the source palette, or .... - :param info: encoderinfo - :returns: tuple of(list of header items, optimized palette) - - """ - if info is None: - info = {} - - used_palette_colors = _get_optimize(im, info) - - if "background" not in info and "background" in im.info: - info["background"] = im.info["background"] - - im_mod = _normalize_palette(im, palette, info) - im.palette = im_mod.palette - im.im = im_mod.im - header = _get_global_header(im, info) - - return header, used_palette_colors - - -def getdata( - im: Image.Image, offset: tuple[int, int] = (0, 0), **params: Any -) -> list[bytes]: - """ - Legacy Method - - Return a list of strings representing this image. - The first string is a local image header, the rest contains - encoded image data. - - To specify duration, add the time in milliseconds, - e.g. ``getdata(im_frame, duration=1000)`` - - :param im: Image object - :param offset: Tuple of (x, y) pixels. Defaults to (0, 0) - :param \\**params: e.g. duration or other encoder info parameters - :returns: List of bytes containing GIF encoded frame data - - """ - from io import BytesIO - - class Collector(BytesIO): - data = [] - - def write(self, data: Buffer) -> int: - self.data.append(data) - return len(data) - - im.load() # make sure raster data is available - - fp = Collector() - - _write_frame_data(fp, im, offset, params) - - return fp.data - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(GifImageFile.format, GifImageFile, _accept) -Image.register_save(GifImageFile.format, _save) -Image.register_save_all(GifImageFile.format, _save_all) -Image.register_extension(GifImageFile.format, ".gif") -Image.register_mime(GifImageFile.format, "image/gif") - -# -# Uncomment the following line if you wish to use NETPBM/PBMPLUS -# instead of the built-in "uncompressed" GIF encoder - -# Image.register_save(GifImageFile.format, _save_netpbm) diff --git a/.venv/lib/python3.12/site-packages/PIL/GimpGradientFile.py b/.venv/lib/python3.12/site-packages/PIL/GimpGradientFile.py deleted file mode 100644 index fb958721..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/GimpGradientFile.py +++ /dev/null @@ -1,154 +0,0 @@ -# -# Python Imaging Library -# $Id$ -# -# stuff to read (and render) GIMP gradient files -# -# History: -# 97-08-23 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# - -""" -Stuff to translate curve segments to palette values (derived from -the corresponding code in GIMP, written by Federico Mena Quintero. -See the GIMP distribution for more information.) -""" - -from __future__ import annotations - -from math import log, pi, sin, sqrt - -from ._binary import o8 - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from typing import IO - -EPSILON = 1e-10 -"""""" # Enable auto-doc for data member - - -def linear(middle: float, pos: float) -> float: - if pos <= middle: - if middle < EPSILON: - return 0.0 - else: - return 0.5 * pos / middle - else: - pos = pos - middle - middle = 1.0 - middle - if middle < EPSILON: - return 1.0 - else: - return 0.5 + 0.5 * pos / middle - - -def curved(middle: float, pos: float) -> float: - return pos ** (log(0.5) / log(max(middle, EPSILON))) - - -def sine(middle: float, pos: float) -> float: - return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0 - - -def sphere_increasing(middle: float, pos: float) -> float: - return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2) - - -def sphere_decreasing(middle: float, pos: float) -> float: - return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2) - - -SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] -"""""" # Enable auto-doc for data member - - -class GradientFile: - gradient: ( - list[ - tuple[ - float, - float, - float, - list[float], - list[float], - Callable[[float, float], float], - ] - ] - | None - ) = None - - def getpalette(self, entries: int = 256) -> tuple[bytes, str]: - assert self.gradient is not None - palette = [] - - ix = 0 - x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] - - for i in range(entries): - x = i / (entries - 1) - - while x1 < x: - ix += 1 - x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] - - w = x1 - x0 - - if w < EPSILON: - scale = segment(0.5, 0.5) - else: - scale = segment((xm - x0) / w, (x - x0) / w) - - # expand to RGBA - r = o8(int(255 * ((rgb1[0] - rgb0[0]) * scale + rgb0[0]) + 0.5)) - g = o8(int(255 * ((rgb1[1] - rgb0[1]) * scale + rgb0[1]) + 0.5)) - b = o8(int(255 * ((rgb1[2] - rgb0[2]) * scale + rgb0[2]) + 0.5)) - a = o8(int(255 * ((rgb1[3] - rgb0[3]) * scale + rgb0[3]) + 0.5)) - - # add to palette - palette.append(r + g + b + a) - - return b"".join(palette), "RGBA" - - -class GimpGradientFile(GradientFile): - """File handler for GIMP's gradient format.""" - - def __init__(self, fp: IO[bytes]) -> None: - if not fp.readline().startswith(b"GIMP Gradient"): - msg = "not a GIMP gradient file" - raise SyntaxError(msg) - - line = fp.readline() - - # GIMP 1.2 gradient files don't contain a name, but GIMP 1.3 files do - if line.startswith(b"Name: "): - line = fp.readline().strip() - - count = int(line) - - self.gradient = [] - - for i in range(count): - s = fp.readline().split() - w = [float(x) for x in s[:11]] - - x0, x1 = w[0], w[2] - xm = w[1] - rgb0 = w[3:7] - rgb1 = w[7:11] - - segment = SEGMENTS[int(s[11])] - cspace = int(s[12]) - - if cspace != 0: - msg = "cannot handle HSV colour space" - raise OSError(msg) - - self.gradient.append((x0, x1, xm, rgb0, rgb1, segment)) diff --git a/.venv/lib/python3.12/site-packages/PIL/GimpPaletteFile.py b/.venv/lib/python3.12/site-packages/PIL/GimpPaletteFile.py deleted file mode 100644 index 016257d3..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/GimpPaletteFile.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# Python Imaging Library -# $Id$ -# -# stuff to read GIMP palette files -# -# History: -# 1997-08-23 fl Created -# 2004-09-07 fl Support GIMP 2.0 palette files. -# -# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. -# Copyright (c) Fredrik Lundh 1997-2004. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re -from io import BytesIO - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import IO - - -class GimpPaletteFile: - """File handler for GIMP's palette format.""" - - rawmode = "RGB" - - def _read(self, fp: IO[bytes], limit: bool = True) -> None: - if not fp.readline().startswith(b"GIMP Palette"): - msg = "not a GIMP palette file" - raise SyntaxError(msg) - - palette: list[int] = [] - i = 0 - while True: - if limit and i == 256 + 3: - break - - i += 1 - s = fp.readline() - if not s: - break - - # skip fields and comment lines - if re.match(rb"\w+:|#", s): - continue - if limit and len(s) > 100: - msg = "bad palette file" - raise SyntaxError(msg) - - v = s.split(maxsplit=3) - if len(v) < 3: - msg = "bad palette entry" - raise ValueError(msg) - - palette += (int(v[i]) for i in range(3)) - if limit and len(palette) == 768: - break - - self.palette = bytes(palette) - - def __init__(self, fp: IO[bytes]) -> None: - self._read(fp) - - @classmethod - def frombytes(cls, data: bytes) -> GimpPaletteFile: - self = cls.__new__(cls) - self._read(BytesIO(data), False) - return self - - def getpalette(self) -> tuple[bytes, str]: - return self.palette, self.rawmode diff --git a/.venv/lib/python3.12/site-packages/PIL/GribStubImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/GribStubImagePlugin.py deleted file mode 100644 index 3784ef2f..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/GribStubImagePlugin.py +++ /dev/null @@ -1,72 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# GRIB stub adapter -# -# Copyright (c) 1996-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import IO - -from . import Image, ImageFile - -_handler = None - - -def register_handler(handler: ImageFile.StubHandler | None) -> None: - """ - Install application-specific GRIB image handler. - - :param handler: Handler object. - """ - global _handler - _handler = handler - - -# -------------------------------------------------------------------- -# Image adapter - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 8 and prefix.startswith(b"GRIB") and prefix[7] == 1 - - -class GribStubImageFile(ImageFile.StubImageFile): - format = "GRIB" - format_description = "GRIB" - - def _open(self) -> None: - assert self.fp is not None - if not _accept(self.fp.read(8)): - msg = "Not a GRIB file" - raise SyntaxError(msg) - - self.fp.seek(-8, os.SEEK_CUR) - - # make something up - self._mode = "F" - self._size = 1, 1 - - def _load(self) -> ImageFile.StubHandler | None: - return _handler - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if _handler is None or not hasattr(_handler, "save"): - msg = "GRIB save handler not installed" - raise OSError(msg) - _handler.save(im, fp, filename) - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(GribStubImageFile.format, GribStubImageFile, _accept) -Image.register_save(GribStubImageFile.format, _save) - -Image.register_extension(GribStubImageFile.format, ".grib") diff --git a/.venv/lib/python3.12/site-packages/PIL/Hdf5StubImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/Hdf5StubImagePlugin.py deleted file mode 100644 index 1a56660f..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/Hdf5StubImagePlugin.py +++ /dev/null @@ -1,72 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# HDF5 stub adapter -# -# Copyright (c) 2000-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import IO - -from . import Image, ImageFile - -_handler = None - - -def register_handler(handler: ImageFile.StubHandler | None) -> None: - """ - Install application-specific HDF5 image handler. - - :param handler: Handler object. - """ - global _handler - _handler = handler - - -# -------------------------------------------------------------------- -# Image adapter - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"\x89HDF\r\n\x1a\n") - - -class HDF5StubImageFile(ImageFile.StubImageFile): - format = "HDF5" - format_description = "HDF5" - - def _open(self) -> None: - assert self.fp is not None - if not _accept(self.fp.read(8)): - msg = "Not an HDF file" - raise SyntaxError(msg) - - self.fp.seek(-8, os.SEEK_CUR) - - # make something up - self._mode = "F" - self._size = 1, 1 - - def _load(self) -> ImageFile.StubHandler | None: - return _handler - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if _handler is None or not hasattr(_handler, "save"): - msg = "HDF5 save handler not installed" - raise OSError(msg) - _handler.save(im, fp, filename) - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept) -Image.register_save(HDF5StubImageFile.format, _save) - -Image.register_extensions(HDF5StubImageFile.format, [".h5", ".hdf"]) diff --git a/.venv/lib/python3.12/site-packages/PIL/IcnsImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/IcnsImagePlugin.py deleted file mode 100644 index cb7a74c2..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/IcnsImagePlugin.py +++ /dev/null @@ -1,401 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# macOS icns file decoder, based on icns.py by Bob Ippolito. -# -# history: -# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. -# 2020-04-04 Allow saving on all operating systems. -# -# Copyright (c) 2004 by Bob Ippolito. -# Copyright (c) 2004 by Secret Labs. -# Copyright (c) 2004 by Fredrik Lundh. -# Copyright (c) 2014 by Alastair Houghton. -# Copyright (c) 2020 by Pan Jing. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import struct -import sys -from typing import IO - -from . import Image, ImageFile, PngImagePlugin, features - -enable_jpeg2k = features.check_codec("jpg_2000") -if enable_jpeg2k: - from . import Jpeg2KImagePlugin - -MAGIC = b"icns" -HEADERSIZE = 8 - - -def nextheader(fobj: IO[bytes]) -> tuple[bytes, int]: - return struct.unpack(">4sI", fobj.read(HEADERSIZE)) - - -def read_32t( - fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] -) -> dict[str, Image.Image]: - # The 128x128 icon seems to have an extra header for some reason. - start, length = start_length - fobj.seek(start) - sig = fobj.read(4) - if sig != b"\x00\x00\x00\x00": - msg = "Unknown signature, expecting 0x00000000" - raise SyntaxError(msg) - return read_32(fobj, (start + 4, length - 4), size) - - -def read_32( - fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] -) -> dict[str, Image.Image]: - """ - Read a 32bit RGB icon resource. Seems to be either uncompressed or - an RLE packbits-like scheme. - """ - start, length = start_length - fobj.seek(start) - pixel_size = (size[0] * size[2], size[1] * size[2]) - sizesq = pixel_size[0] * pixel_size[1] - if length == sizesq * 3: - # uncompressed ("RGBRGBGB") - indata = fobj.read(length) - im = Image.frombuffer("RGB", pixel_size, indata, "raw", "RGB", 0, 1) - else: - # decode image - im = Image.new("RGB", pixel_size, None) - for band_ix in range(3): - data = [] - bytesleft = sizesq - while bytesleft > 0: - byte = fobj.read(1) - if not byte: - break - byte_int = byte[0] - if byte_int & 0x80: - blocksize = byte_int - 125 - byte = fobj.read(1) - data.extend([byte] * blocksize) - else: - blocksize = byte_int + 1 - data.append(fobj.read(blocksize)) - bytesleft -= blocksize - if bytesleft <= 0: - break - if bytesleft != 0: - msg = f"Error reading channel [{repr(bytesleft)} left]" - raise SyntaxError(msg) - band = Image.frombuffer("L", pixel_size, b"".join(data), "raw", "L", 0, 1) - im.im.putband(band.im, band_ix) - return {"RGB": im} - - -def read_mk( - fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] -) -> dict[str, Image.Image]: - # Alpha masks seem to be uncompressed - start = start_length[0] - fobj.seek(start) - pixel_size = (size[0] * size[2], size[1] * size[2]) - sizesq = pixel_size[0] * pixel_size[1] - band = Image.frombuffer("L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1) - return {"A": band} - - -def read_png_or_jpeg2000( - fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] -) -> dict[str, Image.Image]: - start, length = start_length - fobj.seek(start) - sig = fobj.read(12) - - im: Image.Image - if sig.startswith(b"\x89PNG\x0d\x0a\x1a\x0a"): - fobj.seek(start) - im = PngImagePlugin.PngImageFile(fobj) - Image._decompression_bomb_check(im.size) - return {"RGBA": im} - elif ( - sig.startswith((b"\xff\x4f\xff\x51", b"\x0d\x0a\x87\x0a")) - or sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" - ): - if not enable_jpeg2k: - msg = ( - "Unsupported icon subimage format (rebuild PIL " - "with JPEG 2000 support to fix this)" - ) - raise ValueError(msg) - # j2k, jpc or j2c - fobj.seek(start) - jp2kstream = fobj.read(length) - f = io.BytesIO(jp2kstream) - im = Jpeg2KImagePlugin.Jpeg2KImageFile(f) - Image._decompression_bomb_check(im.size) - if im.mode != "RGBA": - im = im.convert("RGBA") - return {"RGBA": im} - else: - msg = "Unsupported icon subimage format" - raise ValueError(msg) - - -class IcnsFile: - SIZES = { - (512, 512, 2): [(b"ic10", read_png_or_jpeg2000)], - (512, 512, 1): [(b"ic09", read_png_or_jpeg2000)], - (256, 256, 2): [(b"ic14", read_png_or_jpeg2000)], - (256, 256, 1): [(b"ic08", read_png_or_jpeg2000)], - (128, 128, 2): [(b"ic13", read_png_or_jpeg2000)], - (128, 128, 1): [ - (b"ic07", read_png_or_jpeg2000), - (b"it32", read_32t), - (b"t8mk", read_mk), - ], - (64, 64, 1): [(b"icp6", read_png_or_jpeg2000)], - (32, 32, 2): [(b"ic12", read_png_or_jpeg2000)], - (48, 48, 1): [(b"ih32", read_32), (b"h8mk", read_mk)], - (32, 32, 1): [ - (b"icp5", read_png_or_jpeg2000), - (b"il32", read_32), - (b"l8mk", read_mk), - ], - (16, 16, 2): [(b"ic11", read_png_or_jpeg2000)], - (16, 16, 1): [ - (b"icp4", read_png_or_jpeg2000), - (b"is32", read_32), - (b"s8mk", read_mk), - ], - } - - def __init__(self, fobj: IO[bytes]) -> None: - """ - fobj is a file-like object as an icns resource - """ - # signature : (start, length) - self.dct = {} - self.fobj = fobj - sig, filesize = nextheader(fobj) - if not _accept(sig): - msg = "not an icns file" - raise SyntaxError(msg) - i = HEADERSIZE - while i < filesize: - sig, blocksize = nextheader(fobj) - if blocksize <= 0: - msg = "invalid block header" - raise SyntaxError(msg) - i += HEADERSIZE - blocksize -= HEADERSIZE - self.dct[sig] = (i, blocksize) - fobj.seek(blocksize, io.SEEK_CUR) - i += blocksize - - def itersizes(self) -> list[tuple[int, int, int]]: - sizes = [] - for size, fmts in self.SIZES.items(): - for fmt, reader in fmts: - if fmt in self.dct: - sizes.append(size) - break - return sizes - - def bestsize(self) -> tuple[int, int, int]: - sizes = self.itersizes() - if not sizes: - msg = "No 32bit icon resources found" - raise SyntaxError(msg) - return max(sizes) - - def dataforsize(self, size: tuple[int, int, int]) -> dict[str, Image.Image]: - """ - Get an icon resource as {channel: array}. Note that - the arrays are bottom-up like windows bitmaps and will likely - need to be flipped or transposed in some way. - """ - dct = {} - for code, reader in self.SIZES[size]: - desc = self.dct.get(code) - if desc is not None: - dct.update(reader(self.fobj, desc, size)) - return dct - - def getimage( - self, size: tuple[int, int] | tuple[int, int, int] | None = None - ) -> Image.Image: - if size is None: - size = self.bestsize() - elif len(size) == 2: - size = (size[0], size[1], 1) - channels = self.dataforsize(size) - - im = channels.get("RGBA") - if im: - return im - - im = channels["RGB"].copy() - try: - im.putalpha(channels["A"]) - except KeyError: - pass - return im - - -## -# Image plugin for Mac OS icons. - - -class IcnsImageFile(ImageFile.ImageFile): - """ - PIL image support for Mac OS .icns files. - Chooses the best resolution, but will possibly load - a different size image if you mutate the size attribute - before calling 'load'. - - The info dictionary has a key 'sizes' that is a list - of sizes that the icns file has. - """ - - format = "ICNS" - format_description = "Mac OS icns resource" - - def _open(self) -> None: - assert self.fp is not None - self.icns = IcnsFile(self.fp) - self._mode = "RGBA" - self.info["sizes"] = self.icns.itersizes() - self.best_size = self.icns.bestsize() - self.size = ( - self.best_size[0] * self.best_size[2], - self.best_size[1] * self.best_size[2], - ) - - @property - def size(self) -> tuple[int, int]: - return self._size - - @size.setter - def size(self, value: tuple[int, int]) -> None: - # Check that a matching size exists, - # or that there is a scale that would create a size that matches - for size in self.info["sizes"]: - simple_size = size[0] * size[2], size[1] * size[2] - scale = simple_size[0] // value[0] - if simple_size[1] / value[1] == scale: - self._size = value - return - msg = "This is not one of the allowed sizes of this image" - raise ValueError(msg) - - def load(self, scale: int | None = None) -> Image.core.PixelAccess | None: - if scale is not None: - width, height = self.size[:2] - self.size = width * scale, height * scale - self.best_size = width, height, scale - - px = Image.Image.load(self) - if self._im is not None and self.im.size == self.size: - # Already loaded - return px - self.load_prepare() - # This is likely NOT the best way to do it, but whatever. - im = self.icns.getimage(self.best_size) - - # If this is a PNG or JPEG 2000, it won't be loaded yet - px = im.load() - - self.im = im.im - self._mode = im.mode - self.size = im.size - - return px - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - """ - Saves the image as a series of PNG files, - that are then combined into a .icns file. - """ - if hasattr(fp, "flush"): - fp.flush() - - sizes = { - b"ic07": 128, - b"ic08": 256, - b"ic09": 512, - b"ic10": 1024, - b"ic11": 32, - b"ic12": 64, - b"ic13": 256, - b"ic14": 512, - } - provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} - size_streams = {} - for size in set(sizes.values()): - image = ( - provided_images[size] - if size in provided_images - else im.resize((size, size)) - ) - - temp = io.BytesIO() - image.save(temp, "png") - size_streams[size] = temp.getvalue() - - entries = [] - for type, size in sizes.items(): - stream = size_streams[size] - entries.append((type, HEADERSIZE + len(stream), stream)) - - # Header - fp.write(MAGIC) - file_length = HEADERSIZE # Header - file_length += HEADERSIZE + 8 * len(entries) # TOC - file_length += sum(entry[1] for entry in entries) - fp.write(struct.pack(">i", file_length)) - - # TOC - fp.write(b"TOC ") - fp.write(struct.pack(">i", HEADERSIZE + len(entries) * HEADERSIZE)) - for entry in entries: - fp.write(entry[0]) - fp.write(struct.pack(">i", entry[1])) - - # Data - for entry in entries: - fp.write(entry[0]) - fp.write(struct.pack(">i", entry[1])) - fp.write(entry[2]) - - if hasattr(fp, "flush"): - fp.flush() - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(MAGIC) - - -Image.register_open(IcnsImageFile.format, IcnsImageFile, _accept) -Image.register_extension(IcnsImageFile.format, ".icns") - -Image.register_save(IcnsImageFile.format, _save) -Image.register_mime(IcnsImageFile.format, "image/icns") - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Syntax: python3 IcnsImagePlugin.py [file]") - sys.exit() - - with open(sys.argv[1], "rb") as fp: - imf = IcnsImageFile(fp) - for size in imf.info["sizes"]: - width, height, scale = imf.size = size - imf.save(f"out-{width}-{height}-{scale}.png") - with Image.open(sys.argv[1]) as im: - im.save("out.png") - if sys.platform == "windows": - os.startfile("out.png") diff --git a/.venv/lib/python3.12/site-packages/PIL/IcoImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/IcoImagePlugin.py deleted file mode 100644 index 8dd57ff8..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/IcoImagePlugin.py +++ /dev/null @@ -1,396 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Windows Icon support for PIL -# -# History: -# 96-05-27 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# - -# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis -# . -# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki -# -# Copyright 2008 Bryan 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 -# -# https://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. - -# Icon format references: -# * https://en.wikipedia.org/wiki/ICO_(file_format) -# * https://msdn.microsoft.com/en-us/library/ms997538.aspx -from __future__ import annotations - -import warnings -from io import BytesIO -from math import ceil, log -from typing import IO, NamedTuple - -from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin -from ._binary import i16le as i16 -from ._binary import i32le as i32 -from ._binary import o8 -from ._binary import o16le as o16 -from ._binary import o32le as o32 - -# -# -------------------------------------------------------------------- - -_MAGIC = b"\0\0\1\0" - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - fp.write(_MAGIC) # (2+2) - bmp = im.encoderinfo.get("bitmap_format") == "bmp" - sizes = im.encoderinfo.get( - "sizes", - [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)], - ) - frames = [] - provided_ims = [im] + im.encoderinfo.get("append_images", []) - width, height = im.size - for size in sorted(set(sizes)): - if size[0] > width or size[1] > height or size[0] > 256 or size[1] > 256: - continue - - for provided_im in provided_ims: - if provided_im.size != size: - continue - frames.append(provided_im) - if bmp: - bits = BmpImagePlugin.SAVE[provided_im.mode][1] - bits_used = [bits] - for other_im in provided_ims: - if other_im.size != size: - continue - bits = BmpImagePlugin.SAVE[other_im.mode][1] - if bits not in bits_used: - # Another image has been supplied for this size - # with a different bit depth - frames.append(other_im) - bits_used.append(bits) - break - else: - # TODO: invent a more convenient method for proportional scalings - frame = provided_im.copy() - frame.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None) - frames.append(frame) - fp.write(o16(len(frames))) # idCount(2) - offset = fp.tell() + len(frames) * 16 - for frame in frames: - width, height = frame.size - # 0 means 256 - fp.write(o8(width if width < 256 else 0)) # bWidth(1) - fp.write(o8(height if height < 256 else 0)) # bHeight(1) - - bits, colors = BmpImagePlugin.SAVE[frame.mode][1:] if bmp else (32, 0) - fp.write(o8(colors)) # bColorCount(1) - fp.write(b"\0") # bReserved(1) - fp.write(b"\0\0") # wPlanes(2) - fp.write(o16(bits)) # wBitCount(2) - - image_io = BytesIO() - if bmp: - frame.save(image_io, "dib") - - if bits != 32: - and_mask = Image.new("1", size) - ImageFile._save( - and_mask, - image_io, - [ImageFile._Tile("raw", (0, 0) + size, 0, ("1", 0, -1))], - ) - else: - frame.save(image_io, "png") - image_io.seek(0) - image_bytes = image_io.read() - if bmp: - image_bytes = image_bytes[:8] + o32(height * 2) + image_bytes[12:] - bytes_len = len(image_bytes) - fp.write(o32(bytes_len)) # dwBytesInRes(4) - fp.write(o32(offset)) # dwImageOffset(4) - current = fp.tell() - fp.seek(offset) - fp.write(image_bytes) - offset = offset + bytes_len - fp.seek(current) - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(_MAGIC) - - -class IconHeader(NamedTuple): - width: int - height: int - nb_color: int - reserved: int - planes: int - bpp: int - size: int - offset: int - dim: tuple[int, int] - square: int - color_depth: int - - -class IcoFile: - def __init__(self, buf: IO[bytes]) -> None: - """ - Parse image from file-like object containing ico file data - """ - - # check magic - s = buf.read(6) - if not _accept(s): - msg = "not an ICO file" - raise SyntaxError(msg) - - self.buf = buf - self.entry = [] - - # Number of items in file - self.nb_items = i16(s, 4) - - # Get headers for each item - for i in range(self.nb_items): - s = buf.read(16) - - # See Wikipedia - width = s[0] or 256 - height = s[1] or 256 - - # No. of colors in image (0 if >=8bpp) - nb_color = s[2] - bpp = i16(s, 6) - icon_header = IconHeader( - width=width, - height=height, - nb_color=nb_color, - reserved=s[3], - planes=i16(s, 4), - bpp=i16(s, 6), - size=i32(s, 8), - offset=i32(s, 12), - dim=(width, height), - square=width * height, - # See Wikipedia notes about color depth. - # We need this just to differ images with equal sizes - color_depth=bpp or (nb_color != 0 and ceil(log(nb_color, 2))) or 256, - ) - - self.entry.append(icon_header) - - self.entry = sorted(self.entry, key=lambda x: x.color_depth) - # ICO images are usually squares - self.entry = sorted(self.entry, key=lambda x: x.square, reverse=True) - - def sizes(self) -> set[tuple[int, int]]: - """ - Get a set of all available icon sizes and color depths. - """ - return {(h.width, h.height) for h in self.entry} - - def getentryindex(self, size: tuple[int, int], bpp: int | bool = False) -> int: - for i, h in enumerate(self.entry): - if size == h.dim and (bpp is False or bpp == h.color_depth): - return i - return 0 - - def getimage(self, size: tuple[int, int], bpp: int | bool = False) -> Image.Image: - """ - Get an image from the icon - """ - return self.frame(self.getentryindex(size, bpp)) - - def frame(self, idx: int) -> Image.Image: - """ - Get an image from frame idx - """ - - header = self.entry[idx] - - self.buf.seek(header.offset) - data = self.buf.read(8) - self.buf.seek(header.offset) - - im: Image.Image - if data[:8] == PngImagePlugin._MAGIC: - # png frame - im = PngImagePlugin.PngImageFile(self.buf) - Image._decompression_bomb_check(im.size) - else: - # XOR + AND mask bmp frame - im = BmpImagePlugin.DibImageFile(self.buf) - Image._decompression_bomb_check(im.size) - - # change tile dimension to only encompass XOR image - im._size = (im.size[0], int(im.size[1] / 2)) - d, e, o, a = im.tile[0] - im.tile[0] = ImageFile._Tile(d, (0, 0) + im.size, o, a) - - # figure out where AND mask image starts - if header.bpp == 32: - # 32-bit color depth icon image allows semitransparent areas - # PIL's DIB format ignores transparency bits, recover them. - # The DIB is packed in BGRX byte order where X is the alpha - # channel. - - # Back up to start of bmp data - self.buf.seek(o) - # extract every 4th byte (eg. 3,7,11,15,...) - alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] - - # convert to an 8bpp grayscale image - try: - mask = Image.frombuffer( - "L", # 8bpp - im.size, # (w, h) - alpha_bytes, # source chars - "raw", # raw decoder - ("L", 0, -1), # 8bpp inverted, unpadded, reversed - ) - except ValueError: - if ImageFile.LOAD_TRUNCATED_IMAGES: - mask = None - else: - raise - else: - # get AND image from end of bitmap - w = im.size[0] - if (w % 32) > 0: - # bitmap row data is aligned to word boundaries - w += 32 - (im.size[0] % 32) - - # the total mask data is - # padded row size * height / bits per char - - total_bytes = int((w * im.size[1]) / 8) - and_mask_offset = header.offset + header.size - total_bytes - - self.buf.seek(and_mask_offset) - mask_data = self.buf.read(total_bytes) - - # convert raw data to image - try: - mask = Image.frombuffer( - "1", # 1 bpp - im.size, # (w, h) - mask_data, # source chars - "raw", # raw decoder - ("1;I", int(w / 8), -1), # 1bpp inverted, padded, reversed - ) - except ValueError: - if ImageFile.LOAD_TRUNCATED_IMAGES: - mask = None - else: - raise - - # now we have two images, im is XOR image and mask is AND image - - # apply mask image as alpha channel - if mask: - im = im.convert("RGBA") - im.putalpha(mask) - - return im - - -## -# Image plugin for Windows Icon files. - - -class IcoImageFile(ImageFile.ImageFile): - """ - PIL read-only image support for Microsoft Windows .ico files. - - By default the largest resolution image in the file will be loaded. This - can be changed by altering the 'size' attribute before calling 'load'. - - The info dictionary has a key 'sizes' that is a list of the sizes available - in the icon file. - - Handles classic, XP and Vista icon formats. - - When saving, PNG compression is used. Support for this was only added in - Windows Vista. If you are unable to view the icon in Windows, convert the - image to "RGBA" mode before saving. - - This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis - . - https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki - """ - - format = "ICO" - format_description = "Windows Icon" - - def _open(self) -> None: - assert self.fp is not None - self.ico = IcoFile(self.fp) - self.info["sizes"] = self.ico.sizes() - self.size = self.ico.entry[0].dim - self.load() - - @property - def size(self) -> tuple[int, int]: - return self._size - - @size.setter - def size(self, value: tuple[int, int]) -> None: - if value not in self.info["sizes"]: - msg = "This is not one of the allowed sizes of this image" - raise ValueError(msg) - self._size = value - - def load(self) -> Image.core.PixelAccess | None: - if self._im is not None and self.im.size == self.size: - # Already loaded - return Image.Image.load(self) - im = self.ico.getimage(self.size) - # if tile is PNG, it won't really be loaded yet - im.load() - self.im = im.im - self._mode = im.mode - if im.palette: - self.palette = im.palette - if im.size != self.size: - warnings.warn("Image was not the expected size") - - index = self.ico.getentryindex(self.size) - sizes = list(self.info["sizes"]) - sizes[index] = im.size - self.info["sizes"] = set(sizes) - - self.size = im.size - return Image.Image.load(self) - - def load_seek(self, pos: int) -> None: - # Flag the ImageFile.Parser so that it - # just does all the decode at the end. - pass - - -# -# -------------------------------------------------------------------- - - -Image.register_open(IcoImageFile.format, IcoImageFile, _accept) -Image.register_save(IcoImageFile.format, _save) -Image.register_extension(IcoImageFile.format, ".ico") - -Image.register_mime(IcoImageFile.format, "image/x-icon") diff --git a/.venv/lib/python3.12/site-packages/PIL/ImImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/ImImagePlugin.py deleted file mode 100644 index ef54f16e..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImImagePlugin.py +++ /dev/null @@ -1,390 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# IFUNC IM file handling for PIL -# -# history: -# 1995-09-01 fl Created. -# 1997-01-03 fl Save palette images -# 1997-01-08 fl Added sequence support -# 1997-01-23 fl Added P and RGB save support -# 1997-05-31 fl Read floating point images -# 1997-06-22 fl Save floating point images -# 1997-08-27 fl Read and save 1-bit images -# 1998-06-25 fl Added support for RGB+LUT images -# 1998-07-02 fl Added support for YCC images -# 1998-07-15 fl Renamed offset attribute to avoid name clash -# 1998-12-29 fl Added I;16 support -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) -# 2003-09-26 fl Added LA/PA support -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2001 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -import re -from typing import IO, Any - -from . import Image, ImageFile, ImagePalette -from ._util import DeferredError - -# -------------------------------------------------------------------- -# Standard tags - -COMMENT = "Comment" -DATE = "Date" -EQUIPMENT = "Digitalization equipment" -FRAMES = "File size (no of images)" -LUT = "Lut" -NAME = "Name" -SCALE = "Scale (x,y)" -SIZE = "Image size (x*y)" -MODE = "Image type" - -TAGS = { - COMMENT: 0, - DATE: 0, - EQUIPMENT: 0, - FRAMES: 0, - LUT: 0, - NAME: 0, - SCALE: 0, - SIZE: 0, - MODE: 0, -} - -OPEN = { - # ifunc93/p3cfunc formats - "0 1 image": ("1", "1"), - "L 1 image": ("1", "1"), - "Greyscale image": ("L", "L"), - "Grayscale image": ("L", "L"), - "RGB image": ("RGB", "RGB;L"), - "RLB image": ("RGB", "RLB"), - "RYB image": ("RGB", "RLB"), - "B1 image": ("1", "1"), - "B2 image": ("P", "P;2"), - "B4 image": ("P", "P;4"), - "X 24 image": ("RGB", "RGB"), - "L 32 S image": ("I", "I;32"), - "L 32 F image": ("F", "F;32"), - # old p3cfunc formats - "RGB3 image": ("RGB", "RGB;T"), - "RYB3 image": ("RGB", "RYB;T"), - # extensions - "LA image": ("LA", "LA;L"), - "PA image": ("LA", "PA;L"), - "RGBA image": ("RGBA", "RGBA;L"), - "RGBX image": ("RGB", "RGBX;L"), - "CMYK image": ("CMYK", "CMYK;L"), - "YCC image": ("YCbCr", "YCbCr;L"), -} - -# ifunc95 extensions -for i in ["8", "8S", "16", "16S", "32", "32F"]: - OPEN[f"L {i} image"] = ("F", f"F;{i}") - OPEN[f"L*{i} image"] = ("F", f"F;{i}") -for i in ["16", "16L", "16B"]: - OPEN[f"L {i} image"] = (f"I;{i}", f"I;{i}") - OPEN[f"L*{i} image"] = (f"I;{i}", f"I;{i}") -for i in ["32S"]: - OPEN[f"L {i} image"] = ("I", f"I;{i}") - OPEN[f"L*{i} image"] = ("I", f"I;{i}") -for j in range(2, 33): - OPEN[f"L*{j} image"] = ("F", f"F;{j}") - - -# -------------------------------------------------------------------- -# Read IM directory - -split = re.compile(rb"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") - - -def number(s: Any) -> float: - try: - return int(s) - except ValueError: - return float(s) - - -## -# Image plugin for the IFUNC IM file format. - - -class ImImageFile(ImageFile.ImageFile): - format = "IM" - format_description = "IFUNC Image Memory" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - # Quick rejection: if there's not an LF among the first - # 100 bytes, this is (probably) not a text header. - - assert self.fp is not None - if b"\n" not in self.fp.read(100): - msg = "not an IM file" - raise SyntaxError(msg) - self.fp.seek(0) - - n = 0 - - # Default values - self.info[MODE] = "L" - self.info[SIZE] = (512, 512) - self.info[FRAMES] = 1 - - self.rawmode = "L" - - while True: - s = self.fp.read(1) - - # Some versions of IFUNC uses \n\r instead of \r\n... - if s == b"\r": - continue - - if not s or s == b"\0" or s == b"\x1a": - break - - # FIXME: this may read whole file if not a text file - s = s + self.fp.readline() - - if len(s) > 100: - msg = "not an IM file" - raise SyntaxError(msg) - - if s.endswith(b"\r\n"): - s = s[:-2] - elif s.endswith(b"\n"): - s = s[:-1] - - try: - m = split.match(s) - except re.error as e: - msg = "not an IM file" - raise SyntaxError(msg) from e - - if m: - k, v = m.group(1, 2) - - # Don't know if this is the correct encoding, - # but a decent guess (I guess) - k = k.decode("latin-1", "replace") - v = v.decode("latin-1", "replace") - - # Convert value as appropriate - if k in [FRAMES, SCALE, SIZE]: - v = v.replace("*", ",") - v = tuple(map(number, v.split(","))) - if len(v) == 1: - v = v[0] - elif k == MODE and v in OPEN: - v, self.rawmode = OPEN[v] - - # Add to dictionary. Note that COMMENT tags are - # combined into a list of strings. - if k == COMMENT: - if k in self.info: - self.info[k].append(v) - else: - self.info[k] = [v] - else: - self.info[k] = v - - if k in TAGS: - n += 1 - - else: - msg = f"Syntax error in IM header: {s.decode('ascii', 'replace')}" - raise SyntaxError(msg) - - if not n: - msg = "Not an IM file" - raise SyntaxError(msg) - - # Basic attributes - self._size = self.info[SIZE] - self._mode = self.info[MODE] - - # Skip forward to start of image data - while s and not s.startswith(b"\x1a"): - s = self.fp.read(1) - if not s: - msg = "File truncated" - raise SyntaxError(msg) - - if LUT in self.info: - # convert lookup table to palette or lut attribute - palette = self.fp.read(768) - greyscale = 1 # greyscale palette - linear = 1 # linear greyscale palette - for i in range(256): - if palette[i] == palette[i + 256] == palette[i + 512]: - if palette[i] != i: - linear = 0 - else: - greyscale = 0 - if self.mode in ["L", "LA", "P", "PA"]: - if greyscale: - if not linear: - self.lut = list(palette[:256]) - else: - if self.mode in ["L", "P"]: - self._mode = self.rawmode = "P" - elif self.mode in ["LA", "PA"]: - self._mode = "PA" - self.rawmode = "PA;L" - self.palette = ImagePalette.raw("RGB;L", palette) - elif self.mode == "RGB": - if not greyscale or not linear: - self.lut = list(palette) - - self.frame = 0 - - self.__offset = offs = self.fp.tell() - - self._fp = self.fp # FIXME: hack - - if self.rawmode.startswith("F;"): - # ifunc95 formats - try: - # use bit decoder (if necessary) - bits = int(self.rawmode[2:]) - if bits not in [8, 16, 32]: - self.tile = [ - ImageFile._Tile( - "bit", (0, 0) + self.size, offs, (bits, 8, 3, 0, -1) - ) - ] - return - except ValueError: - pass - - if self.rawmode in ["RGB;T", "RYB;T"]: - # Old LabEye/3PC files. Would be very surprised if anyone - # ever stumbled upon such a file ;-) - size = self.size[0] * self.size[1] - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, offs, ("G", 0, -1)), - ImageFile._Tile("raw", (0, 0) + self.size, offs + size, ("R", 0, -1)), - ImageFile._Tile( - "raw", (0, 0) + self.size, offs + 2 * size, ("B", 0, -1) - ), - ] - else: - # LabEye/IFUNC files - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1)) - ] - - @property - def n_frames(self) -> int: - return self.info[FRAMES] - - @property - def is_animated(self) -> bool: - return self.info[FRAMES] > 1 - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if isinstance(self._fp, DeferredError): - raise self._fp.ex - - self.frame = frame - - if self.mode == "1": - bits = 1 - else: - bits = 8 * len(self.mode) - - size = ((self.size[0] * bits + 7) // 8) * self.size[1] - offs = self.__offset + frame * size - - self.fp = self._fp - - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1)) - ] - - def tell(self) -> int: - return self.frame - - -# -# -------------------------------------------------------------------- -# Save IM files - - -SAVE = { - # mode: (im type, raw mode) - "1": ("0 1", "1"), - "L": ("Greyscale", "L"), - "LA": ("LA", "LA;L"), - "P": ("Greyscale", "P"), - "PA": ("LA", "PA;L"), - "I": ("L 32S", "I;32S"), - "I;16": ("L 16", "I;16"), - "I;16L": ("L 16L", "I;16L"), - "I;16B": ("L 16B", "I;16B"), - "F": ("L 32F", "F;32F"), - "RGB": ("RGB", "RGB;L"), - "RGBA": ("RGBA", "RGBA;L"), - "RGBX": ("RGBX", "RGBX;L"), - "CMYK": ("CMYK", "CMYK;L"), - "YCbCr": ("YCC", "YCbCr;L"), -} - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - try: - image_type, rawmode = SAVE[im.mode] - except KeyError as e: - msg = f"Cannot save {im.mode} images as IM" - raise ValueError(msg) from e - - frames = im.encoderinfo.get("frames", 1) - - fp.write(f"Image type: {image_type} image\r\n".encode("ascii")) - if filename: - # Each line must be 100 characters or less, - # or: SyntaxError("not an IM file") - # 8 characters are used for "Name: " and "\r\n" - # Keep just the filename, ditch the potentially overlong path - if isinstance(filename, bytes): - filename = filename.decode("ascii") - name, ext = os.path.splitext(os.path.basename(filename)) - name = "".join([name[: 92 - len(ext)], ext]) - - fp.write(f"Name: {name}\r\n".encode("ascii")) - fp.write(f"Image size (x*y): {im.size[0]}*{im.size[1]}\r\n".encode("ascii")) - fp.write(f"File size (no of images): {frames}\r\n".encode("ascii")) - if im.mode in ["P", "PA"]: - fp.write(b"Lut: 1\r\n") - fp.write(b"\000" * (511 - fp.tell()) + b"\032") - if im.mode in ["P", "PA"]: - im_palette = im.im.getpalette("RGB", "RGB;L") - colors = len(im_palette) // 3 - palette = b"" - for i in range(3): - palette += im_palette[colors * i : colors * (i + 1)] - palette += b"\x00" * (256 - colors) - fp.write(palette) # 768 bytes - ImageFile._save( - im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, -1))] - ) - - -# -# -------------------------------------------------------------------- -# Registry - - -Image.register_open(ImImageFile.format, ImImageFile) -Image.register_save(ImImageFile.format, _save) - -Image.register_extension(ImImageFile.format, ".im") diff --git a/.venv/lib/python3.12/site-packages/PIL/Image.py b/.venv/lib/python3.12/site-packages/PIL/Image.py deleted file mode 100644 index 57498077..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/Image.py +++ /dev/null @@ -1,4381 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# the Image class wrapper -# -# partial release history: -# 1995-09-09 fl Created -# 1996-03-11 fl PIL release 0.0 (proof of concept) -# 1996-04-30 fl PIL release 0.1b1 -# 1999-07-28 fl PIL release 1.0 final -# 2000-06-07 fl PIL release 1.1 -# 2000-10-20 fl PIL release 1.1.1 -# 2001-05-07 fl PIL release 1.1.2 -# 2002-03-15 fl PIL release 1.1.3 -# 2003-05-10 fl PIL release 1.1.4 -# 2005-03-28 fl PIL release 1.1.5 -# 2006-12-02 fl PIL release 1.1.6 -# 2009-11-15 fl PIL release 1.1.7 -# -# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. -# Copyright (c) 1995-2009 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -from __future__ import annotations - -import abc -import atexit -import builtins -import io -import logging -import math -import os -import re -import struct -import sys -import tempfile -import warnings -from collections.abc import MutableMapping -from enum import IntEnum -from typing import IO, Protocol, cast - -# VERSION was removed in Pillow 6.0.0. -# PILLOW_VERSION was removed in Pillow 9.0.0. -# Use __version__ instead. -from . import ( - ExifTags, - ImageMode, - TiffTags, - UnidentifiedImageError, - __version__, - _plugins, -) -from ._binary import i32le, o32be, o32le -from ._deprecate import deprecate -from ._util import DeferredError, is_path - -ElementTree: ModuleType | None -try: - from defusedxml import ElementTree -except ImportError: - ElementTree = None - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable, Iterator, Sequence - from types import ModuleType - from typing import Any, Literal - -logger = logging.getLogger(__name__) - - -class DecompressionBombWarning(RuntimeWarning): - pass - - -class DecompressionBombError(Exception): - pass - - -WARN_POSSIBLE_FORMATS: bool = False - -# Limit to around a quarter gigabyte for a 24-bit (3 bpp) image -MAX_IMAGE_PIXELS: int | None = int(1024 * 1024 * 1024 // 4 // 3) - - -try: - # If the _imaging C module is not present, Pillow will not load. - # Note that other modules should not refer to _imaging directly; - # import Image and use the Image.core variable instead. - # Also note that Image.core is not a publicly documented interface, - # and should be considered private and subject to change. - from . import _imaging as core - - if __version__ != getattr(core, "PILLOW_VERSION", None): - msg = ( - "The _imaging extension was built for another version of Pillow or PIL:\n" - f"Core version: {getattr(core, 'PILLOW_VERSION', None)}\n" - f"Pillow version: {__version__}" - ) - raise ImportError(msg) - -except ImportError as v: - # Explanations for ways that we know we might have an import error - if str(v).startswith("Module use of python"): - # The _imaging C module is present, but not compiled for - # the right version (windows only). Print a warning, if - # possible. - warnings.warn( - "The _imaging extension was built for another version of Python.", - RuntimeWarning, - ) - elif str(v).startswith("The _imaging extension"): - warnings.warn(str(v), RuntimeWarning) - # Fail here anyway. Don't let people run with a mostly broken Pillow. - # see docs/porting.rst - raise - - -# -# Constants - - -# transpose -class Transpose(IntEnum): - FLIP_LEFT_RIGHT = 0 - FLIP_TOP_BOTTOM = 1 - ROTATE_90 = 2 - ROTATE_180 = 3 - ROTATE_270 = 4 - TRANSPOSE = 5 - TRANSVERSE = 6 - - -# transforms (also defined in Imaging.h) -class Transform(IntEnum): - AFFINE = 0 - EXTENT = 1 - PERSPECTIVE = 2 - QUAD = 3 - MESH = 4 - - -# resampling filters (also defined in Imaging.h) -class Resampling(IntEnum): - NEAREST = 0 - BOX = 4 - BILINEAR = 2 - HAMMING = 5 - BICUBIC = 3 - LANCZOS = 1 - - -_filters_support = { - Resampling.BOX: 0.5, - Resampling.BILINEAR: 1.0, - Resampling.HAMMING: 1.0, - Resampling.BICUBIC: 2.0, - Resampling.LANCZOS: 3.0, -} - - -# dithers -class Dither(IntEnum): - NONE = 0 - ORDERED = 1 # Not yet implemented - RASTERIZE = 2 # Not yet implemented - FLOYDSTEINBERG = 3 # default - - -# palettes/quantizers -class Palette(IntEnum): - WEB = 0 - ADAPTIVE = 1 - - -class Quantize(IntEnum): - MEDIANCUT = 0 - MAXCOVERAGE = 1 - FASTOCTREE = 2 - LIBIMAGEQUANT = 3 - - -module = sys.modules[__name__] -for enum in (Transpose, Transform, Resampling, Dither, Palette, Quantize): - for item in enum: - setattr(module, item.name, item.value) - - -if hasattr(core, "DEFAULT_STRATEGY"): - DEFAULT_STRATEGY = core.DEFAULT_STRATEGY - FILTERED = core.FILTERED - HUFFMAN_ONLY = core.HUFFMAN_ONLY - RLE = core.RLE - FIXED = core.FIXED - - -# -------------------------------------------------------------------- -# Registries - -TYPE_CHECKING = False -if TYPE_CHECKING: - import mmap - from xml.etree.ElementTree import Element - - from IPython.lib.pretty import PrettyPrinter - - from . import ImageFile, ImageFilter, ImagePalette, ImageQt, TiffImagePlugin - from ._typing import CapsuleType, NumpyArray, StrOrBytesPath -ID: list[str] = [] -OPEN: dict[ - str, - tuple[ - Callable[[IO[bytes], str | bytes], ImageFile.ImageFile], - Callable[[bytes], bool | str] | None, - ], -] = {} -MIME: dict[str, str] = {} -SAVE: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {} -SAVE_ALL: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {} -EXTENSION: dict[str, str] = {} -DECODERS: dict[str, type[ImageFile.PyDecoder]] = {} -ENCODERS: dict[str, type[ImageFile.PyEncoder]] = {} - -# -------------------------------------------------------------------- -# Modes - -_ENDIAN = "<" if sys.byteorder == "little" else ">" - - -def _conv_type_shape(im: Image) -> tuple[tuple[int, ...], str]: - m = ImageMode.getmode(im.mode) - shape: tuple[int, ...] = (im.height, im.width) - extra = len(m.bands) - if extra != 1: - shape += (extra,) - return shape, m.typestr - - -MODES = [ - "1", - "CMYK", - "F", - "HSV", - "I", - "I;16", - "I;16B", - "I;16L", - "I;16N", - "L", - "LA", - "La", - "LAB", - "P", - "PA", - "RGB", - "RGBA", - "RGBa", - "RGBX", - "YCbCr", -] - -# raw modes that may be memory mapped. NOTE: if you change this, you -# may have to modify the stride calculation in map.c too! -_MAPMODES = ("L", "P", "RGBX", "RGBA", "CMYK", "I;16", "I;16L", "I;16B") - - -def getmodebase(mode: str) -> str: - """ - Gets the "base" mode for given mode. This function returns "L" for - images that contain grayscale data, and "RGB" for images that - contain color data. - - :param mode: Input mode. - :returns: "L" or "RGB". - :exception KeyError: If the input mode was not a standard mode. - """ - return ImageMode.getmode(mode).basemode - - -def getmodetype(mode: str) -> str: - """ - Gets the storage type mode. Given a mode, this function returns a - single-layer mode suitable for storing individual bands. - - :param mode: Input mode. - :returns: "L", "I", or "F". - :exception KeyError: If the input mode was not a standard mode. - """ - return ImageMode.getmode(mode).basetype - - -def getmodebandnames(mode: str) -> tuple[str, ...]: - """ - Gets a list of individual band names. Given a mode, this function returns - a tuple containing the names of individual bands (use - :py:method:`~PIL.Image.getmodetype` to get the mode used to store each - individual band. - - :param mode: Input mode. - :returns: A tuple containing band names. The length of the tuple - gives the number of bands in an image of the given mode. - :exception KeyError: If the input mode was not a standard mode. - """ - return ImageMode.getmode(mode).bands - - -def getmodebands(mode: str) -> int: - """ - Gets the number of individual bands for this mode. - - :param mode: Input mode. - :returns: The number of bands in this mode. - :exception KeyError: If the input mode was not a standard mode. - """ - return len(ImageMode.getmode(mode).bands) - - -# -------------------------------------------------------------------- -# Helpers - -_initialized = 0 - -# Mapping from file extension to plugin module name for lazy importing -_EXTENSION_PLUGIN: dict[str, str] = { - # Common formats (preinit) - ".bmp": "BmpImagePlugin", - ".dib": "BmpImagePlugin", - ".gif": "GifImagePlugin", - ".jfif": "JpegImagePlugin", - ".jpe": "JpegImagePlugin", - ".jpg": "JpegImagePlugin", - ".jpeg": "JpegImagePlugin", - ".pbm": "PpmImagePlugin", - ".pgm": "PpmImagePlugin", - ".pnm": "PpmImagePlugin", - ".ppm": "PpmImagePlugin", - ".pfm": "PpmImagePlugin", - ".png": "PngImagePlugin", - ".apng": "PngImagePlugin", - # Less common formats (init) - ".avif": "AvifImagePlugin", - ".avifs": "AvifImagePlugin", - ".blp": "BlpImagePlugin", - ".bufr": "BufrStubImagePlugin", - ".cur": "CurImagePlugin", - ".dcx": "DcxImagePlugin", - ".dds": "DdsImagePlugin", - ".ps": "EpsImagePlugin", - ".eps": "EpsImagePlugin", - ".fit": "FitsImagePlugin", - ".fits": "FitsImagePlugin", - ".fli": "FliImagePlugin", - ".flc": "FliImagePlugin", - ".fpx": "FpxImagePlugin", - ".ftc": "FtexImagePlugin", - ".ftu": "FtexImagePlugin", - ".gbr": "GbrImagePlugin", - ".grib": "GribStubImagePlugin", - ".h5": "Hdf5StubImagePlugin", - ".hdf": "Hdf5StubImagePlugin", - ".icns": "IcnsImagePlugin", - ".ico": "IcoImagePlugin", - ".im": "ImImagePlugin", - ".iim": "IptcImagePlugin", - ".jp2": "Jpeg2KImagePlugin", - ".j2k": "Jpeg2KImagePlugin", - ".jpc": "Jpeg2KImagePlugin", - ".jpf": "Jpeg2KImagePlugin", - ".jpx": "Jpeg2KImagePlugin", - ".j2c": "Jpeg2KImagePlugin", - ".mic": "MicImagePlugin", - ".mpg": "MpegImagePlugin", - ".mpeg": "MpegImagePlugin", - ".mpo": "MpoImagePlugin", - ".msp": "MspImagePlugin", - ".palm": "PalmImagePlugin", - ".pcd": "PcdImagePlugin", - ".pcx": "PcxImagePlugin", - ".pdf": "PdfImagePlugin", - ".pxr": "PixarImagePlugin", - ".psd": "PsdImagePlugin", - ".qoi": "QoiImagePlugin", - ".bw": "SgiImagePlugin", - ".rgb": "SgiImagePlugin", - ".rgba": "SgiImagePlugin", - ".sgi": "SgiImagePlugin", - ".ras": "SunImagePlugin", - ".tga": "TgaImagePlugin", - ".icb": "TgaImagePlugin", - ".vda": "TgaImagePlugin", - ".vst": "TgaImagePlugin", - ".tif": "TiffImagePlugin", - ".tiff": "TiffImagePlugin", - ".webp": "WebPImagePlugin", - ".wmf": "WmfImagePlugin", - ".emf": "WmfImagePlugin", - ".xbm": "XbmImagePlugin", - ".xpm": "XpmImagePlugin", -} - - -def _import_plugin_for_extension(ext: str | bytes) -> bool: - """Import only the plugin needed for a specific file extension.""" - if not ext: - return False - - if isinstance(ext, bytes): - ext = ext.decode() - ext = ext.lower() - if ext in EXTENSION: - return True - - plugin = _EXTENSION_PLUGIN.get(ext) - if plugin is None: - return False - - try: - logger.debug("Importing %s", plugin) - __import__(f"{__spec__.parent}.{plugin}", globals(), locals(), []) - return True - except ImportError as e: - logger.debug("Image: failed to import %s: %s", plugin, e) - return False - - -def preinit() -> None: - """ - Explicitly loads BMP, GIF, JPEG, PPM and PNG file format drivers. - - It is called when opening or saving images. - """ - - global _initialized - if _initialized >= 1: - return - - try: - from . import BmpImagePlugin - - assert BmpImagePlugin - except ImportError: - pass - try: - from . import GifImagePlugin - - assert GifImagePlugin - except ImportError: - pass - try: - from . import JpegImagePlugin - - assert JpegImagePlugin - except ImportError: - pass - try: - from . import PpmImagePlugin - - assert PpmImagePlugin - except ImportError: - pass - try: - from . import PngImagePlugin - - assert PngImagePlugin - except ImportError: - pass - - _initialized = 1 - - -def init() -> bool: - """ - Explicitly initializes the Python Imaging Library. This function - loads all available file format drivers. - - It is called when opening or saving images if :py:meth:`~preinit()` is - insufficient, and by :py:meth:`~PIL.features.pilinfo`. - """ - - global _initialized - if _initialized >= 2: - return False - - for plugin in _plugins: - try: - logger.debug("Importing %s", plugin) - __import__(f"{__spec__.parent}.{plugin}", globals(), locals(), []) - except ImportError as e: # noqa: PERF203 - logger.debug("Image: failed to import %s: %s", plugin, e) - - if OPEN or SAVE: - _initialized = 2 - return True - return False - - -# -------------------------------------------------------------------- -# Codec factories (used by tobytes/frombytes and ImageFile.load) - - -def _getdecoder( - mode: str, decoder_name: str, args: Any, extra: tuple[Any, ...] = () -) -> core.ImagingDecoder | ImageFile.PyDecoder: - # tweak arguments - if args is None: - args = () - elif not isinstance(args, tuple): - args = (args,) - - try: - decoder = DECODERS[decoder_name] - except KeyError: - pass - else: - return decoder(mode, *args + extra) - - try: - # get decoder - decoder = getattr(core, f"{decoder_name}_decoder") - except AttributeError as e: - msg = f"decoder {decoder_name} not available" - raise OSError(msg) from e - return decoder(mode, *args + extra) - - -def _getencoder( - mode: str, encoder_name: str, args: Any, extra: tuple[Any, ...] = () -) -> core.ImagingEncoder | ImageFile.PyEncoder: - # tweak arguments - if args is None: - args = () - elif not isinstance(args, tuple): - args = (args,) - - try: - encoder = ENCODERS[encoder_name] - except KeyError: - pass - else: - return encoder(mode, *args + extra) - - try: - # get encoder - encoder = getattr(core, f"{encoder_name}_encoder") - except AttributeError as e: - msg = f"encoder {encoder_name} not available" - raise OSError(msg) from e - return encoder(mode, *args + extra) - - -# -------------------------------------------------------------------- -# Simple expression analyzer - - -class ImagePointTransform: - """ - Used with :py:meth:`~PIL.Image.Image.point` for single band images with more than - 8 bits, this represents an affine transformation, where the value is multiplied by - ``scale`` and ``offset`` is added. - """ - - def __init__(self, scale: float, offset: float) -> None: - self.scale = scale - self.offset = offset - - def __neg__(self) -> ImagePointTransform: - return ImagePointTransform(-self.scale, -self.offset) - - def __add__(self, other: ImagePointTransform | float) -> ImagePointTransform: - if isinstance(other, ImagePointTransform): - return ImagePointTransform( - self.scale + other.scale, self.offset + other.offset - ) - return ImagePointTransform(self.scale, self.offset + other) - - __radd__ = __add__ - - def __sub__(self, other: ImagePointTransform | float) -> ImagePointTransform: - return self + -other - - def __rsub__(self, other: ImagePointTransform | float) -> ImagePointTransform: - return other + -self - - def __mul__(self, other: ImagePointTransform | float) -> ImagePointTransform: - if isinstance(other, ImagePointTransform): - return NotImplemented - return ImagePointTransform(self.scale * other, self.offset * other) - - __rmul__ = __mul__ - - def __truediv__(self, other: ImagePointTransform | float) -> ImagePointTransform: - if isinstance(other, ImagePointTransform): - return NotImplemented - return ImagePointTransform(self.scale / other, self.offset / other) - - -def _getscaleoffset( - expr: Callable[[ImagePointTransform], ImagePointTransform | float], -) -> tuple[float, float]: - a = expr(ImagePointTransform(1, 0)) - return (a.scale, a.offset) if isinstance(a, ImagePointTransform) else (0, a) - - -# -------------------------------------------------------------------- -# Implementation wrapper - - -class SupportsGetData(Protocol): - def getdata( - self, - ) -> tuple[Transform, Sequence[int]]: ... - - -class Image: - """ - This class represents an image object. To create - :py:class:`~PIL.Image.Image` objects, use the appropriate factory - functions. There's hardly ever any reason to call the Image constructor - directly. - - * :py:func:`~PIL.Image.open` - * :py:func:`~PIL.Image.new` - * :py:func:`~PIL.Image.frombytes` - """ - - format: str | None = None - format_description: str | None = None - _close_exclusive_fp_after_loading = True - - def __init__(self) -> None: - # FIXME: take "new" parameters / other image? - self._im: core.ImagingCore | DeferredError | None = None - self._mode = "" - self._size = (0, 0) - self.palette: ImagePalette.ImagePalette | None = None - self.info: dict[str | tuple[int, int], Any] = {} - self.readonly = 0 - self._exif: Exif | None = None - - @property - def im(self) -> core.ImagingCore: - if isinstance(self._im, DeferredError): - raise self._im.ex - assert self._im is not None - return self._im - - @im.setter - def im(self, im: core.ImagingCore) -> None: - self._im = im - - @property - def width(self) -> int: - return self.size[0] - - @property - def height(self) -> int: - return self.size[1] - - @property - def size(self) -> tuple[int, int]: - return self._size - - @property - def mode(self) -> str: - return self._mode - - @property - def readonly(self) -> int: - return (self._im and self._im.readonly) or self._readonly - - @readonly.setter - def readonly(self, readonly: int) -> None: - self._readonly = readonly - - def _new(self, im: core.ImagingCore) -> Image: - new = Image() - new.im = im - new._mode = im.mode - new._size = im.size - if im.mode in ("P", "PA"): - if self.palette: - new.palette = self.palette.copy() - else: - from . import ImagePalette - - new.palette = ImagePalette.ImagePalette() - new.info = self.info.copy() - return new - - # Context manager support - def __enter__(self) -> Image: - return self - - def __exit__(self, *args: object) -> None: - pass - - def close(self) -> None: - """ - This operation will destroy the image core and release its memory. - The image data will be unusable afterward. - - This function is required to close images that have multiple frames or - have not had their file read and closed by the - :py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for - more information. - """ - if getattr(self, "map", None): - if sys.platform == "win32" and hasattr(sys, "pypy_version_info"): - self.map.close() - self.map: mmap.mmap | None = None - - # Instead of simply setting to None, we're setting up a - # deferred error that will better explain that the core image - # object is gone. - self._im = DeferredError(ValueError("Operation on closed image")) - - def _copy(self) -> None: - self.load() - self.im = self.im.copy() - self.readonly = 0 - - def _ensure_mutable(self) -> None: - if self.readonly: - self._copy() - else: - self.load() - - def _dump( - self, file: str | None = None, format: str | None = None, **options: Any - ) -> str: - suffix = "" - if format: - suffix = f".{format}" - - if not file: - f, filename = tempfile.mkstemp(suffix) - os.close(f) - else: - filename = file - if not filename.endswith(suffix): - filename = filename + suffix - - self.load() - - if not format or format == "PPM": - self.im.save_ppm(filename) - else: - self.save(filename, format, **options) - - return filename - - def __eq__(self, other: object) -> bool: - if self.__class__ is not other.__class__: - return False - assert isinstance(other, Image) - return ( - self.mode == other.mode - and self.size == other.size - and self.info == other.info - and self.getpalette() == other.getpalette() - and self.tobytes() == other.tobytes() - ) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__module__}.{self.__class__.__name__} " - f"image mode={self.mode} size={self.size[0]}x{self.size[1]} " - f"at 0x{id(self):X}>" - ) - - def _repr_pretty_(self, p: PrettyPrinter, cycle: bool) -> None: - """IPython plain text display support""" - - # Same as __repr__ but without unpredictable id(self), - # to keep Jupyter notebook `text/plain` output stable. - p.text( - f"<{self.__class__.__module__}.{self.__class__.__name__} " - f"image mode={self.mode} size={self.size[0]}x{self.size[1]}>" - ) - - def _repr_image(self, image_format: str, **kwargs: Any) -> bytes | None: - """Helper function for iPython display hook. - - :param image_format: Image format. - :returns: image as bytes, saved into the given format. - """ - b = io.BytesIO() - try: - self.save(b, image_format, **kwargs) - except Exception: - return None - return b.getvalue() - - def _repr_png_(self) -> bytes | None: - """iPython display hook support for PNG format. - - :returns: PNG version of the image as bytes - """ - return self._repr_image("PNG", compress_level=1) - - def _repr_jpeg_(self) -> bytes | None: - """iPython display hook support for JPEG format. - - :returns: JPEG version of the image as bytes - """ - return self._repr_image("JPEG") - - @property - def __array_interface__(self) -> dict[str, str | bytes | int | tuple[int, ...]]: - # numpy array interface support - new: dict[str, str | bytes | int | tuple[int, ...]] = {"version": 3} - if self.mode == "1": - # Binary images need to be extended from bits to bytes - # See: https://github.com/python-pillow/Pillow/issues/350 - new["data"] = self.tobytes("raw", "L") - else: - new["data"] = self.tobytes() - new["shape"], new["typestr"] = _conv_type_shape(self) - return new - - def __arrow_c_schema__(self) -> object: - self.load() - return self.im.__arrow_c_schema__() - - def __arrow_c_array__( - self, requested_schema: object | None = None - ) -> tuple[object, object]: - self.load() - return (self.im.__arrow_c_schema__(), self.im.__arrow_c_array__()) - - def __getstate__(self) -> list[Any]: - im_data = self.tobytes() # load image first - return [self.info, self.mode, self.size, self.getpalette(), im_data] - - def __setstate__(self, state: list[Any]) -> None: - Image.__init__(self) - info, mode, size, palette, data = state[:5] - self.info = info - self._mode = mode - self._size = size - self.im = core.new(mode, size) - if mode in ("L", "LA", "P", "PA") and palette: - self.putpalette(palette) - self.frombytes(data) - - def tobytes(self, encoder_name: str = "raw", *args: Any) -> bytes: - """ - Return image as a bytes object. - - .. warning:: - - This method returns raw image data derived from Pillow's internal - storage. For compressed image data (e.g. PNG, JPEG) use - :meth:`~.save`, with a BytesIO parameter for in-memory data. - - :param encoder_name: What encoder to use. - - The default is to use the standard "raw" encoder. - To see how this packs pixel data into the returned - bytes, see :file:`libImaging/Pack.c`. - - A list of C encoders can be seen under codecs - section of the function array in - :file:`_imaging.c`. Python encoders are registered - within the relevant plugins. - :param args: Extra arguments to the encoder. - :returns: A :py:class:`bytes` object. - """ - - encoder_args: Any = args - if len(encoder_args) == 1 and isinstance(encoder_args[0], tuple): - # may pass tuple instead of argument list - encoder_args = encoder_args[0] - - if encoder_name == "raw" and encoder_args == (): - encoder_args = self.mode - - self.load() - - if self.width == 0 or self.height == 0: - return b"" - - # unpack data - e = _getencoder(self.mode, encoder_name, encoder_args) - e.setimage(self.im, (0, 0) + self.size) - - from . import ImageFile - - bufsize = max(ImageFile.MAXBLOCK, self.size[0] * 4) # see RawEncode.c - - output = [] - while True: - bytes_consumed, errcode, data = e.encode(bufsize) - output.append(data) - if errcode: - break - if errcode < 0: - msg = f"encoder error {errcode} in tobytes" - raise RuntimeError(msg) - - return b"".join(output) - - def tobitmap(self, name: str = "image") -> bytes: - """ - Returns the image converted to an X11 bitmap. - - .. note:: This method only works for mode "1" images. - - :param name: The name prefix to use for the bitmap variables. - :returns: A string containing an X11 bitmap. - :raises ValueError: If the mode is not "1" - """ - - self.load() - if self.mode != "1": - msg = "not a bitmap" - raise ValueError(msg) - data = self.tobytes("xbm") - return b"".join( - [ - f"#define {name}_width {self.size[0]}\n".encode("ascii"), - f"#define {name}_height {self.size[1]}\n".encode("ascii"), - f"static char {name}_bits[] = {{\n".encode("ascii"), - data, - b"};", - ] - ) - - def frombytes( - self, - data: bytes | bytearray | SupportsArrayInterface, - decoder_name: str = "raw", - *args: Any, - ) -> None: - """ - Loads this image with pixel data from a bytes object. - - This method is similar to the :py:func:`~PIL.Image.frombytes` function, - but loads data into this image instead of creating a new image object. - """ - - if self.width == 0 or self.height == 0: - return - - decoder_args: Any = args - if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple): - # may pass tuple instead of argument list - decoder_args = decoder_args[0] - - # default format - if decoder_name == "raw" and decoder_args == (): - decoder_args = self.mode - - # unpack data - d = _getdecoder(self.mode, decoder_name, decoder_args) - d.setimage(self.im, (0, 0) + self.size) - s = d.decode(data) - - if s[0] >= 0: - msg = "not enough image data" - raise ValueError(msg) - if s[1] != 0: - msg = "cannot decode image data" - raise ValueError(msg) - - def load(self) -> core.PixelAccess | None: - """ - Allocates storage for the image and loads the pixel data. In - normal cases, you don't need to call this method, since the - Image class automatically loads an opened image when it is - accessed for the first time. - - If the file associated with the image was opened by Pillow, then this - method will close it. The exception to this is if the image has - multiple frames, in which case the file will be left open for seek - operations. See :ref:`file-handling` for more information. - - :returns: An image access object. - :rtype: :py:class:`.PixelAccess` - """ - if self._im is not None and self.palette and self.palette.dirty: - # realize palette - mode, arr = self.palette.getdata() - self.im.putpalette(self.palette.mode, mode, arr) - self.palette.dirty = 0 - self.palette.rawmode = None - if "transparency" in self.info and mode in ("LA", "PA"): - if isinstance(self.info["transparency"], int): - self.im.putpalettealpha(self.info["transparency"], 0) - else: - self.im.putpalettealphas(self.info["transparency"]) - self.palette.mode = "RGBA" - elif self.palette.mode != mode: - # If the palette rawmode is different to the mode, - # then update the Python palette data - self.palette.palette = self.im.getpalette( - self.palette.mode, self.palette.mode - ) - - if self._im is not None: - return self.im.pixel_access(self.readonly) - return None - - def verify(self) -> None: - """ - Verifies the contents of a file. For data read from a file, this - method attempts to determine if the file is broken, without - actually decoding the image data. If this method finds any - problems, it raises suitable exceptions. If you need to load - the image after using this method, you must reopen the image - file. - """ - pass - - def convert( - self, - mode: str | None = None, - matrix: tuple[float, ...] | None = None, - dither: Dither | None = None, - palette: Palette = Palette.WEB, - colors: int = 256, - ) -> Image: - """ - Returns a converted copy of this image. For the "P" mode, this - method translates pixels through the palette. If mode is - omitted, a mode is chosen so that all information in the image - and the palette can be represented without a palette. - - This supports all possible conversions between "L", "RGB" and "CMYK". The - ``matrix`` argument only supports "L" and "RGB". - - When translating a color image to grayscale (mode "L"), - the library uses the ITU-R 601-2 luma transform:: - - L = R * 299/1000 + G * 587/1000 + B * 114/1000 - - The default method of converting a grayscale ("L") or "RGB" - image into a bilevel (mode "1") image uses Floyd-Steinberg - dither to approximate the original image luminosity levels. If - dither is ``None``, all values larger than 127 are set to 255 (white), - all other values to 0 (black). To use other thresholds, use the - :py:meth:`~PIL.Image.Image.point` method. - - When converting from "RGBA" to "P" without a ``matrix`` argument, - this passes the operation to :py:meth:`~PIL.Image.Image.quantize`, - and ``dither`` and ``palette`` are ignored. - - When converting from "PA", if an "RGBA" palette is present, the alpha - channel from the image will be used instead of the values from the palette. - - :param mode: The requested mode. See: :ref:`concept-modes`. - :param matrix: An optional conversion matrix. If given, this - should be 4- or 12-tuple containing floating point values. - :param dither: Dithering method, used when converting from - mode "RGB" to "P" or from "RGB" or "L" to "1". - Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` - (default). Note that this is not used when ``matrix`` is supplied. - :param palette: Palette to use when converting from mode "RGB" - to "P". Available palettes are :data:`Palette.WEB` or - :data:`Palette.ADAPTIVE`. - :param colors: Number of colors to use for the :data:`Palette.ADAPTIVE` - palette. Defaults to 256. - :rtype: :py:class:`~PIL.Image.Image` - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - self.load() - - has_transparency = "transparency" in self.info - if not mode and self.mode == "P": - # determine default mode - if self.palette: - mode = self.palette.mode - else: - mode = "RGB" - if mode == "RGB" and has_transparency: - mode = "RGBA" - if not mode or (mode == self.mode and not matrix): - return self.copy() - - if matrix: - # matrix conversion - if mode not in ("L", "RGB"): - msg = "illegal conversion" - raise ValueError(msg) - im = self.im.convert_matrix(mode, matrix) - new_im = self._new(im) - if has_transparency and self.im.bands == 3: - transparency = new_im.info["transparency"] - - def convert_transparency( - m: tuple[float, ...], v: tuple[int, int, int] - ) -> int: - value = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5 - return max(0, min(255, int(value))) - - if mode == "L": - transparency = convert_transparency(matrix, transparency) - elif len(mode) == 3: - transparency = tuple( - convert_transparency(matrix[i * 4 : i * 4 + 4], transparency) - for i in range(len(transparency)) - ) - new_im.info["transparency"] = transparency - return new_im - - if self.mode == "RGBA": - if mode == "P": - return self.quantize(colors) - elif mode == "PA": - r, g, b, a = self.split() - rgb = merge("RGB", (r, g, b)) - p = rgb.quantize(colors) - return merge("PA", (p, a)) - - trns = None - delete_trns = False - # transparency handling - if has_transparency: - if (self.mode in ("1", "L", "I", "I;16") and mode in ("LA", "RGBA")) or ( - self.mode == "RGB" and mode in ("La", "LA", "RGBa", "RGBA") - ): - # Use transparent conversion to promote from transparent - # color to an alpha channel. - new_im = self._new( - self.im.convert_transparent(mode, self.info["transparency"]) - ) - del new_im.info["transparency"] - return new_im - elif self.mode in ("L", "RGB", "P") and mode in ("L", "RGB", "P"): - t = self.info["transparency"] - if isinstance(t, bytes): - # Dragons. This can't be represented by a single color - warnings.warn( - "Palette images with Transparency expressed in bytes should be " - "converted to RGBA images" - ) - delete_trns = True - else: - # get the new transparency color. - # use existing conversions - trns_im = new(self.mode, (1, 1)) - if self.mode == "P": - assert self.palette is not None - trns_im.putpalette(self.palette, self.palette.mode) - if isinstance(t, tuple): - err = "Couldn't allocate a palette color for transparency" - assert trns_im.palette is not None - try: - t = trns_im.palette.getcolor(t, self) - except ValueError as e: - if str(e) == "cannot allocate more than 256 colors": - # If all 256 colors are in use, - # then there is no need for transparency - t = None - else: - raise ValueError(err) from e - if t is None: - trns = None - else: - trns_im.putpixel((0, 0), t) - - if mode in ("L", "RGB"): - trns_im = trns_im.convert(mode) - else: - # can't just retrieve the palette number, got to do it - # after quantization. - trns_im = trns_im.convert("RGB") - trns = trns_im.getpixel((0, 0)) - - elif self.mode == "P" and mode in ("LA", "PA", "RGBA"): - t = self.info["transparency"] - delete_trns = True - - if isinstance(t, bytes): - self.im.putpalettealphas(t) - elif isinstance(t, int): - self.im.putpalettealpha(t, 0) - else: - msg = "Transparency for P mode should be bytes or int" - raise ValueError(msg) - - if mode == "P" and palette == Palette.ADAPTIVE: - im = self.im.quantize(colors) - new_im = self._new(im) - from . import ImagePalette - - new_im.palette = ImagePalette.ImagePalette( - "RGB", new_im.im.getpalette("RGB") - ) - if delete_trns: - # This could possibly happen if we requantize to fewer colors. - # The transparency would be totally off in that case. - del new_im.info["transparency"] - if trns is not None: - try: - new_im.info["transparency"] = new_im.palette.getcolor( - cast(tuple[int, ...], trns), # trns was converted to RGB - new_im, - ) - except Exception: - # if we can't make a transparent color, don't leave the old - # transparency hanging around to mess us up. - del new_im.info["transparency"] - warnings.warn("Couldn't allocate palette entry for transparency") - return new_im - - if "LAB" in (self.mode, mode): - im = self - if mode == "LAB": - if im.mode not in ("RGB", "RGBA", "RGBX"): - im = im.convert("RGBA") - other_mode = im.mode - else: - other_mode = mode - if other_mode in ("RGB", "RGBA", "RGBX"): - from . import ImageCms - - srgb = ImageCms.createProfile("sRGB") - lab = ImageCms.createProfile("LAB") - profiles = [lab, srgb] if im.mode == "LAB" else [srgb, lab] - transform = ImageCms.buildTransform( - profiles[0], profiles[1], im.mode, mode - ) - return transform.apply(im) - - # colorspace conversion - if dither is None: - dither = Dither.FLOYDSTEINBERG - - try: - im = self.im.convert(mode, dither) - except ValueError: - try: - # normalize source image and try again - modebase = getmodebase(self.mode) - if modebase == self.mode: - raise - im = self.im.convert(modebase) - im = im.convert(mode, dither) - except KeyError as e: - msg = "illegal conversion" - raise ValueError(msg) from e - - new_im = self._new(im) - if mode in ("P", "PA") and palette != Palette.ADAPTIVE: - from . import ImagePalette - - new_im.palette = ImagePalette.ImagePalette("RGB", im.getpalette("RGB")) - if delete_trns: - # crash fail if we leave a bytes transparency in an rgb/l mode. - del new_im.info["transparency"] - if trns is not None: - if new_im.mode == "P" and new_im.palette: - try: - new_im.info["transparency"] = new_im.palette.getcolor( - cast(tuple[int, ...], trns), new_im # trns was converted to RGB - ) - except ValueError as e: - del new_im.info["transparency"] - if str(e) != "cannot allocate more than 256 colors": - # If all 256 colors are in use, - # then there is no need for transparency - warnings.warn( - "Couldn't allocate palette entry for transparency" - ) - else: - new_im.info["transparency"] = trns - return new_im - - def quantize( - self, - colors: int = 256, - method: int | None = None, - kmeans: int = 0, - palette: Image | None = None, - dither: Dither = Dither.FLOYDSTEINBERG, - ) -> Image: - """ - Convert the image to 'P' mode with the specified number - of colors. - - :param colors: The desired number of colors, <= 256 - :param method: :data:`Quantize.MEDIANCUT` (median cut), - :data:`Quantize.MAXCOVERAGE` (maximum coverage), - :data:`Quantize.FASTOCTREE` (fast octree), - :data:`Quantize.LIBIMAGEQUANT` (libimagequant; check support - using :py:func:`PIL.features.check_feature` with - ``feature="libimagequant"``). - - By default, :data:`Quantize.MEDIANCUT` will be used. - - The exception to this is RGBA images. :data:`Quantize.MEDIANCUT` - and :data:`Quantize.MAXCOVERAGE` do not support RGBA images, so - :data:`Quantize.FASTOCTREE` is used by default instead. - :param kmeans: Integer greater than or equal to zero. - :param palette: Quantize to the palette of given - :py:class:`PIL.Image.Image`. - :param dither: Dithering method, used when converting from - mode "RGB" to "P" or from "RGB" or "L" to "1". - Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` - (default). - :returns: A new image - """ - - self.load() - - if method is None: - # defaults: - method = Quantize.MEDIANCUT - if self.mode == "RGBA": - method = Quantize.FASTOCTREE - - if self.mode == "RGBA" and method not in ( - Quantize.FASTOCTREE, - Quantize.LIBIMAGEQUANT, - ): - # Caller specified an invalid mode. - msg = ( - "Fast Octree (method == 2) and libimagequant (method == 3) " - "are the only valid methods for quantizing RGBA images" - ) - raise ValueError(msg) - - if palette: - # use palette from reference image - palette.load() - if palette.mode != "P": - msg = "bad mode for palette image" - raise ValueError(msg) - if self.mode not in {"RGB", "L"}: - msg = "only RGB or L mode images can be quantized to a palette" - raise ValueError(msg) - im = self.im.convert("P", dither, palette.im) - new_im = self._new(im) - assert palette.palette is not None - new_im.palette = palette.palette.copy() - return new_im - - if kmeans < 0: - msg = "kmeans must not be negative" - raise ValueError(msg) - - im = self._new(self.im.quantize(colors, method, kmeans)) - - from . import ImagePalette - - mode = im.im.getpalettemode() - palette_data = im.im.getpalette(mode, mode)[: colors * len(mode)] - im.palette = ImagePalette.ImagePalette(mode, palette_data) - - return im - - def copy(self) -> Image: - """ - Copies this image. Use this method if you wish to paste things - into an image, but still retain the original. - - :rtype: :py:class:`~PIL.Image.Image` - :returns: An :py:class:`~PIL.Image.Image` object. - """ - self.load() - return self._new(self.im.copy()) - - __copy__ = copy - - def crop(self, box: tuple[float, float, float, float] | None = None) -> Image: - """ - Returns a rectangular region from this image. The box is a - 4-tuple defining the left, upper, right, and lower pixel - coordinate. See :ref:`coordinate-system`. - - Note: Prior to Pillow 3.4.0, this was a lazy operation. - - :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. - :rtype: :py:class:`~PIL.Image.Image` - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - if box is None: - return self.copy() - - if box[2] < box[0]: - msg = "Coordinate 'right' is less than 'left'" - raise ValueError(msg) - elif box[3] < box[1]: - msg = "Coordinate 'lower' is less than 'upper'" - raise ValueError(msg) - - self.load() - return self._new(self._crop(self.im, box)) - - def _crop( - self, im: core.ImagingCore, box: tuple[float, float, float, float] - ) -> core.ImagingCore: - """ - Returns a rectangular region from the core image object im. - - This is equivalent to calling im.crop((x0, y0, x1, y1)), but - includes additional sanity checks. - - :param im: a core image object - :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. - :returns: A core image object. - """ - - x0, y0, x1, y1 = map(int, map(round, box)) - - absolute_values = (abs(x1 - x0), abs(y1 - y0)) - - _decompression_bomb_check(absolute_values) - - return im.crop((x0, y0, x1, y1)) - - def draft( - self, mode: str | None, size: tuple[int, int] | None - ) -> tuple[str, tuple[int, int, float, float]] | None: - """ - Configures the image file loader so it returns a version of the - image that as closely as possible matches the given mode and - size. For example, you can use this method to convert a color - JPEG to grayscale while loading it. - - If any changes are made, returns a tuple with the chosen ``mode`` and - ``box`` with coordinates of the original image within the altered one. - - Note that this method modifies the :py:class:`~PIL.Image.Image` object - in place. If the image has already been loaded, this method has no - effect. - - Note: This method is not implemented for most images. It is - currently implemented only for JPEG and MPO images. - - :param mode: The requested mode. - :param size: The requested size in pixels, as a 2-tuple: - (width, height). - """ - pass - - def filter(self, filter: ImageFilter.Filter | type[ImageFilter.Filter]) -> Image: - """ - Filters this image using the given filter. For a list of - available filters, see the :py:mod:`~PIL.ImageFilter` module. - - :param filter: Filter kernel. - :returns: An :py:class:`~PIL.Image.Image` object.""" - - from . import ImageFilter - - self.load() - - if callable(filter): - filter = filter() - if not hasattr(filter, "filter"): - msg = "filter argument should be ImageFilter.Filter instance or class" - raise TypeError(msg) - - multiband = isinstance(filter, ImageFilter.MultibandFilter) - if self.im.bands == 1 or multiband: - return self._new(filter.filter(self.im)) - - ims = [ - self._new(filter.filter(self.im.getband(c))) for c in range(self.im.bands) - ] - return merge(self.mode, ims) - - def getbands(self) -> tuple[str, ...]: - """ - Returns a tuple containing the name of each band in this image. - For example, ``getbands`` on an RGB image returns ("R", "G", "B"). - - :returns: A tuple containing band names. - :rtype: tuple - """ - return ImageMode.getmode(self.mode).bands - - def getbbox(self, *, alpha_only: bool = True) -> tuple[int, int, int, int] | None: - """ - Calculates the bounding box of the non-zero regions in the - image. - - :param alpha_only: Optional flag, defaulting to ``True``. - If ``True`` and the image has an alpha channel, trim transparent pixels. - Otherwise, trim pixels when all channels are zero. - Keyword-only argument. - :returns: The bounding box is returned as a 4-tuple defining the - left, upper, right, and lower pixel coordinate. See - :ref:`coordinate-system`. If the image is completely empty, this - method returns None. - - """ - - self.load() - return self.im.getbbox(alpha_only) - - def getcolors( - self, maxcolors: int = 256 - ) -> list[tuple[int, tuple[int, ...]]] | list[tuple[int, float]] | None: - """ - Returns a list of colors used in this image. - - The colors will be in the image's mode. For example, an RGB image will - return a tuple of (red, green, blue) color values, and a P image will - return the index of the color in the palette. - - :param maxcolors: Maximum number of colors. If this number is - exceeded, this method returns None. The default limit is - 256 colors. - :returns: An unsorted list of (count, pixel) values. - """ - - self.load() - if self.mode in ("1", "L", "P"): - h = self.im.histogram() - out: list[tuple[int, float]] = [(h[i], i) for i in range(256) if h[i]] - if len(out) > maxcolors: - return None - return out - return self.im.getcolors(maxcolors) - - def getdata(self, band: int | None = None) -> core.ImagingCore: - """ - Returns the contents of this image as a sequence object - containing pixel values. The sequence object is flattened, so - that values for line one follow directly after the values of - line zero, and so on. - - Note that the sequence object returned by this method is an - internal PIL data type, which only supports certain sequence - operations. To convert it to an ordinary sequence (e.g. for - printing), use ``list(im.getdata())``. - - :param band: What band to return. The default is to return - all bands. To return a single band, pass in the index - value (e.g. 0 to get the "R" band from an "RGB" image). - :returns: A sequence-like object. - """ - deprecate("Image.Image.getdata", 14, "get_flattened_data") - - self.load() - if band is not None: - return self.im.getband(band) - return self.im # could be abused - - def get_flattened_data( - self, band: int | None = None - ) -> tuple[tuple[int, ...], ...] | tuple[float, ...]: - """ - Returns the contents of this image as a tuple containing pixel values. - The sequence object is flattened, so that values for line one follow - directly after the values of line zero, and so on. - - :param band: What band to return. The default is to return - all bands. To return a single band, pass in the index - value (e.g. 0 to get the "R" band from an "RGB" image). - :returns: A tuple containing pixel values. - """ - self.load() - if band is not None: - return tuple(self.im.getband(band)) - return tuple(self.im) - - def getextrema(self) -> tuple[float, float] | tuple[tuple[int, int], ...]: - """ - Gets the minimum and maximum pixel values for each band in - the image. - - :returns: For a single-band image, a 2-tuple containing the - minimum and maximum pixel value. For a multi-band image, - a tuple containing one 2-tuple for each band. - """ - - self.load() - if self.im.bands > 1: - return tuple(self.im.getband(i).getextrema() for i in range(self.im.bands)) - return self.im.getextrema() - - def getxmp(self) -> dict[str, Any]: - """ - Returns a dictionary containing the XMP tags. - Requires defusedxml to be installed. - - :returns: XMP tags in a dictionary. - """ - - def get_name(tag: str) -> str: - return re.sub("^{[^}]+}", "", tag) - - def get_value(element: Element) -> str | dict[str, Any] | None: - value: dict[str, Any] = {get_name(k): v for k, v in element.attrib.items()} - children = list(element) - if children: - for child in children: - name = get_name(child.tag) - child_value = get_value(child) - if name in value: - if not isinstance(value[name], list): - value[name] = [value[name]] - value[name].append(child_value) - else: - value[name] = child_value - elif value: - if element.text: - value["text"] = element.text - else: - return element.text - return value - - if ElementTree is None: - warnings.warn("XMP data cannot be read without defusedxml dependency") - return {} - if "xmp" not in self.info: - return {} - root = ElementTree.fromstring(self.info["xmp"].rstrip(b"\x00 ")) - return {get_name(root.tag): get_value(root)} - - def getexif(self) -> Exif: - """ - Gets EXIF data from the image. - - :returns: an :py:class:`~PIL.Image.Exif` object. - """ - if self._exif is None: - self._exif = Exif() - elif self._exif._loaded: - return self._exif - self._exif._loaded = True - - exif_info = self.info.get("exif") - if exif_info is None: - if "Raw profile type exif" in self.info: - exif_info = bytes.fromhex( - "".join(self.info["Raw profile type exif"].split("\n")[3:]) - ) - elif hasattr(self, "tag_v2"): - from . import TiffImagePlugin - - assert isinstance(self, TiffImagePlugin.TiffImageFile) - self._exif.bigtiff = self.tag_v2._bigtiff - self._exif.endian = self.tag_v2._endian - - assert self.fp is not None - self._exif.load_from_fp(self.fp, self.tag_v2._offset) - if exif_info is not None: - self._exif.load(exif_info) - - # XMP tags - if ExifTags.Base.Orientation not in self._exif: - xmp_tags = self.info.get("XML:com.adobe.xmp") - pattern: str | bytes = r'tiff:Orientation(="|>)([0-9])' - if not xmp_tags and (xmp_tags := self.info.get("xmp")): - pattern = rb'tiff:Orientation(="|>)([0-9])' - if xmp_tags: - match = re.search(pattern, xmp_tags) - if match: - self._exif[ExifTags.Base.Orientation] = int(match[2]) - - return self._exif - - def _reload_exif(self) -> None: - if self._exif is None or not self._exif._loaded: - return - self._exif._loaded = False - self.getexif() - - def get_child_images(self) -> list[ImageFile.ImageFile]: - from . import ImageFile - - deprecate("Image.Image.get_child_images", 13) - return ImageFile.ImageFile.get_child_images(self) # type: ignore[arg-type] - - def getim(self) -> CapsuleType: - """ - Returns a capsule that points to the internal image memory. - - :returns: A capsule object. - """ - - self.load() - return self.im.ptr - - def getpalette(self, rawmode: str | None = "RGB") -> list[int] | None: - """ - Returns the image palette as a list. - - :param rawmode: The mode in which to return the palette. ``None`` will - return the palette in its current mode. - - .. versionadded:: 9.1.0 - - :returns: A list of color values [r, g, b, ...], or None if the - image has no palette. - """ - - self.load() - try: - mode = self.im.getpalettemode() - except ValueError: - return None # no palette - if rawmode is None: - rawmode = mode - return list(self.im.getpalette(mode, rawmode)) - - @property - def has_transparency_data(self) -> bool: - """ - Determine if an image has transparency data, whether in the form of an - alpha channel, a palette with an alpha channel, or a "transparency" key - in the info dictionary. - - Note the image might still appear solid, if all of the values shown - within are opaque. - - :returns: A boolean. - """ - if ( - self.mode in ("LA", "La", "PA", "RGBA", "RGBa") - or "transparency" in self.info - ): - return True - if self.mode == "P": - assert self.palette is not None - return self.palette.mode.endswith("A") - return False - - def apply_transparency(self) -> None: - """ - If a P mode image has a "transparency" key in the info dictionary, - remove the key and instead apply the transparency to the palette. - Otherwise, the image is unchanged. - """ - if self.mode != "P" or "transparency" not in self.info: - return - - from . import ImagePalette - - palette = self.getpalette("RGBA") - assert palette is not None - transparency = self.info["transparency"] - if isinstance(transparency, bytes): - for i, alpha in enumerate(transparency): - palette[i * 4 + 3] = alpha - else: - palette[transparency * 4 + 3] = 0 - self.palette = ImagePalette.ImagePalette("RGBA", bytes(palette)) - self.palette.dirty = 1 - - del self.info["transparency"] - - def getpixel( - self, xy: tuple[int, int] | list[int] - ) -> float | tuple[int, ...] | None: - """ - Returns the pixel value at a given position. - - :param xy: The coordinate, given as (x, y). See - :ref:`coordinate-system`. - :returns: The pixel value. If the image is a multi-layer image, - this method returns a tuple. - """ - - self.load() - return self.im.getpixel(tuple(xy)) - - def getprojection(self) -> tuple[list[int], list[int]]: - """ - Get projection to x and y axes - - :returns: Two sequences, indicating where there are non-zero - pixels along the X-axis and the Y-axis, respectively. - """ - - self.load() - x, y = self.im.getprojection() - return list(x), list(y) - - def histogram( - self, mask: Image | None = None, extrema: tuple[float, float] | None = None - ) -> list[int]: - """ - Returns a histogram for the image. The histogram is returned as a - list of pixel counts, one for each pixel value in the source - image. Counts are grouped into 256 bins for each band, even if - the image has more than 8 bits per band. If the image has more - than one band, the histograms for all bands are concatenated (for - example, the histogram for an "RGB" image contains 768 values). - - A bilevel image (mode "1") is treated as a grayscale ("L") image - by this method. - - If a mask is provided, the method returns a histogram for those - parts of the image where the mask image is non-zero. The mask - image must have the same size as the image, and be either a - bi-level image (mode "1") or a grayscale image ("L"). - - :param mask: An optional mask. - :param extrema: An optional tuple of manually-specified extrema. - :returns: A list containing pixel counts. - """ - self.load() - if mask: - mask.load() - return self.im.histogram((0, 0), mask.im) - if self.mode in ("I", "F"): - return self.im.histogram( - extrema if extrema is not None else self.getextrema() - ) - return self.im.histogram() - - def entropy( - self, mask: Image | None = None, extrema: tuple[float, float] | None = None - ) -> float: - """ - Calculates and returns the entropy for the image. - - A bilevel image (mode "1") is treated as a grayscale ("L") - image by this method. - - If a mask is provided, the method employs the histogram for - those parts of the image where the mask image is non-zero. - The mask image must have the same size as the image, and be - either a bi-level image (mode "1") or a grayscale image ("L"). - - :param mask: An optional mask. - :param extrema: An optional tuple of manually-specified extrema. - :returns: A float value representing the image entropy - """ - self.load() - if mask: - mask.load() - return self.im.entropy((0, 0), mask.im) - if self.mode in ("I", "F"): - return self.im.entropy( - extrema if extrema is not None else self.getextrema() - ) - return self.im.entropy() - - def paste( - self, - im: Image | str | float | tuple[float, ...], - box: Image | tuple[int, int, int, int] | tuple[int, int] | None = None, - mask: Image | None = None, - ) -> None: - """ - Pastes another image into this image. The box argument is either - a 2-tuple giving the upper left corner, a 4-tuple defining the - left, upper, right, and lower pixel coordinate, or None (same as - (0, 0)). See :ref:`coordinate-system`. If a 4-tuple is given, the size - of the pasted image must match the size of the region. - - If the modes don't match, the pasted image is converted to the mode of - this image (see the :py:meth:`~PIL.Image.Image.convert` method for - details). - - Instead of an image, the source can be a integer or tuple - containing pixel values. The method then fills the region - with the given color. When creating RGB images, you can - also use color strings as supported by the ImageColor module. See - :ref:`colors` for more information. - - If a mask is given, this method updates only the regions - indicated by the mask. You can use either "1", "L", "LA", "RGBA" - or "RGBa" images (if present, the alpha band is used as mask). - Where the mask is 255, the given image is copied as is. Where - the mask is 0, the current value is preserved. Intermediate - values will mix the two images together, including their alpha - channels if they have them. - - See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to - combine images with respect to their alpha channels. - - :param im: Source image or pixel value (integer, float or tuple). - :param box: An optional 4-tuple giving the region to paste into. - If a 2-tuple is used instead, it's treated as the upper left - corner. If omitted or None, the source is pasted into the - upper left corner. - - If an image is given as the second argument and there is no - third, the box defaults to (0, 0), and the second argument - is interpreted as a mask image. - :param mask: An optional mask image. - """ - - if isinstance(box, Image): - if mask is not None: - msg = "If using second argument as mask, third argument must be None" - raise ValueError(msg) - # abbreviated paste(im, mask) syntax - mask = box - box = None - - if box is None: - box = (0, 0) - - if len(box) == 2: - # upper left corner given; get size from image or mask - if isinstance(im, Image): - size = im.size - elif isinstance(mask, Image): - size = mask.size - else: - # FIXME: use self.size here? - msg = "cannot determine region size; use 4-item box" - raise ValueError(msg) - box += (box[0] + size[0], box[1] + size[1]) - - source: core.ImagingCore | str | float | tuple[float, ...] - if isinstance(im, str): - from . import ImageColor - - source = ImageColor.getcolor(im, self.mode) - elif isinstance(im, Image): - im.load() - if self.mode != im.mode: - if self.mode != "RGB" or im.mode not in ("LA", "RGBA", "RGBa"): - # should use an adapter for this! - im = im.convert(self.mode) - source = im.im - else: - source = im - - self._ensure_mutable() - - if mask: - mask.load() - self.im.paste(source, box, mask.im) - else: - self.im.paste(source, box) - - def alpha_composite( - self, im: Image, dest: Sequence[int] = (0, 0), source: Sequence[int] = (0, 0) - ) -> None: - """'In-place' analog of Image.alpha_composite. Composites an image - onto this image. - - :param im: image to composite over this one - :param dest: Optional 2 tuple (left, top) specifying the upper - left corner in this (destination) image. - :param source: Optional 2 (left, top) tuple for the upper left - corner in the overlay source image, or 4 tuple (left, top, right, - bottom) for the bounds of the source rectangle - - Performance Note: Not currently implemented in-place in the core layer. - """ - - if not isinstance(source, (list, tuple)): - msg = "Source must be a list or tuple" - raise ValueError(msg) - if not isinstance(dest, (list, tuple)): - msg = "Destination must be a list or tuple" - raise ValueError(msg) - - if len(source) == 4: - overlay_crop_box = tuple(source) - elif len(source) == 2: - overlay_crop_box = tuple(source) + im.size - else: - msg = "Source must be a sequence of length 2 or 4" - raise ValueError(msg) - - if not len(dest) == 2: - msg = "Destination must be a sequence of length 2" - raise ValueError(msg) - if min(source) < 0: - msg = "Source must be non-negative" - raise ValueError(msg) - - # over image, crop if it's not the whole image. - if overlay_crop_box == (0, 0) + im.size: - overlay = im - else: - overlay = im.crop(overlay_crop_box) - - # target for the paste - box = tuple(dest) + (dest[0] + overlay.width, dest[1] + overlay.height) - - # destination image. don't copy if we're using the whole image. - if box == (0, 0) + self.size: - background = self - else: - background = self.crop(box) - - result = alpha_composite(background, overlay) - self.paste(result, box) - - def point( - self, - lut: ( - Sequence[float] - | NumpyArray - | Callable[[int], float] - | Callable[[ImagePointTransform], ImagePointTransform | float] - | ImagePointHandler - ), - mode: str | None = None, - ) -> Image: - """ - Maps this image through a lookup table or function. - - :param lut: A lookup table, containing 256 (or 65536 if - self.mode=="I" and mode == "L") values per band in the - image. A function can be used instead, it should take a - single argument. The function is called once for each - possible pixel value, and the resulting table is applied to - all bands of the image. - - It may also be an :py:class:`~PIL.Image.ImagePointHandler` - object:: - - class Example(Image.ImagePointHandler): - def point(self, im: Image) -> Image: - # Return result - :param mode: Output mode (default is same as input). This can only be used if - the source image has mode "L" or "P", and the output has mode "1" or the - source image mode is "I" and the output mode is "L". - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - self.load() - - if isinstance(lut, ImagePointHandler): - return lut.point(self) - - if callable(lut): - # if it isn't a list, it should be a function - if self.mode in ("I", "I;16", "F"): - # check if the function can be used with point_transform - # UNDONE wiredfool -- I think this prevents us from ever doing - # a gamma function point transform on > 8bit images. - scale, offset = _getscaleoffset(lut) # type: ignore[arg-type] - return self._new(self.im.point_transform(scale, offset)) - # for other modes, convert the function to a table - flatLut = [lut(i) for i in range(256)] * self.im.bands # type: ignore[arg-type] - else: - flatLut = lut - - if self.mode == "F": - # FIXME: _imaging returns a confusing error message for this case - msg = "point operation not supported for this mode" - raise ValueError(msg) - - if mode != "F": - flatLut = [round(i) for i in flatLut] - return self._new(self.im.point(flatLut, mode)) - - def putalpha(self, alpha: Image | int) -> None: - """ - Adds or replaces the alpha layer in this image. If the image - does not have an alpha layer, it's converted to "LA" or "RGBA". - The new layer must be either "L" or "1". - - :param alpha: The new alpha layer. This can either be an "L" or "1" - image having the same size as this image, or an integer. - """ - - self._ensure_mutable() - - if self.mode not in ("LA", "PA", "RGBA"): - # attempt to promote self to a matching alpha mode - try: - mode = getmodebase(self.mode) + "A" - try: - self.im.setmode(mode) - except (AttributeError, ValueError) as e: - # do things the hard way - im = self.im.convert(mode) - if im.mode not in ("LA", "PA", "RGBA"): - msg = "alpha channel could not be added" - raise ValueError(msg) from e # sanity check - self.im = im - self._mode = self.im.mode - except KeyError as e: - msg = "illegal image mode" - raise ValueError(msg) from e - - if self.mode in ("LA", "PA"): - band = 1 - else: - band = 3 - - if isinstance(alpha, Image): - # alpha layer - if alpha.mode not in ("1", "L"): - msg = "illegal image mode" - raise ValueError(msg) - alpha.load() - if alpha.mode == "1": - alpha = alpha.convert("L") - else: - # constant alpha - try: - self.im.fillband(band, alpha) - except (AttributeError, ValueError): - # do things the hard way - alpha = new("L", self.size, alpha) - else: - return - - self.im.putband(alpha.im, band) - - def putdata( - self, - data: Sequence[float] | Sequence[Sequence[int]] | core.ImagingCore | NumpyArray, - scale: float = 1.0, - offset: float = 0.0, - ) -> None: - """ - Copies pixel data from a flattened sequence object into the image. The - values should start at the upper left corner (0, 0), continue to the - end of the line, followed directly by the first value of the second - line, and so on. Data will be read until either the image or the - sequence ends. The scale and offset values are used to adjust the - sequence values: **pixel = value*scale + offset**. - - :param data: A flattened sequence object. See :ref:`colors` for more - information about values. - :param scale: An optional scale value. The default is 1.0. - :param offset: An optional offset value. The default is 0.0. - """ - - self._ensure_mutable() - - self.im.putdata(data, scale, offset) - - def putpalette( - self, - data: ImagePalette.ImagePalette | bytes | Sequence[int], - rawmode: str = "RGB", - ) -> None: - """ - Attaches a palette to this image. The image must be a "P", "PA", "L" - or "LA" image. - - The palette sequence must contain at most 256 colors, made up of one - integer value for each channel in the raw mode. - For example, if the raw mode is "RGB", then it can contain at most 768 - values, made up of red, green and blue values for the corresponding pixel - index in the 256 colors. - If the raw mode is "RGBA", then it can contain at most 1024 values, - containing red, green, blue and alpha values. - - Alternatively, an 8-bit string may be used instead of an integer sequence. - - :param data: A palette sequence (either a list or a string). - :param rawmode: The raw mode of the palette. Either "RGB", "RGBA", "CMYK", or a - mode that can be transformed to one of those modes (e.g. "R", "RGBA;L"). - """ - from . import ImagePalette - - if self.mode not in ("L", "LA", "P", "PA"): - msg = "illegal image mode" - raise ValueError(msg) - if isinstance(data, ImagePalette.ImagePalette): - if data.rawmode is not None: - palette = ImagePalette.raw(data.rawmode, data.palette) - else: - palette = ImagePalette.ImagePalette(palette=data.palette) - palette.dirty = 1 - else: - if not isinstance(data, bytes): - data = bytes(data) - palette = ImagePalette.raw(rawmode, data) - self._mode = "PA" if "A" in self.mode else "P" - self.palette = palette - if rawmode.startswith("CMYK"): - self.palette.mode = "CMYK" - elif "A" in rawmode: - self.palette.mode = "RGBA" - else: - self.palette.mode = "RGB" - self.load() # install new palette - - def putpixel( - self, xy: tuple[int, int], value: float | tuple[int, ...] | list[int] - ) -> None: - """ - Modifies the pixel at the given position. The color is given as - a single numerical value for single-band images, and a tuple for - multi-band images. In addition to this, RGB and RGBA tuples are - accepted for P and PA images. See :ref:`colors` for more information. - - Note that this method is relatively slow. For more extensive changes, - use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` - module instead. - - See: - - * :py:meth:`~PIL.Image.Image.paste` - * :py:meth:`~PIL.Image.Image.putdata` - * :py:mod:`~PIL.ImageDraw` - - :param xy: The pixel coordinate, given as (x, y). See - :ref:`coordinate-system`. - :param value: The pixel value. - """ - - self._ensure_mutable() - - if ( - self.mode in ("P", "PA") - and isinstance(value, (list, tuple)) - and len(value) in [3, 4] - ): - # RGB or RGBA value for a P or PA image - if self.mode == "PA": - alpha = value[3] if len(value) == 4 else 255 - value = value[:3] - assert self.palette is not None - palette_index = self.palette.getcolor(tuple(value), self) - value = (palette_index, alpha) if self.mode == "PA" else palette_index - return self.im.putpixel(xy, value) - - def remap_palette( - self, dest_map: list[int], source_palette: bytes | bytearray | None = None - ) -> Image: - """ - Rewrites the image to reorder the palette. - - :param dest_map: A list of indexes into the original palette. - e.g. ``[1,0]`` would swap a two item palette, and ``list(range(256))`` - is the identity transform. - :param source_palette: Bytes or None. - :returns: An :py:class:`~PIL.Image.Image` object. - - """ - from . import ImagePalette - - if self.mode not in ("L", "P"): - msg = "illegal image mode" - raise ValueError(msg) - - bands = 3 - palette_mode = "RGB" - if source_palette is None: - if self.mode == "P": - self.load() - palette_mode = self.im.getpalettemode() - if palette_mode == "RGBA": - bands = 4 - source_palette = self.im.getpalette(palette_mode, palette_mode) - else: # L-mode - source_palette = bytearray(i // 3 for i in range(768)) - elif len(source_palette) > 768: - bands = 4 - palette_mode = "RGBA" - - palette_bytes = b"" - new_positions = [0] * 256 - - # pick only the used colors from the palette - for i, oldPosition in enumerate(dest_map): - palette_bytes += source_palette[ - oldPosition * bands : oldPosition * bands + bands - ] - new_positions[oldPosition] = i - - # replace the palette color id of all pixel with the new id - - # Palette images are [0..255], mapped through a 1 or 3 - # byte/color map. We need to remap the whole image - # from palette 1 to palette 2. New_positions is - # an array of indexes into palette 1. Palette 2 is - # palette 1 with any holes removed. - - # We're going to leverage the convert mechanism to use the - # C code to remap the image from palette 1 to palette 2, - # by forcing the source image into 'L' mode and adding a - # mapping 'L' mode palette, then converting back to 'L' - # sans palette thus converting the image bytes, then - # assigning the optimized RGB palette. - - # perf reference, 9500x4000 gif, w/~135 colors - # 14 sec prepatch, 1 sec postpatch with optimization forced. - - mapping_palette = bytearray(new_positions) - - m_im = self.copy() - m_im._mode = "P" - - m_im.palette = ImagePalette.ImagePalette( - palette_mode, palette=mapping_palette * bands - ) - # possibly set palette dirty, then - # m_im.putpalette(mapping_palette, 'L') # converts to 'P' - # or just force it. - # UNDONE -- this is part of the general issue with palettes - m_im.im.putpalette(palette_mode, palette_mode + ";L", m_im.palette.tobytes()) - - m_im = m_im.convert("L") - - m_im.putpalette(palette_bytes, palette_mode) - m_im.palette = ImagePalette.ImagePalette(palette_mode, palette=palette_bytes) - - if "transparency" in self.info: - try: - m_im.info["transparency"] = dest_map.index(self.info["transparency"]) - except ValueError: - if "transparency" in m_im.info: - del m_im.info["transparency"] - - return m_im - - def _get_safe_box( - self, - size: tuple[int, int], - resample: Resampling, - box: tuple[float, float, float, float], - ) -> tuple[int, int, int, int]: - """Expands the box so it includes adjacent pixels - that may be used by resampling with the given resampling filter. - """ - filter_support = _filters_support[resample] - 0.5 - scale_x = (box[2] - box[0]) / size[0] - scale_y = (box[3] - box[1]) / size[1] - support_x = filter_support * scale_x - support_y = filter_support * scale_y - - return ( - max(0, int(box[0] - support_x)), - max(0, int(box[1] - support_y)), - min(self.size[0], math.ceil(box[2] + support_x)), - min(self.size[1], math.ceil(box[3] + support_y)), - ) - - def resize( - self, - size: tuple[int, int] | list[int] | NumpyArray, - resample: int | None = None, - box: tuple[float, float, float, float] | None = None, - reducing_gap: float | None = None, - ) -> Image: - """ - Returns a resized copy of this image. - - :param size: The requested size in pixels, as a tuple or array: - (width, height). - :param resample: An optional resampling filter. This can be - one of :py:data:`Resampling.NEAREST`, :py:data:`Resampling.BOX`, - :py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`, - :py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`. - If the image has mode "1" or "P", it is always set to - :py:data:`Resampling.NEAREST`. Otherwise, the default filter is - :py:data:`Resampling.BICUBIC`. See: :ref:`concept-filters`. - :param box: An optional 4-tuple of floats providing - the source image region to be scaled. - The values must be within (0, 0, width, height) rectangle. - If omitted or None, the entire source is used. - :param reducing_gap: Apply optimization by resizing the image - in two steps. First, reducing the image by integer times - using :py:meth:`~PIL.Image.Image.reduce`. - Second, resizing using regular resampling. The last step - changes size no less than by ``reducing_gap`` times. - ``reducing_gap`` may be None (no first step is performed) - or should be greater than 1.0. The bigger ``reducing_gap``, - the closer the result to the fair resampling. - The smaller ``reducing_gap``, the faster resizing. - With ``reducing_gap`` greater or equal to 3.0, the result is - indistinguishable from fair resampling in most cases. - The default value is None (no optimization). - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - if resample is None: - resample = Resampling.BICUBIC - elif resample not in ( - Resampling.NEAREST, - Resampling.BILINEAR, - Resampling.BICUBIC, - Resampling.LANCZOS, - Resampling.BOX, - Resampling.HAMMING, - ): - msg = f"Unknown resampling filter ({resample})." - - filters = [ - f"{filter[1]} ({filter[0]})" - for filter in ( - (Resampling.NEAREST, "Image.Resampling.NEAREST"), - (Resampling.LANCZOS, "Image.Resampling.LANCZOS"), - (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), - (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), - (Resampling.BOX, "Image.Resampling.BOX"), - (Resampling.HAMMING, "Image.Resampling.HAMMING"), - ) - ] - msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" - raise ValueError(msg) - - if reducing_gap is not None and reducing_gap < 1.0: - msg = "reducing_gap must be 1.0 or greater" - raise ValueError(msg) - - if box is None: - box = (0, 0) + self.size - - size = tuple(size) - if self.size == size and box == (0, 0) + self.size: - return self.copy() - - if self.mode in ("1", "P"): - resample = Resampling.NEAREST - - if self.mode in ["LA", "RGBA"] and resample != Resampling.NEAREST: - im = self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) - im = im.resize(size, resample, box) - return im.convert(self.mode) - - self.load() - - if reducing_gap is not None and resample != Resampling.NEAREST: - factor_x = int((box[2] - box[0]) / size[0] / reducing_gap) or 1 - factor_y = int((box[3] - box[1]) / size[1] / reducing_gap) or 1 - if factor_x > 1 or factor_y > 1: - reduce_box = self._get_safe_box(size, cast(Resampling, resample), box) - factor = (factor_x, factor_y) - self = ( - self.reduce(factor, box=reduce_box) - if callable(self.reduce) - else Image.reduce(self, factor, box=reduce_box) - ) - box = ( - (box[0] - reduce_box[0]) / factor_x, - (box[1] - reduce_box[1]) / factor_y, - (box[2] - reduce_box[0]) / factor_x, - (box[3] - reduce_box[1]) / factor_y, - ) - - if self.size[1] > self.size[0] * 100 and size[1] < self.size[1]: - im = self.im.resize( - (self.size[0], size[1]), resample, (0, box[1], self.size[0], box[3]) - ) - im = im.resize(size, resample, (box[0], 0, box[2], size[1])) - else: - im = self.im.resize(size, resample, box) - return self._new(im) - - def reduce( - self, - factor: int | tuple[int, int], - box: tuple[int, int, int, int] | None = None, - ) -> Image: - """ - Returns a copy of the image reduced ``factor`` times. - If the size of the image is not dividable by ``factor``, - the resulting size will be rounded up. - - :param factor: A greater than 0 integer or tuple of two integers - for width and height separately. - :param box: An optional 4-tuple of ints providing - the source image region to be reduced. - The values must be within ``(0, 0, width, height)`` rectangle. - If omitted or ``None``, the entire source is used. - """ - if not isinstance(factor, (list, tuple)): - factor = (factor, factor) - - if box is None: - box = (0, 0) + self.size - - if factor == (1, 1) and box == (0, 0) + self.size: - return self.copy() - - if self.mode in ["LA", "RGBA"]: - im = self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) - im = im.reduce(factor, box) - return im.convert(self.mode) - - self.load() - - return self._new(self.im.reduce(factor, box)) - - def rotate( - self, - angle: float, - resample: Resampling = Resampling.NEAREST, - expand: int | bool = False, - center: tuple[float, float] | None = None, - translate: tuple[int, int] | None = None, - fillcolor: float | tuple[float, ...] | str | None = None, - ) -> Image: - """ - Returns a rotated copy of this image. This method returns a - copy of this image, rotated the given number of degrees counter - clockwise around its centre. - - :param angle: In degrees counter clockwise. - :param resample: An optional resampling filter. This can be - one of :py:data:`Resampling.NEAREST` (use nearest neighbour), - :py:data:`Resampling.BILINEAR` (linear interpolation in a 2x2 - environment), or :py:data:`Resampling.BICUBIC` (cubic spline - interpolation in a 4x4 environment). If omitted, or if the image has - mode "1" or "P", it is set to :py:data:`Resampling.NEAREST`. - See :ref:`concept-filters`. - :param expand: Optional expansion flag. If true, expands the output - image to make it large enough to hold the entire rotated image. - If false or omitted, make the output image the same size as the - input image. Note that the expand flag assumes rotation around - the center and no translation. - :param center: Optional center of rotation (a 2-tuple). Origin is - the upper left corner. Default is the center of the image. - :param translate: An optional post-rotate translation (a 2-tuple). - :param fillcolor: An optional color for area outside the rotated image. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - angle = angle % 360.0 - - # Fast paths regardless of filter, as long as we're not - # translating or changing the center. - if not (center or translate): - if angle == 0: - return self.copy() - if angle == 180: - return self.transpose(Transpose.ROTATE_180) - if angle in (90, 270) and (expand or self.width == self.height): - return self.transpose( - Transpose.ROTATE_90 if angle == 90 else Transpose.ROTATE_270 - ) - - # Calculate the affine matrix. Note that this is the reverse - # transformation (from destination image to source) because we - # want to interpolate the (discrete) destination pixel from - # the local area around the (floating) source pixel. - - # The matrix we actually want (note that it operates from the right): - # (1, 0, tx) (1, 0, cx) ( cos a, sin a, 0) (1, 0, -cx) - # (0, 1, ty) * (0, 1, cy) * (-sin a, cos a, 0) * (0, 1, -cy) - # (0, 0, 1) (0, 0, 1) ( 0, 0, 1) (0, 0, 1) - - # The reverse matrix is thus: - # (1, 0, cx) ( cos -a, sin -a, 0) (1, 0, -cx) (1, 0, -tx) - # (0, 1, cy) * (-sin -a, cos -a, 0) * (0, 1, -cy) * (0, 1, -ty) - # (0, 0, 1) ( 0, 0, 1) (0, 0, 1) (0, 0, 1) - - # In any case, the final translation may be updated at the end to - # compensate for the expand flag. - - w, h = self.size - - if translate is None: - post_trans = (0, 0) - else: - post_trans = translate - if center is None: - center = (w / 2, h / 2) - - angle = -math.radians(angle) - matrix = [ - round(math.cos(angle), 15), - round(math.sin(angle), 15), - 0.0, - round(-math.sin(angle), 15), - round(math.cos(angle), 15), - 0.0, - ] - - def transform(x: float, y: float, matrix: list[float]) -> tuple[float, float]: - a, b, c, d, e, f = matrix - return a * x + b * y + c, d * x + e * y + f - - matrix[2], matrix[5] = transform( - -center[0] - post_trans[0], -center[1] - post_trans[1], matrix - ) - matrix[2] += center[0] - matrix[5] += center[1] - - if expand: - # calculate output size - xx = [] - yy = [] - for x, y in ((0, 0), (w, 0), (w, h), (0, h)): - transformed_x, transformed_y = transform(x, y, matrix) - xx.append(transformed_x) - yy.append(transformed_y) - nw = math.ceil(max(xx)) - math.floor(min(xx)) - nh = math.ceil(max(yy)) - math.floor(min(yy)) - - # We multiply a translation matrix from the right. Because of its - # special form, this is the same as taking the image of the - # translation vector as new translation vector. - matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix) - w, h = nw, nh - - return self.transform( - (w, h), Transform.AFFINE, matrix, resample, fillcolor=fillcolor - ) - - def save( - self, fp: StrOrBytesPath | IO[bytes], format: str | None = None, **params: Any - ) -> None: - """ - Saves this image under the given filename. If no format is - specified, the format to use is determined from the filename - extension, if possible. - - Keyword options can be used to provide additional instructions - to the writer. If a writer doesn't recognise an option, it is - silently ignored. The available options are described in the - :doc:`image format documentation - <../handbook/image-file-formats>` for each writer. - - You can use a file object instead of a filename. In this case, - you must always specify the format. The file object must - implement the ``seek``, ``tell``, and ``write`` - methods, and be opened in binary mode. - - :param fp: A filename (string), os.PathLike object or file object. - :param format: Optional format override. If omitted, the - format to use is determined from the filename extension. - If a file object was used instead of a filename, this - parameter should always be used. - :param params: Extra parameters to the image writer. These can also be - set on the image itself through ``encoderinfo``. This is useful when - saving multiple images:: - - # Saving XMP data to a single image - from PIL import Image - red = Image.new("RGB", (1, 1), "#f00") - red.save("out.mpo", xmp=b"test") - - # Saving XMP data to the second frame of an image - from PIL import Image - black = Image.new("RGB", (1, 1)) - red = Image.new("RGB", (1, 1), "#f00") - red.encoderinfo = {"xmp": b"test"} - black.save("out.mpo", save_all=True, append_images=[red]) - :returns: None - :exception ValueError: If the output format could not be determined - from the file name. Use the format option to solve this. - :exception OSError: If the file could not be written. The file - may have been created, and may contain partial data. - """ - - filename: str | bytes = "" - open_fp = False - if is_path(fp): - filename = os.fspath(fp) - open_fp = True - elif fp == sys.stdout: - try: - fp = sys.stdout.buffer - except AttributeError: - pass - if not filename and hasattr(fp, "name") and is_path(fp.name): - # only set the name for metadata purposes - filename = os.fspath(fp.name) - - if format: - preinit() - else: - filename_ext = os.path.splitext(filename)[1].lower() - ext = ( - filename_ext.decode() - if isinstance(filename_ext, bytes) - else filename_ext - ) - - # Try importing only the plugin for this extension first - if not _import_plugin_for_extension(ext): - preinit() - - if ext not in EXTENSION: - init() - try: - format = EXTENSION[ext] - except KeyError as e: - msg = f"unknown file extension: {ext}" - raise ValueError(msg) from e - - from . import ImageFile - - # may mutate self! - if isinstance(self, ImageFile.ImageFile) and os.path.abspath( - filename - ) == os.path.abspath(self.filename): - self._ensure_mutable() - else: - self.load() - - save_all = params.pop("save_all", None) - self._default_encoderinfo = params - encoderinfo = getattr(self, "encoderinfo", {}) - self._attach_default_encoderinfo(self) - self.encoderconfig: tuple[Any, ...] = () - - if format.upper() not in SAVE: - init() - if save_all or ( - save_all is None - and params.get("append_images") - and format.upper() in SAVE_ALL - ): - save_handler = SAVE_ALL[format.upper()] - else: - save_handler = SAVE[format.upper()] - - created = False - if open_fp: - created = not os.path.exists(filename) - if params.get("append", False): - # Open also for reading ("+"), because TIFF save_all - # writer needs to go back and edit the written data. - fp = builtins.open(filename, "r+b") - else: - fp = builtins.open(filename, "w+b") - else: - fp = cast(IO[bytes], fp) - - try: - save_handler(self, fp, filename) - except Exception: - if open_fp: - fp.close() - if created: - try: - os.remove(filename) - except PermissionError: - pass - raise - finally: - self.encoderinfo = encoderinfo - if open_fp: - fp.close() - - def _attach_default_encoderinfo(self, im: Image) -> dict[str, Any]: - encoderinfo = getattr(self, "encoderinfo", {}) - self.encoderinfo = {**im._default_encoderinfo, **encoderinfo} - return encoderinfo - - def seek(self, frame: int) -> None: - """ - Seeks to the given frame in this sequence file. If you seek - beyond the end of the sequence, the method raises an - ``EOFError`` exception. When a sequence file is opened, the - library automatically seeks to frame 0. - - See :py:meth:`~PIL.Image.Image.tell`. - - If defined, :attr:`~PIL.Image.Image.n_frames` refers to the - number of available frames. - - :param frame: Frame number, starting at 0. - :exception EOFError: If the call attempts to seek beyond the end - of the sequence. - """ - - # overridden by file handlers - if frame != 0: - msg = "no more images in file" - raise EOFError(msg) - - def show(self, title: str | None = None) -> None: - """ - Displays this image. This method is mainly intended for debugging purposes. - - This method calls :py:func:`PIL.ImageShow.show` internally. You can use - :py:func:`PIL.ImageShow.register` to override its default behaviour. - - The image is first saved to a temporary file. By default, it will be in - PNG format. - - On Unix, the image is then opened using the **xdg-open**, **display**, - **gm**, **eog** or **xv** utility, depending on which one can be found. - - On macOS, the image is opened with the native Preview application. - - On Windows, the image is opened with the standard PNG display utility. - - :param title: Optional title to use for the image window, where possible. - """ - - from . import ImageShow - - ImageShow.show(self, title) - - def split(self) -> tuple[Image, ...]: - """ - Split this image into individual bands. This method returns a - tuple of individual image bands from an image. For example, - splitting an "RGB" image creates three new images each - containing a copy of one of the original bands (red, green, - blue). - - If you need only one band, :py:meth:`~PIL.Image.Image.getchannel` - method can be more convenient and faster. - - :returns: A tuple containing bands. - """ - - self.load() - if self.im.bands == 1: - return (self.copy(),) - return tuple(map(self._new, self.im.split())) - - def getchannel(self, channel: int | str) -> Image: - """ - Returns an image containing a single channel of the source image. - - :param channel: What channel to return. Could be index - (0 for "R" channel of "RGB") or channel name - ("A" for alpha channel of "RGBA"). - :returns: An image in "L" mode. - - .. versionadded:: 4.3.0 - """ - self.load() - - if isinstance(channel, str): - try: - channel = self.getbands().index(channel) - except ValueError as e: - msg = f'The image has no channel "{channel}"' - raise ValueError(msg) from e - - return self._new(self.im.getband(channel)) - - def tell(self) -> int: - """ - Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. - - If defined, :attr:`~PIL.Image.Image.n_frames` refers to the - number of available frames. - - :returns: Frame number, starting with 0. - """ - return 0 - - def thumbnail( - self, - size: tuple[float, float], - resample: Resampling = Resampling.BICUBIC, - reducing_gap: float | None = 2.0, - ) -> None: - """ - Make this image into a thumbnail. This method modifies the - image to contain a thumbnail version of itself, no larger than - the given size. This method calculates an appropriate thumbnail - size to preserve the aspect of the image, calls the - :py:meth:`~PIL.Image.Image.draft` method to configure the file reader - (where applicable), and finally resizes the image. - - Note that this function modifies the :py:class:`~PIL.Image.Image` - object in place. If you need to use the full resolution image as well, - apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original - image. - - :param size: The requested size in pixels, as a 2-tuple: - (width, height). - :param resample: Optional resampling filter. This can be one - of :py:data:`Resampling.NEAREST`, :py:data:`Resampling.BOX`, - :py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`, - :py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`. - If omitted, it defaults to :py:data:`Resampling.BICUBIC`. - (was :py:data:`Resampling.NEAREST` prior to version 2.5.0). - See: :ref:`concept-filters`. - :param reducing_gap: Apply optimization by resizing the image - in two steps. First, reducing the image by integer times - using :py:meth:`~PIL.Image.Image.reduce` or - :py:meth:`~PIL.Image.Image.draft` for JPEG images. - Second, resizing using regular resampling. The last step - changes size no less than by ``reducing_gap`` times. - ``reducing_gap`` may be None (no first step is performed) - or should be greater than 1.0. The bigger ``reducing_gap``, - the closer the result to the fair resampling. - The smaller ``reducing_gap``, the faster resizing. - With ``reducing_gap`` greater or equal to 3.0, the result is - indistinguishable from fair resampling in most cases. - The default value is 2.0 (very close to fair resampling - while still being faster in many cases). - :returns: None - """ - - provided_size = tuple(map(math.floor, size)) - - def preserve_aspect_ratio() -> tuple[int, int] | None: - def round_aspect(number: float, key: Callable[[int], float]) -> int: - return max(min(math.floor(number), math.ceil(number), key=key), 1) - - x, y = provided_size - if x >= self.width and y >= self.height: - return None - - aspect = self.width / self.height - if x / y >= aspect: - x = round_aspect(y * aspect, key=lambda n: abs(aspect - n / y)) - else: - y = round_aspect( - x / aspect, key=lambda n: 0 if n == 0 else abs(aspect - x / n) - ) - return x, y - - preserved_size = preserve_aspect_ratio() - if preserved_size is None: - return - final_size = preserved_size - - box = None - if reducing_gap is not None: - res = self.draft( - None, (int(size[0] * reducing_gap), int(size[1] * reducing_gap)) - ) - if res is not None: - box = res[1] - - if self.size != final_size: - im = self.resize(final_size, resample, box=box, reducing_gap=reducing_gap) - - self.im = im.im - self._size = final_size - self._mode = self.im.mode - - self.readonly = 0 - - # FIXME: the different transform methods need further explanation - # instead of bloating the method docs, add a separate chapter. - def transform( - self, - size: tuple[int, int], - method: Transform | ImageTransformHandler | SupportsGetData, - data: Sequence[Any] | None = None, - resample: int = Resampling.NEAREST, - fill: int = 1, - fillcolor: float | tuple[float, ...] | str | None = None, - ) -> Image: - """ - Transforms this image. This method creates a new image with the - given size, and the same mode as the original, and copies data - to the new image using the given transform. - - :param size: The output size in pixels, as a 2-tuple: - (width, height). - :param method: The transformation method. This is one of - :py:data:`Transform.EXTENT` (cut out a rectangular subregion), - :py:data:`Transform.AFFINE` (affine transform), - :py:data:`Transform.PERSPECTIVE` (perspective transform), - :py:data:`Transform.QUAD` (map a quadrilateral to a rectangle), or - :py:data:`Transform.MESH` (map a number of source quadrilaterals - in one operation). - - It may also be an :py:class:`~PIL.Image.ImageTransformHandler` - object:: - - class Example(Image.ImageTransformHandler): - def transform(self, size, data, resample, fill=1): - # Return result - - Implementations of :py:class:`~PIL.Image.ImageTransformHandler` - for some of the :py:class:`Transform` methods are provided - in :py:mod:`~PIL.ImageTransform`. - - It may also be an object with a ``method.getdata`` method - that returns a tuple supplying new ``method`` and ``data`` values:: - - class Example: - def getdata(self): - method = Image.Transform.EXTENT - data = (0, 0, 100, 100) - return method, data - :param data: Extra data to the transformation method. - :param resample: Optional resampling filter. It can be one of - :py:data:`Resampling.NEAREST` (use nearest neighbour), - :py:data:`Resampling.BILINEAR` (linear interpolation in a 2x2 - environment), or :py:data:`Resampling.BICUBIC` (cubic spline - interpolation in a 4x4 environment). If omitted, or if the image - has mode "1" or "P", it is set to :py:data:`Resampling.NEAREST`. - See: :ref:`concept-filters`. - :param fill: If ``method`` is an - :py:class:`~PIL.Image.ImageTransformHandler` object, this is one of - the arguments passed to it. Otherwise, it is unused. - :param fillcolor: Optional fill color for the area outside the - transform in the output image. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - if self.mode in ("LA", "RGBA") and resample != Resampling.NEAREST: - return ( - self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) - .transform(size, method, data, resample, fill, fillcolor) - .convert(self.mode) - ) - - if isinstance(method, ImageTransformHandler): - return method.transform(size, self, resample=resample, fill=fill) - - if hasattr(method, "getdata"): - # compatibility w. old-style transform objects - method, data = method.getdata() - - if data is None: - msg = "missing method data" - raise ValueError(msg) - - im = new(self.mode, size, fillcolor) - if self.mode == "P" and self.palette: - im.palette = self.palette.copy() - im.info = self.info.copy() - if method == Transform.MESH: - # list of quads - for box, quad in data: - im.__transformer( - box, self, Transform.QUAD, quad, resample, fillcolor is None - ) - else: - im.__transformer( - (0, 0) + size, self, method, data, resample, fillcolor is None - ) - - return im - - def __transformer( - self, - box: tuple[int, int, int, int], - image: Image, - method: Transform, - data: Sequence[float], - resample: int = Resampling.NEAREST, - fill: bool = True, - ) -> None: - w = box[2] - box[0] - h = box[3] - box[1] - - if method == Transform.AFFINE: - data = data[:6] - - elif method == Transform.EXTENT: - # convert extent to an affine transform - x0, y0, x1, y1 = data - xs = (x1 - x0) / w - ys = (y1 - y0) / h - method = Transform.AFFINE - data = (xs, 0, x0, 0, ys, y0) - - elif method == Transform.PERSPECTIVE: - data = data[:8] - - elif method == Transform.QUAD: - # quadrilateral warp. data specifies the four corners - # given as NW, SW, SE, and NE. - nw = data[:2] - sw = data[2:4] - se = data[4:6] - ne = data[6:8] - x0, y0 = nw - As = 1.0 / w - At = 1.0 / h - data = ( - x0, - (ne[0] - x0) * As, - (sw[0] - x0) * At, - (se[0] - sw[0] - ne[0] + x0) * As * At, - y0, - (ne[1] - y0) * As, - (sw[1] - y0) * At, - (se[1] - sw[1] - ne[1] + y0) * As * At, - ) - - else: - msg = "unknown transformation method" - raise ValueError(msg) - - if resample not in ( - Resampling.NEAREST, - Resampling.BILINEAR, - Resampling.BICUBIC, - ): - if resample in (Resampling.BOX, Resampling.HAMMING, Resampling.LANCZOS): - unusable: dict[int, str] = { - Resampling.BOX: "Image.Resampling.BOX", - Resampling.HAMMING: "Image.Resampling.HAMMING", - Resampling.LANCZOS: "Image.Resampling.LANCZOS", - } - msg = unusable[resample] + f" ({resample}) cannot be used." - else: - msg = f"Unknown resampling filter ({resample})." - - filters = [ - f"{filter[1]} ({filter[0]})" - for filter in ( - (Resampling.NEAREST, "Image.Resampling.NEAREST"), - (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), - (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), - ) - ] - msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" - raise ValueError(msg) - - image.load() - - self.load() - - if image.mode in ("1", "P"): - resample = Resampling.NEAREST - - self.im.transform(box, image.im, method, data, resample, fill) - - def transpose(self, method: Transpose) -> Image: - """ - Transpose image (flip or rotate in 90 degree steps) - - :param method: One of :py:data:`Transpose.FLIP_LEFT_RIGHT`, - :py:data:`Transpose.FLIP_TOP_BOTTOM`, :py:data:`Transpose.ROTATE_90`, - :py:data:`Transpose.ROTATE_180`, :py:data:`Transpose.ROTATE_270`, - :py:data:`Transpose.TRANSPOSE` or :py:data:`Transpose.TRANSVERSE`. - :returns: Returns a flipped or rotated copy of this image. - """ - - self.load() - return self._new(self.im.transpose(method)) - - def effect_spread(self, distance: int) -> Image: - """ - Randomly spread pixels in an image. - - :param distance: Distance to spread pixels. - """ - self.load() - return self._new(self.im.effect_spread(distance)) - - def toqimage(self) -> ImageQt.ImageQt: - """Returns a QImage copy of this image""" - from . import ImageQt - - if not ImageQt.qt_is_installed: - msg = "Qt bindings are not installed" - raise ImportError(msg) - return ImageQt.toqimage(self) - - def toqpixmap(self) -> ImageQt.QPixmap: - """Returns a QPixmap copy of this image""" - from . import ImageQt - - if not ImageQt.qt_is_installed: - msg = "Qt bindings are not installed" - raise ImportError(msg) - return ImageQt.toqpixmap(self) - - -# -------------------------------------------------------------------- -# Abstract handlers. - - -class ImagePointHandler(abc.ABC): - """ - Used as a mixin by point transforms - (for use with :py:meth:`~PIL.Image.Image.point`) - """ - - @abc.abstractmethod - def point(self, im: Image) -> Image: - pass - - -class ImageTransformHandler(abc.ABC): - """ - Used as a mixin by geometry transforms - (for use with :py:meth:`~PIL.Image.Image.transform`) - """ - - @abc.abstractmethod - def transform( - self, - size: tuple[int, int], - image: Image, - **options: Any, - ) -> Image: - pass - - -# -------------------------------------------------------------------- -# Factories - - -def _check_size(size: Any) -> None: - """ - Common check to enforce type and sanity check on size tuples - - :param size: Should be a 2 tuple of (width, height) - :returns: None, or raises a ValueError - """ - - if not isinstance(size, (list, tuple)): - msg = "Size must be a list or tuple" - raise ValueError(msg) - if len(size) != 2: - msg = "Size must be a sequence of length 2" - raise ValueError(msg) - if size[0] < 0 or size[1] < 0: - msg = "Width and height must be >= 0" - raise ValueError(msg) - - -def new( - mode: str, - size: tuple[int, int] | list[int], - color: float | tuple[float, ...] | str | None = 0, -) -> Image: - """ - Creates a new image with the given mode and size. - - :param mode: The mode to use for the new image. See: - :ref:`concept-modes`. - :param size: A 2-tuple, containing (width, height) in pixels. - :param color: What color to use for the image. Default is black. If given, - this should be a single integer or floating point value for single-band - modes, and a tuple for multi-band modes (one value per band). When - creating RGB or HSV images, you can also use color strings as supported - by the ImageColor module. See :ref:`colors` for more information. If the - color is None, the image is not initialised. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - _check_size(size) - - if color is None: - # don't initialize - return Image()._new(core.new(mode, size)) - - if isinstance(color, str): - # css3-style specifier - - from . import ImageColor - - color = ImageColor.getcolor(color, mode) - - im = Image() - if ( - mode == "P" - and isinstance(color, (list, tuple)) - and all(isinstance(i, int) for i in color) - ): - color_ints: tuple[int, ...] = cast(tuple[int, ...], tuple(color)) - if len(color_ints) == 3 or len(color_ints) == 4: - # RGB or RGBA value for a P image - from . import ImagePalette - - im.palette = ImagePalette.ImagePalette() - color = im.palette.getcolor(color_ints) - return im._new(core.fill(mode, size, color)) - - -def frombytes( - mode: str, - size: tuple[int, int], - data: bytes | bytearray | SupportsArrayInterface, - decoder_name: str = "raw", - *args: Any, -) -> Image: - """ - Creates a copy of an image memory from pixel data in a buffer. - - In its simplest form, this function takes three arguments - (mode, size, and unpacked pixel data). - - You can also use any pixel decoder supported by PIL. For more - information on available decoders, see the section - :ref:`Writing Your Own File Codec `. - - Note that this function decodes pixel data only, not entire images. - If you have an entire image in a string, wrap it in a - :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load - it. - - :param mode: The image mode. See: :ref:`concept-modes`. - :param size: The image size. - :param data: A byte buffer containing raw data for the given mode. - :param decoder_name: What decoder to use. - :param args: Additional parameters for the given decoder. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - _check_size(size) - - im = new(mode, size) - if im.width != 0 and im.height != 0: - decoder_args: Any = args - if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple): - # may pass tuple instead of argument list - decoder_args = decoder_args[0] - - if decoder_name == "raw" and decoder_args == (): - decoder_args = mode - - im.frombytes(data, decoder_name, decoder_args) - return im - - -def frombuffer( - mode: str, - size: tuple[int, int], - data: bytes | SupportsArrayInterface, - decoder_name: str = "raw", - *args: Any, -) -> Image: - """ - Creates an image memory referencing pixel data in a byte buffer. - - This function is similar to :py:func:`~PIL.Image.frombytes`, but uses data - in the byte buffer, where possible. This means that changes to the - original buffer object are reflected in this image). Not all modes can - share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK". - - Note that this function decodes pixel data only, not entire images. - If you have an entire image file in a string, wrap it in a - :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load it. - - The default parameters used for the "raw" decoder differs from that used for - :py:func:`~PIL.Image.frombytes`. This is a bug, and will probably be fixed in a - future release. The current release issues a warning if you do this; to disable - the warning, you should provide the full set of parameters. See below for details. - - :param mode: The image mode. See: :ref:`concept-modes`. - :param size: The image size. - :param data: A bytes or other buffer object containing raw - data for the given mode. - :param decoder_name: What decoder to use. - :param args: Additional parameters for the given decoder. For the - default encoder ("raw"), it's recommended that you provide the - full set of parameters:: - - frombuffer(mode, size, data, "raw", mode, 0, 1) - - :returns: An :py:class:`~PIL.Image.Image` object. - - .. versionadded:: 1.1.4 - """ - - _check_size(size) - - # may pass tuple instead of argument list - if len(args) == 1 and isinstance(args[0], tuple): - args = args[0] - - if decoder_name == "raw": - if args == (): - args = mode, 0, 1 - if args[0] in _MAPMODES: - im = new(mode, (0, 0)) - im = im._new(core.map_buffer(data, size, decoder_name, 0, args)) - if mode == "P": - from . import ImagePalette - - im.palette = ImagePalette.ImagePalette("RGB", im.im.getpalette("RGB")) - im.readonly = 1 - return im - - return frombytes(mode, size, data, decoder_name, args) - - -class SupportsArrayInterface(Protocol): - """ - An object that has an ``__array_interface__`` dictionary. - """ - - @property - def __array_interface__(self) -> dict[str, Any]: - raise NotImplementedError() - - -class SupportsArrowArrayInterface(Protocol): - """ - An object that has an ``__arrow_c_array__`` method corresponding to the arrow c - data interface. - """ - - def __arrow_c_array__( - self, requested_schema: "PyCapsule" = None # type: ignore[name-defined] # noqa: F821, UP037 - ) -> tuple["PyCapsule", "PyCapsule"]: # type: ignore[name-defined] # noqa: F821, UP037 - raise NotImplementedError() - - -def fromarray(obj: SupportsArrayInterface, mode: str | None = None) -> Image: - """ - Creates an image memory from an object exporting the array interface - (using the buffer protocol):: - - from PIL import Image - import numpy as np - a = np.zeros((5, 5)) - im = Image.fromarray(a) - - If ``obj`` is not contiguous, then the ``tobytes`` method is called - and :py:func:`~PIL.Image.frombuffer` is used. - - In the case of NumPy, be aware that Pillow modes do not always correspond - to NumPy dtypes. Pillow modes only offer 1-bit pixels, 8-bit pixels, - 32-bit signed integer pixels, and 32-bit floating point pixels. - - Pillow images can also be converted to arrays:: - - from PIL import Image - import numpy as np - im = Image.open("hopper.jpg") - a = np.asarray(im) - - When converting Pillow images to arrays however, only pixel values are - transferred. This means that P and PA mode images will lose their palette. - - :param obj: Object with array interface - :param mode: Optional mode to use when reading ``obj``. Since pixel values do not - contain information about palettes or color spaces, this can be used to place - grayscale L mode data within a P mode image, or read RGB data as YCbCr for - example. - - See: :ref:`concept-modes` for general information about modes. - :returns: An image object. - - .. versionadded:: 1.1.6 - """ - arr = obj.__array_interface__ - shape = arr["shape"] - ndim = len(shape) - strides = arr.get("strides", None) - try: - typekey = (1, 1) + shape[2:], arr["typestr"] - except KeyError as e: - if mode is not None: - typekey = None - color_modes: list[str] = [] - else: - msg = "Cannot handle this data type" - raise TypeError(msg) from e - if typekey is not None: - try: - typemode, rawmode, color_modes = _fromarray_typemap[typekey] - except KeyError as e: - typekey_shape, typestr = typekey - msg = f"Cannot handle this data type: {typekey_shape}, {typestr}" - raise TypeError(msg) from e - if mode is not None: - if mode != typemode and mode not in color_modes: - deprecate("'mode' parameter for changing data types", 13) - rawmode = mode - else: - mode = typemode - if mode in ["1", "L", "I", "P", "F"]: - ndmax = 2 - elif mode == "RGB": - ndmax = 3 - else: - ndmax = 4 - if ndim > ndmax: - msg = f"Too many dimensions: {ndim} > {ndmax}." - raise ValueError(msg) - - size = 1 if ndim == 1 else shape[1], shape[0] - if strides is not None: - if hasattr(obj, "tobytes"): - obj = obj.tobytes() - elif hasattr(obj, "tostring"): - obj = obj.tostring() - else: - msg = "'strides' requires either tobytes() or tostring()" - raise ValueError(msg) - - return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) - - -def fromarrow( - obj: SupportsArrowArrayInterface, mode: str, size: tuple[int, int] -) -> Image: - """Creates an image with zero-copy shared memory from an object exporting - the arrow_c_array interface protocol:: - - from PIL import Image - import pyarrow as pa - arr = pa.array([0]*(5*5*4), type=pa.uint8()) - im = Image.fromarrow(arr, 'RGBA', (5, 5)) - - If the data representation of the ``obj`` is not compatible with - Pillow internal storage, a ValueError is raised. - - Pillow images can also be converted to Arrow objects:: - - from PIL import Image - import pyarrow as pa - im = Image.open('hopper.jpg') - arr = pa.array(im) - - As with array support, when converting Pillow images to arrays, - only pixel values are transferred. This means that P and PA mode - images will lose their palette. - - :param obj: Object with an arrow_c_array interface - :param mode: Image mode. - :param size: Image size. This must match the storage of the arrow object. - :returns: An Image object - - Note that according to the Arrow spec, both the producer and the - consumer should consider the exported array to be immutable, as - unsynchronized updates will potentially cause inconsistent data. - - See: :ref:`arrow-support` for more detailed information - - .. versionadded:: 11.2.1 - - """ - if not hasattr(obj, "__arrow_c_array__"): - msg = "arrow_c_array interface not found" - raise ValueError(msg) - - schema_capsule, array_capsule = obj.__arrow_c_array__() - _im = core.new_arrow(mode, size, schema_capsule, array_capsule) - if _im: - return Image()._new(_im) - - msg = "new_arrow returned None without an exception" - raise ValueError(msg) - - -def fromqimage(im: ImageQt.QImage) -> ImageFile.ImageFile: - """Creates an image instance from a QImage image""" - from . import ImageQt - - if not ImageQt.qt_is_installed: - msg = "Qt bindings are not installed" - raise ImportError(msg) - return ImageQt.fromqimage(im) - - -def fromqpixmap(im: ImageQt.QPixmap) -> ImageFile.ImageFile: - """Creates an image instance from a QPixmap image""" - from . import ImageQt - - if not ImageQt.qt_is_installed: - msg = "Qt bindings are not installed" - raise ImportError(msg) - return ImageQt.fromqpixmap(im) - - -_fromarray_typemap = { - # (shape, typestr) => mode, rawmode, color modes - # first two members of shape are set to one - ((1, 1), "|b1"): ("1", "1;8", []), - ((1, 1), "|u1"): ("L", "L", ["P"]), - ((1, 1), "|i1"): ("I", "I;8", []), - ((1, 1), "u2"): ("I", "I;16B", []), - ((1, 1), "i2"): ("I", "I;16BS", []), - ((1, 1), "u4"): ("I", "I;32B", []), - ((1, 1), "i4"): ("I", "I;32BS", []), - ((1, 1), "f4"): ("F", "F;32BF", []), - ((1, 1), "f8"): ("F", "F;64BF", []), - ((1, 1, 2), "|u1"): ("LA", "LA", ["La", "PA"]), - ((1, 1, 3), "|u1"): ("RGB", "RGB", ["YCbCr", "LAB", "HSV"]), - ((1, 1, 4), "|u1"): ("RGBA", "RGBA", ["RGBa", "RGBX", "CMYK"]), - # shortcuts: - ((1, 1), f"{_ENDIAN}i4"): ("I", "I", []), - ((1, 1), f"{_ENDIAN}f4"): ("F", "F", []), -} - - -def _decompression_bomb_check(size: tuple[int, int]) -> None: - if MAX_IMAGE_PIXELS is None: - return - - pixels = max(1, size[0]) * max(1, size[1]) - - if pixels > 2 * MAX_IMAGE_PIXELS: - msg = ( - f"Image size ({pixels} pixels) exceeds limit of {2 * MAX_IMAGE_PIXELS} " - "pixels, could be decompression bomb DOS attack." - ) - raise DecompressionBombError(msg) - - if pixels > MAX_IMAGE_PIXELS: - warnings.warn( - f"Image size ({pixels} pixels) exceeds limit of {MAX_IMAGE_PIXELS} pixels, " - "could be decompression bomb DOS attack.", - DecompressionBombWarning, - ) - - -def open( - fp: StrOrBytesPath | IO[bytes], - mode: Literal["r"] = "r", - formats: list[str] | tuple[str, ...] | None = None, -) -> ImageFile.ImageFile: - """ - Opens and identifies the given image file. - - This is a lazy operation; this function identifies the file, but - the file remains open and the actual image data is not read from - the file until you try to process the data (or call the - :py:meth:`~PIL.Image.Image.load` method). See - :py:func:`~PIL.Image.new`. See :ref:`file-handling`. - - :param fp: A filename (string), os.PathLike object or a file object. - The file object must implement ``file.read``, - ``file.seek``, and ``file.tell`` methods, - and be opened in binary mode. The file object will also seek to zero - before reading. - :param mode: The mode. If given, this argument must be "r". - :param formats: A list or tuple of formats to attempt to load the file in. - This can be used to restrict the set of formats checked. - Pass ``None`` to try all supported formats. You can print the set of - available formats by running ``python3 -m PIL`` or using - the :py:func:`PIL.features.pilinfo` function. - :returns: An :py:class:`~PIL.Image.Image` object. - :exception FileNotFoundError: If the file cannot be found. - :exception PIL.UnidentifiedImageError: If the image cannot be opened and - identified. - :exception ValueError: If the ``mode`` is not "r", or if a ``StringIO`` - instance is used for ``fp``. - :exception TypeError: If ``formats`` is not ``None``, a list or a tuple. - """ - - if mode != "r": - msg = f"bad mode {repr(mode)}" # type: ignore[unreachable] - raise ValueError(msg) - elif isinstance(fp, io.StringIO): - msg = ( # type: ignore[unreachable] - "StringIO cannot be used to open an image. " - "Binary data must be used instead." - ) - raise ValueError(msg) - - if formats is None: - formats = ID - elif not isinstance(formats, (list, tuple)): - msg = "formats must be a list or tuple" # type: ignore[unreachable] - raise TypeError(msg) - - exclusive_fp = False - filename: str | bytes = "" - if is_path(fp): - filename = os.fspath(fp) - fp = builtins.open(filename, "rb") - exclusive_fp = True - else: - fp = cast(IO[bytes], fp) - - try: - fp.seek(0) - except (AttributeError, io.UnsupportedOperation): - fp = io.BytesIO(fp.read()) - exclusive_fp = True - - prefix = fp.read(16) - - # Try to import just the plugin needed for this file extension - # before falling back to preinit() which imports common plugins - ext = os.path.splitext(filename)[1] if filename else "" - if not _import_plugin_for_extension(ext): - preinit() - - warning_messages: list[str] = [] - - def _open_core( - fp: IO[bytes], - filename: str | bytes, - prefix: bytes, - formats: list[str] | tuple[str, ...], - ) -> ImageFile.ImageFile | None: - for i in formats: - i = i.upper() - if i not in OPEN: - init() - try: - factory, accept = OPEN[i] - result = not accept or accept(prefix) - if isinstance(result, str): - warning_messages.append(result) - elif result: - fp.seek(0) - im = factory(fp, filename) - _decompression_bomb_check(im.size) - return im - except (SyntaxError, IndexError, TypeError, struct.error) as e: - if WARN_POSSIBLE_FORMATS: - warning_messages.append(i + " opening failed. " + str(e)) - except BaseException: - if exclusive_fp: - fp.close() - raise - return None - - im = _open_core(fp, filename, prefix, formats) - - if im is None and formats is ID: - # Try preinit (few common plugins) then init (all plugins) - for loader in (preinit, init): - checked_formats = ID.copy() - loader() - if formats != checked_formats: - im = _open_core( - fp, - filename, - prefix, - tuple(f for f in formats if f not in checked_formats), - ) - if im is not None: - break - - if im: - im._exclusive_fp = exclusive_fp - return im - - if exclusive_fp: - fp.close() - for message in warning_messages: - warnings.warn(message) - msg = "cannot identify image file %r" % (filename if filename else fp) - raise UnidentifiedImageError(msg) - - -# -# Image processing. - - -def alpha_composite(im1: Image, im2: Image) -> Image: - """ - Alpha composite im2 over im1. - - :param im1: The first image. Must have mode RGBA or LA. - :param im2: The second image. Must have the same mode and size as the first image. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - im1.load() - im2.load() - return im1._new(core.alpha_composite(im1.im, im2.im)) - - -def blend(im1: Image, im2: Image, alpha: float) -> Image: - """ - Creates a new image by interpolating between two input images, using - a constant alpha:: - - out = image1 * (1.0 - alpha) + image2 * alpha - - :param im1: The first image. - :param im2: The second image. Must have the same mode and size as - the first image. - :param alpha: The interpolation alpha factor. If alpha is 0.0, a - copy of the first image is returned. If alpha is 1.0, a copy of - the second image is returned. There are no restrictions on the - alpha value. If necessary, the result is clipped to fit into - the allowed output range. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - im1.load() - im2.load() - return im1._new(core.blend(im1.im, im2.im, alpha)) - - -def composite(image1: Image, image2: Image, mask: Image) -> Image: - """ - Create composite image by blending images using a transparency mask. - - :param image1: The first image. - :param image2: The second image. Must have the same mode and - size as the first image. - :param mask: A mask image. This image can have mode - "1", "L", or "RGBA", and must have the same size as the - other two images. - """ - - image = image2.copy() - image.paste(image1, None, mask) - return image - - -def eval(image: Image, *args: Callable[[int], float]) -> Image: - """ - Applies the function (which should take one argument) to each pixel - in the given image. If the image has more than one band, the same - function is applied to each band. Note that the function is - evaluated once for each possible pixel value, so you cannot use - random components or other generators. - - :param image: The input image. - :param function: A function object, taking one integer argument. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - return image.point(args[0]) - - -def merge(mode: str, bands: Sequence[Image]) -> Image: - """ - Merge a set of single band images into a new multiband image. - - :param mode: The mode to use for the output image. See: - :ref:`concept-modes`. - :param bands: A sequence containing one single-band image for - each band in the output image. All bands must have the - same size. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - if getmodebands(mode) != len(bands) or "*" in mode: - msg = "wrong number of bands" - raise ValueError(msg) - for band in bands[1:]: - if band.mode != getmodetype(mode): - msg = "mode mismatch" - raise ValueError(msg) - if band.size != bands[0].size: - msg = "size mismatch" - raise ValueError(msg) - for band in bands: - band.load() - return bands[0]._new(core.merge(mode, *[b.im for b in bands])) - - -# -------------------------------------------------------------------- -# Plugin registry - - -def register_open( - id: str, - factory: ( - Callable[[IO[bytes], str | bytes], ImageFile.ImageFile] - | type[ImageFile.ImageFile] - ), - accept: Callable[[bytes], bool | str] | None = None, -) -> None: - """ - Register an image file plugin. This function should not be used - in application code. - - :param id: An image format identifier. - :param factory: An image file factory method. - :param accept: An optional function that can be used to quickly - reject images having another format. - """ - id = id.upper() - if id not in ID: - ID.append(id) - OPEN[id] = factory, accept - - -def register_mime(id: str, mimetype: str) -> None: - """ - Registers an image MIME type by populating ``Image.MIME``. This function - should not be used in application code. - - ``Image.MIME`` provides a mapping from image format identifiers to mime - formats, but :py:meth:`~PIL.ImageFile.ImageFile.get_format_mimetype` can - provide a different result for specific images. - - :param id: An image format identifier. - :param mimetype: The image MIME type for this format. - """ - MIME[id.upper()] = mimetype - - -def register_save( - id: str, driver: Callable[[Image, IO[bytes], str | bytes], None] -) -> None: - """ - Registers an image save function. This function should not be - used in application code. - - :param id: An image format identifier. - :param driver: A function to save images in this format. - """ - SAVE[id.upper()] = driver - - -def register_save_all( - id: str, driver: Callable[[Image, IO[bytes], str | bytes], None] -) -> None: - """ - Registers an image function to save all the frames - of a multiframe format. This function should not be - used in application code. - - :param id: An image format identifier. - :param driver: A function to save images in this format. - """ - SAVE_ALL[id.upper()] = driver - - -def register_extension(id: str, extension: str) -> None: - """ - Registers an image extension. This function should not be - used in application code. - - :param id: An image format identifier. - :param extension: An extension used for this format. - """ - EXTENSION[extension.lower()] = id.upper() - - -def register_extensions(id: str, extensions: list[str]) -> None: - """ - Registers image extensions. This function should not be - used in application code. - - :param id: An image format identifier. - :param extensions: A list of extensions used for this format. - """ - for extension in extensions: - register_extension(id, extension) - - -def registered_extensions() -> dict[str, str]: - """ - Returns a dictionary containing all file extensions belonging - to registered plugins - """ - init() - return EXTENSION - - -def register_decoder(name: str, decoder: type[ImageFile.PyDecoder]) -> None: - """ - Registers an image decoder. This function should not be - used in application code. - - :param name: The name of the decoder - :param decoder: An ImageFile.PyDecoder object - - .. versionadded:: 4.1.0 - """ - DECODERS[name] = decoder - - -def register_encoder(name: str, encoder: type[ImageFile.PyEncoder]) -> None: - """ - Registers an image encoder. This function should not be - used in application code. - - :param name: The name of the encoder - :param encoder: An ImageFile.PyEncoder object - - .. versionadded:: 4.1.0 - """ - ENCODERS[name] = encoder - - -# -------------------------------------------------------------------- -# Simple display support. - - -def _show(image: Image, **options: Any) -> None: - from . import ImageShow - - deprecate("Image._show", 13, "ImageShow.show") - ImageShow.show(image, **options) - - -# -------------------------------------------------------------------- -# Effects - - -def effect_mandelbrot( - size: tuple[int, int], extent: tuple[float, float, float, float], quality: int -) -> Image: - """ - Generate a Mandelbrot set covering the given extent. - - :param size: The requested size in pixels, as a 2-tuple: - (width, height). - :param extent: The extent to cover, as a 4-tuple: - (x0, y0, x1, y1). - :param quality: Quality. - """ - return Image()._new(core.effect_mandelbrot(size, extent, quality)) - - -def effect_noise(size: tuple[int, int], sigma: float) -> Image: - """ - Generate Gaussian noise centered around 128. - - :param size: The requested size in pixels, as a 2-tuple: - (width, height). - :param sigma: Standard deviation of noise. - """ - return Image()._new(core.effect_noise(size, sigma)) - - -def linear_gradient(mode: str) -> Image: - """ - Generate 256x256 linear gradient from black to white, top to bottom. - - :param mode: Input mode. - """ - return Image()._new(core.linear_gradient(mode)) - - -def radial_gradient(mode: str) -> Image: - """ - Generate 256x256 radial gradient from black to white, centre to edge. - - :param mode: Input mode. - """ - return Image()._new(core.radial_gradient(mode)) - - -# -------------------------------------------------------------------- -# Resources - - -def _apply_env_variables(env: dict[str, str] | None = None) -> None: - env_dict = env if env is not None else os.environ - - for var_name, setter in [ - ("PILLOW_ALIGNMENT", core.set_alignment), - ("PILLOW_BLOCK_SIZE", core.set_block_size), - ("PILLOW_BLOCKS_MAX", core.set_blocks_max), - ]: - if var_name not in env_dict: - continue - - var = env_dict[var_name].lower() - - units = 1 - for postfix, mul in [("k", 1024), ("m", 1024 * 1024)]: - if var.endswith(postfix): - units = mul - var = var[: -len(postfix)] - - try: - var_int = int(var) * units - except ValueError: - warnings.warn(f"{var_name} is not int") - continue - - try: - setter(var_int) - except ValueError as e: - warnings.warn(f"{var_name}: {e}") - - -_apply_env_variables() -atexit.register(core.clear_cache) - - -if TYPE_CHECKING: - _ExifBase = MutableMapping[int, Any] -else: - _ExifBase = MutableMapping - - -class Exif(_ExifBase): - """ - This class provides read and write access to EXIF image data:: - - from PIL import Image - im = Image.open("exif.png") - exif = im.getexif() # Returns an instance of this class - - Information can be read and written, iterated over or deleted:: - - print(exif[274]) # 1 - exif[274] = 2 - for k, v in exif.items(): - print("Tag", k, "Value", v) # Tag 274 Value 2 - del exif[274] - - To access information beyond IFD0, :py:meth:`~PIL.Image.Exif.get_ifd` - returns a dictionary:: - - from PIL import ExifTags - im = Image.open("exif_gps.jpg") - exif = im.getexif() - gps_ifd = exif.get_ifd(ExifTags.IFD.GPSInfo) - print(gps_ifd) - - Other IFDs include ``ExifTags.IFD.Exif``, ``ExifTags.IFD.MakerNote``, - ``ExifTags.IFD.Interop`` and ``ExifTags.IFD.IFD1``. - - :py:mod:`~PIL.ExifTags` also has enum classes to provide names for data:: - - print(exif[ExifTags.Base.Software]) # PIL - print(gps_ifd[ExifTags.GPS.GPSDateStamp]) # 1999:99:99 99:99:99 - """ - - endian: str | None = None - bigtiff = False - _loaded = False - - def __init__(self) -> None: - self._data: dict[int, Any] = {} - self._hidden_data: dict[int, Any] = {} - self._ifds: dict[int, dict[int, Any]] = {} - self._info: TiffImagePlugin.ImageFileDirectory_v2 | None = None - self._loaded_exif: bytes | None = None - - def _fixup(self, value: Any) -> Any: - try: - if len(value) == 1 and isinstance(value, tuple): - return value[0] - except Exception: - pass - return value - - def _fixup_dict(self, src_dict: dict[int, Any]) -> dict[int, Any]: - # Helper function - # returns a dict with any single item tuples/lists as individual values - return {k: self._fixup(v) for k, v in src_dict.items()} - - def _get_ifd_dict( - self, offset: int, group: int | None = None - ) -> dict[int, Any] | None: - try: - # an offset pointer to the location of the nested embedded IFD. - # It should be a long, but may be corrupted. - self.fp.seek(offset) - except (KeyError, TypeError): - return None - else: - from . import TiffImagePlugin - - info = TiffImagePlugin.ImageFileDirectory_v2(self.head, group=group) - info.load(self.fp) - return self._fixup_dict(dict(info)) - - def _get_head(self) -> bytes: - version = b"\x2b" if self.bigtiff else b"\x2a" - if self.endian == "<": - head = b"II" + version + b"\x00" + o32le(8) - else: - head = b"MM\x00" + version + o32be(8) - if self.bigtiff: - head += o32le(8) if self.endian == "<" else o32be(8) - head += b"\x00\x00\x00\x00" - return head - - def load(self, data: bytes) -> None: - # Extract EXIF information. This is highly experimental, - # and is likely to be replaced with something better in a future - # version. - - # The EXIF record consists of a TIFF file embedded in a JPEG - # application marker (!). - if data == self._loaded_exif: - return - self._loaded_exif = data - self._data.clear() - self._hidden_data.clear() - self._ifds.clear() - while data and data.startswith(b"Exif\x00\x00"): - data = data[6:] - if not data: - self._info = None - return - - self.fp: IO[bytes] = io.BytesIO(data) - self.head = self.fp.read(8) - # process dictionary - from . import TiffImagePlugin - - self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) - self.endian = self._info._endian - self.fp.seek(self._info.next) - self._info.load(self.fp) - - def load_from_fp(self, fp: IO[bytes], offset: int | None = None) -> None: - self._loaded_exif = None - self._data.clear() - self._hidden_data.clear() - self._ifds.clear() - - # process dictionary - from . import TiffImagePlugin - - self.fp = fp - if offset is not None: - self.head = self._get_head() - else: - self.head = self.fp.read(8) - self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) - if self.endian is None: - self.endian = self._info._endian - if offset is None: - offset = self._info.next - self.fp.tell() - self.fp.seek(offset) - self._info.load(self.fp) - - def _get_merged_dict(self) -> dict[int, Any]: - merged_dict = dict(self) - - # get EXIF extension - if ExifTags.IFD.Exif in self: - ifd = self._get_ifd_dict(self[ExifTags.IFD.Exif], ExifTags.IFD.Exif) - if ifd: - merged_dict.update(ifd) - - # GPS - if ExifTags.IFD.GPSInfo in self: - merged_dict[ExifTags.IFD.GPSInfo] = self._get_ifd_dict( - self[ExifTags.IFD.GPSInfo], ExifTags.IFD.GPSInfo - ) - - return merged_dict - - def tobytes(self, offset: int = 8) -> bytes: - from . import TiffImagePlugin - - head = self._get_head() - ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head) - for tag, ifd_dict in self._ifds.items(): - if tag not in self: - ifd[tag] = ifd_dict - for tag, value in self.items(): - if tag in [ - ExifTags.IFD.Exif, - ExifTags.IFD.GPSInfo, - ] and not isinstance(value, dict): - value = self.get_ifd(tag) - if ( - tag == ExifTags.IFD.Exif - and ExifTags.IFD.Interop in value - and not isinstance(value[ExifTags.IFD.Interop], dict) - ): - value = value.copy() - value[ExifTags.IFD.Interop] = self.get_ifd(ExifTags.IFD.Interop) - ifd[tag] = value - return b"Exif\x00\x00" + head + ifd.tobytes(offset) - - def get_ifd(self, tag: int) -> dict[int, Any]: - if tag not in self._ifds: - if tag == ExifTags.IFD.IFD1: - if self._info is not None and self._info.next != 0: - ifd = self._get_ifd_dict(self._info.next) - if ifd is not None: - self._ifds[tag] = ifd - elif tag in [ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo]: - offset = self._hidden_data.get(tag, self.get(tag)) - if offset is not None: - ifd = self._get_ifd_dict(offset, tag) - if ifd is not None: - self._ifds[tag] = ifd - elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.MakerNote]: - if ExifTags.IFD.Exif not in self._ifds: - self.get_ifd(ExifTags.IFD.Exif) - tag_data = self._ifds[ExifTags.IFD.Exif][tag] - if tag == ExifTags.IFD.MakerNote: - from .TiffImagePlugin import ImageFileDirectory_v2 - - try: - if tag_data.startswith(b"FUJIFILM"): - ifd_offset = i32le(tag_data, 8) - ifd_data = tag_data[ifd_offset:] - - makernote = {} - for i in range(struct.unpack(" 4: - (offset,) = struct.unpack("H", tag_data[:2])[0]): - ifd_tag, typ, count, data = struct.unpack( - ">HHL4s", tag_data[i * 12 + 2 : (i + 1) * 12 + 2] - ) - if ifd_tag == 0x1101: - # CameraInfo - (offset,) = struct.unpack(">L", data) - self.fp.seek(offset) - - camerainfo: dict[str, int | bytes] = { - "ModelID": self.fp.read(4) - } - - self.fp.read(4) - # Seconds since 2000 - camerainfo["TimeStamp"] = i32le(self.fp.read(12)) - - self.fp.read(4) - camerainfo["InternalSerialNumber"] = self.fp.read(4) - - self.fp.read(12) - parallax = self.fp.read(4) - handler = ImageFileDirectory_v2._load_dispatch[ - TiffTags.FLOAT - ][1] - camerainfo["Parallax"] = handler( - ImageFileDirectory_v2(), parallax, False - )[0] - - self.fp.read(4) - camerainfo["Category"] = self.fp.read(2) - - makernote = {0x1101: camerainfo} - self._ifds[tag] = makernote - except struct.error: - pass - else: - # Interop - ifd = self._get_ifd_dict(tag_data, tag) - if ifd is not None: - self._ifds[tag] = ifd - ifd = self._ifds.setdefault(tag, {}) - if tag == ExifTags.IFD.Exif and self._hidden_data: - ifd = { - k: v - for (k, v) in ifd.items() - if k not in (ExifTags.IFD.Interop, ExifTags.IFD.MakerNote) - } - return ifd - - def hide_offsets(self) -> None: - for tag in (ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo): - if tag in self: - self._hidden_data[tag] = self[tag] - del self[tag] - - def __str__(self) -> str: - if self._info is not None: - # Load all keys into self._data - for tag in self._info: - self[tag] - - return str(self._data) - - def __len__(self) -> int: - keys = set(self._data) - if self._info is not None: - keys.update(self._info) - return len(keys) - - def __getitem__(self, tag: int) -> Any: - if self._info is not None and tag not in self._data and tag in self._info: - self._data[tag] = self._fixup(self._info[tag]) - del self._info[tag] - return self._data[tag] - - def __contains__(self, tag: object) -> bool: - return tag in self._data or (self._info is not None and tag in self._info) - - def __setitem__(self, tag: int, value: Any) -> None: - if self._info is not None and tag in self._info: - del self._info[tag] - self._data[tag] = value - - def __delitem__(self, tag: int) -> None: - if self._info is not None and tag in self._info: - del self._info[tag] - else: - del self._data[tag] - if tag in self._ifds: - del self._ifds[tag] - - def __iter__(self) -> Iterator[int]: - keys = set(self._data) - if self._info is not None: - keys.update(self._info) - return iter(keys) diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageChops.py b/.venv/lib/python3.12/site-packages/PIL/ImageChops.py deleted file mode 100644 index 29a5c995..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageChops.py +++ /dev/null @@ -1,311 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard channel operations -# -# History: -# 1996-03-24 fl Created -# 1996-08-13 fl Added logical operations (for "1" images) -# 2000-10-12 fl Added offset method (from Image.py) -# -# Copyright (c) 1997-2000 by Secret Labs AB -# Copyright (c) 1996-2000 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - -from __future__ import annotations - -from . import Image - - -def constant(image: Image.Image, value: int) -> Image.Image: - """Fill a channel with a given gray level. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return Image.new("L", image.size, value) - - -def duplicate(image: Image.Image) -> Image.Image: - """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return image.copy() - - -def invert(image: Image.Image) -> Image.Image: - """ - Invert an image (channel). :: - - out = MAX - image - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image.load() - return image._new(image.im.chop_invert()) - - -def lighter(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Compares the two images, pixel by pixel, and returns a new image containing - the lighter values. :: - - out = max(image1, image2) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_lighter(image2.im)) - - -def darker(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Compares the two images, pixel by pixel, and returns a new image containing - the darker values. :: - - out = min(image1, image2) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_darker(image2.im)) - - -def difference(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Returns the absolute value of the pixel-by-pixel difference between the two - images. :: - - out = abs(image1 - image2) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_difference(image2.im)) - - -def multiply(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other. - - If you multiply an image with a solid black image, the result is black. If - you multiply with a solid white image, the image is unaffected. :: - - out = image1 * image2 / MAX - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_multiply(image2.im)) - - -def screen(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two inverted images on top of each other. :: - - out = MAX - ((MAX - image1) * (MAX - image2) / MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_screen(image2.im)) - - -def soft_light(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other using the Soft Light algorithm - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_soft_light(image2.im)) - - -def hard_light(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other using the Hard Light algorithm - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_hard_light(image2.im)) - - -def overlay(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other using the Overlay algorithm - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_overlay(image2.im)) - - -def add( - image1: Image.Image, image2: Image.Image, scale: float = 1.0, offset: float = 0 -) -> Image.Image: - """ - Adds two images, dividing the result by scale and adding the - offset. If omitted, scale defaults to 1.0, and offset to 0.0. :: - - out = ((image1 + image2) / scale + offset) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_add(image2.im, scale, offset)) - - -def subtract( - image1: Image.Image, image2: Image.Image, scale: float = 1.0, offset: float = 0 -) -> Image.Image: - """ - Subtracts two images, dividing the result by scale and adding the offset. - If omitted, scale defaults to 1.0, and offset to 0.0. :: - - out = ((image1 - image2) / scale + offset) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) - - -def add_modulo(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Add two images, without clipping the result. :: - - out = ((image1 + image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_add_modulo(image2.im)) - - -def subtract_modulo(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Subtract two images, without clipping the result. :: - - out = ((image1 - image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_subtract_modulo(image2.im)) - - -def logical_and(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Logical AND between two images. - - Both of the images must have mode "1". If you would like to perform a - logical AND on an image with a mode other than "1", try - :py:meth:`~PIL.ImageChops.multiply` instead, using a black-and-white mask - as the second image. :: - - out = ((image1 and image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_and(image2.im)) - - -def logical_or(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Logical OR between two images. - - Both of the images must have mode "1". :: - - out = ((image1 or image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_or(image2.im)) - - -def logical_xor(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Logical XOR between two images. - - Both of the images must have mode "1". :: - - out = ((bool(image1) != bool(image2)) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_xor(image2.im)) - - -def blend(image1: Image.Image, image2: Image.Image, alpha: float) -> Image.Image: - """Blend images using constant transparency weight. Alias for - :py:func:`PIL.Image.blend`. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return Image.blend(image1, image2, alpha) - - -def composite( - image1: Image.Image, image2: Image.Image, mask: Image.Image -) -> Image.Image: - """Create composite using transparency mask. Alias for - :py:func:`PIL.Image.composite`. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return Image.composite(image1, image2, mask) - - -def offset(image: Image.Image, xoffset: int, yoffset: int | None = None) -> Image.Image: - """Returns a copy of the image where data has been offset by the given - distances. Data wraps around the edges. If ``yoffset`` is omitted, it - is assumed to be equal to ``xoffset``. - - :param image: Input image. - :param xoffset: The horizontal distance. - :param yoffset: The vertical distance. If omitted, both - distances are set to the same value. - :rtype: :py:class:`~PIL.Image.Image` - """ - - if yoffset is None: - yoffset = xoffset - image.load() - return image._new(image.im.offset(xoffset, yoffset)) diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageCms.py b/.venv/lib/python3.12/site-packages/PIL/ImageCms.py deleted file mode 100644 index 513e28ac..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageCms.py +++ /dev/null @@ -1,1076 +0,0 @@ -# The Python Imaging Library. -# $Id$ - -# Optional color management support, based on Kevin Cazabon's PyCMS -# library. - -# Originally released under LGPL. Graciously donated to PIL in -# March 2009, for distribution under the standard PIL license - -# History: - -# 2009-03-08 fl Added to PIL. - -# Copyright (C) 2002-2003 Kevin Cazabon -# Copyright (c) 2009 by Fredrik Lundh -# Copyright (c) 2013 by Eric Soroos - -# See the README file for information on usage and redistribution. See -# below for the original description. -from __future__ import annotations - -import operator -import sys -from enum import IntEnum, IntFlag -from functools import reduce -from typing import Any, Literal, SupportsFloat, SupportsInt, Union - -from . import Image -from ._deprecate import deprecate -from ._typing import SupportsRead - -try: - from . import _imagingcms as core - - _CmsProfileCompatible = Union[ - str, SupportsRead[bytes], core.CmsProfile, "ImageCmsProfile" - ] -except ImportError as ex: - # Allow error import for doc purposes, but error out when accessing - # anything in core. - from ._util import DeferredError - - core = DeferredError.new(ex) - -_DESCRIPTION = """ -pyCMS - - a Python / PIL interface to the littleCMS ICC Color Management System - Copyright (C) 2002-2003 Kevin Cazabon - kevin@cazabon.com - https://www.cazabon.com - - pyCMS home page: https://www.cazabon.com/pyCMS - littleCMS home page: https://www.littlecms.com - (littleCMS is Copyright (C) 1998-2001 Marti Maria) - - Originally released under LGPL. Graciously donated to PIL in - March 2009, for distribution under the standard PIL license - - The pyCMS.py module provides a "clean" interface between Python/PIL and - pyCMSdll, taking care of some of the more complex handling of the direct - pyCMSdll functions, as well as error-checking and making sure that all - relevant data is kept together. - - While it is possible to call pyCMSdll functions directly, it's not highly - recommended. - - Version History: - - 1.0.0 pil Oct 2013 Port to LCMS 2. - - 0.1.0 pil mod March 10, 2009 - - Renamed display profile to proof profile. The proof - profile is the profile of the device that is being - simulated, not the profile of the device which is - actually used to display/print the final simulation - (that'd be the output profile) - also see LCMSAPI.txt - input colorspace -> using 'renderingIntent' -> proof - colorspace -> using 'proofRenderingIntent' -> output - colorspace - - Added LCMS FLAGS support. - Added FLAGS["SOFTPROOFING"] as default flag for - buildProofTransform (otherwise the proof profile/intent - would be ignored). - - 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms - - 0.0.2 alpha Jan 6, 2002 - - Added try/except statements around type() checks of - potential CObjects... Python won't let you use type() - on them, and raises a TypeError (stupid, if you ask - me!) - - Added buildProofTransformFromOpenProfiles() function. - Additional fixes in DLL, see DLL code for details. - - 0.0.1 alpha first public release, Dec. 26, 2002 - - Known to-do list with current version (of Python interface, not pyCMSdll): - - none - -""" - -_VERSION = "1.0.0 pil" - - -# --------------------------------------------------------------------. - - -# -# intent/direction values - - -class Intent(IntEnum): - PERCEPTUAL = 0 - RELATIVE_COLORIMETRIC = 1 - SATURATION = 2 - ABSOLUTE_COLORIMETRIC = 3 - - -class Direction(IntEnum): - INPUT = 0 - OUTPUT = 1 - PROOF = 2 - - -# -# flags - - -class Flags(IntFlag): - """Flags and documentation are taken from ``lcms2.h``.""" - - NONE = 0 - NOCACHE = 0x0040 - """Inhibit 1-pixel cache""" - NOOPTIMIZE = 0x0100 - """Inhibit optimizations""" - NULLTRANSFORM = 0x0200 - """Don't transform anyway""" - GAMUTCHECK = 0x1000 - """Out of Gamut alarm""" - SOFTPROOFING = 0x4000 - """Do softproofing""" - BLACKPOINTCOMPENSATION = 0x2000 - NOWHITEONWHITEFIXUP = 0x0004 - """Don't fix scum dot""" - HIGHRESPRECALC = 0x0400 - """Use more memory to give better accuracy""" - LOWRESPRECALC = 0x0800 - """Use less memory to minimize resources""" - # this should be 8BITS_DEVICELINK, but that is not a valid name in Python: - USE_8BITS_DEVICELINK = 0x0008 - """Create 8 bits devicelinks""" - GUESSDEVICECLASS = 0x0020 - """Guess device class (for ``transform2devicelink``)""" - KEEP_SEQUENCE = 0x0080 - """Keep profile sequence for devicelink creation""" - FORCE_CLUT = 0x0002 - """Force CLUT optimization""" - CLUT_POST_LINEARIZATION = 0x0001 - """create postlinearization tables if possible""" - CLUT_PRE_LINEARIZATION = 0x0010 - """create prelinearization tables if possible""" - NONEGATIVES = 0x8000 - """Prevent negative numbers in floating point transforms""" - COPY_ALPHA = 0x04000000 - """Alpha channels are copied on ``cmsDoTransform()``""" - NODEFAULTRESOURCEDEF = 0x01000000 - - _GRIDPOINTS_1 = 1 << 16 - _GRIDPOINTS_2 = 2 << 16 - _GRIDPOINTS_4 = 4 << 16 - _GRIDPOINTS_8 = 8 << 16 - _GRIDPOINTS_16 = 16 << 16 - _GRIDPOINTS_32 = 32 << 16 - _GRIDPOINTS_64 = 64 << 16 - _GRIDPOINTS_128 = 128 << 16 - - @staticmethod - def GRIDPOINTS(n: int) -> Flags: - """ - Fine-tune control over number of gridpoints - - :param n: :py:class:`int` in range ``0 <= n <= 255`` - """ - return Flags.NONE | ((n & 0xFF) << 16) - - -_MAX_FLAG = reduce(operator.or_, Flags) - - -_FLAGS = { - "MATRIXINPUT": 1, - "MATRIXOUTPUT": 2, - "MATRIXONLY": (1 | 2), - "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot - # Don't create prelinearization tables on precalculated transforms - # (internal use): - "NOPRELINEARIZATION": 16, - "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) - "NOTCACHE": 64, # Inhibit 1-pixel cache - "NOTPRECALC": 256, - "NULLTRANSFORM": 512, # Don't transform anyway - "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy - "LOWRESPRECALC": 2048, # Use less memory to minimize resources - "WHITEBLACKCOMPENSATION": 8192, - "BLACKPOINTCOMPENSATION": 8192, - "GAMUTCHECK": 4096, # Out of Gamut alarm - "SOFTPROOFING": 16384, # Do softproofing - "PRESERVEBLACK": 32768, # Black preservation - "NODEFAULTRESOURCEDEF": 16777216, # CRD special - "GRIDPOINTS": lambda n: (n & 0xFF) << 16, # Gridpoints -} - - -# --------------------------------------------------------------------. -# Experimental PIL-level API -# --------------------------------------------------------------------. - -## -# Profile. - - -class ImageCmsProfile: - def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None: - """ - :param profile: Either a string representing a filename, - a file like object containing a profile or a - low-level profile object - - """ - self.filename: str | None = None - - if isinstance(profile, str): - if sys.platform == "win32": - profile_bytes_path = profile.encode() - try: - profile_bytes_path.decode("ascii") - except UnicodeDecodeError: - with open(profile, "rb") as f: - self.profile = core.profile_frombytes(f.read()) - return - self.filename = profile - self.profile = core.profile_open(profile) - elif hasattr(profile, "read"): - self.profile = core.profile_frombytes(profile.read()) - elif isinstance(profile, core.CmsProfile): - self.profile = profile - else: - msg = "Invalid type for Profile" # type: ignore[unreachable] - raise TypeError(msg) - - def __getattr__(self, name: str) -> Any: - if name in ("product_name", "product_info"): - deprecate(f"ImageCms.ImageCmsProfile.{name}", 13) - return None - msg = f"'{self.__class__.__name__}' object has no attribute '{name}'" - raise AttributeError(msg) - - def tobytes(self) -> bytes: - """ - Returns the profile in a format suitable for embedding in - saved images. - - :returns: a bytes object containing the ICC profile. - """ - - return core.profile_tobytes(self.profile) - - -class ImageCmsTransform(Image.ImagePointHandler): - """ - Transform. This can be used with the procedural API, or with the standard - :py:func:`~PIL.Image.Image.point` method. - - Will return the output profile in the ``output.info['icc_profile']``. - """ - - def __init__( - self, - input: ImageCmsProfile, - output: ImageCmsProfile, - input_mode: str, - output_mode: str, - intent: Intent = Intent.PERCEPTUAL, - proof: ImageCmsProfile | None = None, - proof_intent: Intent = Intent.ABSOLUTE_COLORIMETRIC, - flags: Flags = Flags.NONE, - ): - if proof is None: - self.transform = core.buildTransform( - input.profile, output.profile, input_mode, output_mode, intent, flags - ) - else: - self.transform = core.buildProofTransform( - input.profile, - output.profile, - proof.profile, - input_mode, - output_mode, - intent, - proof_intent, - flags, - ) - # Note: inputMode and outputMode are for pyCMS compatibility only - self.input_mode = self.inputMode = input_mode - self.output_mode = self.outputMode = output_mode - - self.output_profile = output - - def point(self, im: Image.Image) -> Image.Image: - return self.apply(im) - - def apply(self, im: Image.Image, imOut: Image.Image | None = None) -> Image.Image: - if imOut is None: - imOut = Image.new(self.output_mode, im.size, None) - self.transform.apply(im.getim(), imOut.getim()) - imOut.info["icc_profile"] = self.output_profile.tobytes() - return imOut - - def apply_in_place(self, im: Image.Image) -> Image.Image: - if im.mode != self.output_mode: - msg = "mode mismatch" - raise ValueError(msg) # wrong output mode - self.transform.apply(im.getim(), im.getim()) - im.info["icc_profile"] = self.output_profile.tobytes() - return im - - -def get_display_profile(handle: SupportsInt | None = None) -> ImageCmsProfile | None: - """ - (experimental) Fetches the profile for the current display device. - - :returns: ``None`` if the profile is not known. - """ - - if sys.platform != "win32": - return None - - from . import ImageWin # type: ignore[unused-ignore, unreachable] - - if isinstance(handle, ImageWin.HDC): - profile = core.get_display_profile_win32(int(handle), 1) - else: - profile = core.get_display_profile_win32(int(handle or 0)) - if profile is None: - return None - return ImageCmsProfile(profile) - - -# --------------------------------------------------------------------. -# pyCMS compatible layer -# --------------------------------------------------------------------. - - -class PyCMSError(Exception): - """(pyCMS) Exception class. - This is used for all errors in the pyCMS API.""" - - pass - - -def profileToProfile( - im: Image.Image, - inputProfile: _CmsProfileCompatible, - outputProfile: _CmsProfileCompatible, - renderingIntent: Intent = Intent.PERCEPTUAL, - outputMode: str | None = None, - inPlace: bool = False, - flags: Flags = Flags.NONE, -) -> Image.Image | None: - """ - (pyCMS) Applies an ICC transformation to a given image, mapping from - ``inputProfile`` to ``outputProfile``. - - If the input or output profiles specified are not valid filenames, a - :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and - ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised. - If an error occurs during application of the profiles, - a :exc:`PyCMSError` will be raised. - If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS), - a :exc:`PyCMSError` will be raised. - - This function applies an ICC transformation to im from ``inputProfile``'s - color space to ``outputProfile``'s color space using the specified rendering - intent to decide how to handle out-of-gamut colors. - - ``outputMode`` can be used to specify that a color mode conversion is to - be done using these profiles, but the specified profiles must be able - to handle that mode. I.e., if converting im from RGB to CMYK using - profiles, the input profile must handle RGB data, and the output - profile must handle CMYK data. - - :param im: An open :py:class:`~PIL.Image.Image` object (i.e. Image.new(...) - or Image.open(...), etc.) - :param inputProfile: String, as a valid filename path to the ICC input - profile you wish to use for this image, or a profile object - :param outputProfile: String, as a valid filename path to the ICC output - profile you wish to use for this image, or a profile object - :param renderingIntent: Integer (0-3) specifying the rendering intent you - wish to use for the transform - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :param outputMode: A valid PIL mode for the output image (i.e. "RGB", - "CMYK", etc.). Note: if rendering the image "inPlace", outputMode - MUST be the same mode as the input, or omitted completely. If - omitted, the outputMode will be the same as the mode of the input - image (im.mode) - :param inPlace: Boolean. If ``True``, the original image is modified in-place, - and ``None`` is returned. If ``False`` (default), a new - :py:class:`~PIL.Image.Image` object is returned with the transform applied. - :param flags: Integer (0-...) specifying additional flags - :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on - the value of ``inPlace`` - :exception PyCMSError: - """ - - if outputMode is None: - outputMode = im.mode - - if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): - msg = "renderingIntent must be an integer between 0 and 3" - raise PyCMSError(msg) - - if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - msg = f"flags must be an integer between 0 and {_MAX_FLAG}" - raise PyCMSError(msg) - - try: - if not isinstance(inputProfile, ImageCmsProfile): - inputProfile = ImageCmsProfile(inputProfile) - if not isinstance(outputProfile, ImageCmsProfile): - outputProfile = ImageCmsProfile(outputProfile) - transform = ImageCmsTransform( - inputProfile, - outputProfile, - im.mode, - outputMode, - renderingIntent, - flags=flags, - ) - if inPlace: - transform.apply_in_place(im) - imOut = None - else: - imOut = transform.apply(im) - except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - return imOut - - -def getOpenProfile( - profileFilename: str | SupportsRead[bytes] | core.CmsProfile, -) -> ImageCmsProfile: - """ - (pyCMS) Opens an ICC profile file. - - The PyCMSProfile object can be passed back into pyCMS for use in creating - transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). - - If ``profileFilename`` is not a valid filename for an ICC profile, - a :exc:`PyCMSError` will be raised. - - :param profileFilename: String, as a valid filename path to the ICC profile - you wish to open, or a file-like object. - :returns: A CmsProfile class object. - :exception PyCMSError: - """ - - try: - return ImageCmsProfile(profileFilename) - except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def buildTransform( - inputProfile: _CmsProfileCompatible, - outputProfile: _CmsProfileCompatible, - inMode: str, - outMode: str, - renderingIntent: Intent = Intent.PERCEPTUAL, - flags: Flags = Flags.NONE, -) -> ImageCmsTransform: - """ - (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the - ``outputProfile``. Use applyTransform to apply the transform to a given - image. - - If the input or output profiles specified are not valid filenames, a - :exc:`PyCMSError` will be raised. If an error occurs during creation - of the transform, a :exc:`PyCMSError` will be raised. - - If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` - (or by pyCMS), a :exc:`PyCMSError` will be raised. - - This function builds and returns an ICC transform from the ``inputProfile`` - to the ``outputProfile`` using the ``renderingIntent`` to determine what to do - with out-of-gamut colors. It will ONLY work for converting images that - are in ``inMode`` to images that are in ``outMode`` color format (PIL mode, - i.e. "RGB", "RGBA", "CMYK", etc.). - - Building the transform is a fair part of the overhead in - ImageCms.profileToProfile(), so if you're planning on converting multiple - images using the same input/output settings, this can save you time. - Once you have a transform object, it can be used with - ImageCms.applyProfile() to convert images without the need to re-compute - the lookup table for the transform. - - The reason pyCMS returns a class object rather than a handle directly - to the transform is that it needs to keep track of the PIL input/output - modes that the transform is meant for. These attributes are stored in - the ``inMode`` and ``outMode`` attributes of the object (which can be - manually overridden if you really want to, but I don't know of any - time that would be of use, or would even work). - - :param inputProfile: String, as a valid filename path to the ICC input - profile you wish to use for this transform, or a profile object - :param outputProfile: String, as a valid filename path to the ICC output - profile you wish to use for this transform, or a profile object - :param inMode: String, as a valid PIL mode that the appropriate profile - also supports (i.e. "RGB", "RGBA", "CMYK", etc.) - :param outMode: String, as a valid PIL mode that the appropriate profile - also supports (i.e. "RGB", "RGBA", "CMYK", etc.) - :param renderingIntent: Integer (0-3) specifying the rendering intent you - wish to use for the transform - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :param flags: Integer (0-...) specifying additional flags - :returns: A CmsTransform class object. - :exception PyCMSError: - """ - - if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): - msg = "renderingIntent must be an integer between 0 and 3" - raise PyCMSError(msg) - - if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - msg = f"flags must be an integer between 0 and {_MAX_FLAG}" - raise PyCMSError(msg) - - try: - if not isinstance(inputProfile, ImageCmsProfile): - inputProfile = ImageCmsProfile(inputProfile) - if not isinstance(outputProfile, ImageCmsProfile): - outputProfile = ImageCmsProfile(outputProfile) - return ImageCmsTransform( - inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags - ) - except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def buildProofTransform( - inputProfile: _CmsProfileCompatible, - outputProfile: _CmsProfileCompatible, - proofProfile: _CmsProfileCompatible, - inMode: str, - outMode: str, - renderingIntent: Intent = Intent.PERCEPTUAL, - proofRenderingIntent: Intent = Intent.ABSOLUTE_COLORIMETRIC, - flags: Flags = Flags.SOFTPROOFING, -) -> ImageCmsTransform: - """ - (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the - ``outputProfile``, but tries to simulate the result that would be - obtained on the ``proofProfile`` device. - - If the input, output, or proof profiles specified are not valid - filenames, a :exc:`PyCMSError` will be raised. - - If an error occurs during creation of the transform, - a :exc:`PyCMSError` will be raised. - - If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` - (or by pyCMS), a :exc:`PyCMSError` will be raised. - - This function builds and returns an ICC transform from the ``inputProfile`` - to the ``outputProfile``, but tries to simulate the result that would be - obtained on the ``proofProfile`` device using ``renderingIntent`` and - ``proofRenderingIntent`` to determine what to do with out-of-gamut - colors. This is known as "soft-proofing". It will ONLY work for - converting images that are in ``inMode`` to images that are in outMode - color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). - - Usage of the resulting transform object is exactly the same as with - ImageCms.buildTransform(). - - Proof profiling is generally used when using an output device to get a - good idea of what the final printed/displayed image would look like on - the ``proofProfile`` device when it's quicker and easier to use the - output device for judging color. Generally, this means that the - output device is a monitor, or a dye-sub printer (etc.), and the simulated - device is something more expensive, complicated, or time consuming - (making it difficult to make a real print for color judgement purposes). - - Soft-proofing basically functions by adjusting the colors on the - output device to match the colors of the device being simulated. However, - when the simulated device has a much wider gamut than the output - device, you may obtain marginal results. - - :param inputProfile: String, as a valid filename path to the ICC input - profile you wish to use for this transform, or a profile object - :param outputProfile: String, as a valid filename path to the ICC output - (monitor, usually) profile you wish to use for this transform, or a - profile object - :param proofProfile: String, as a valid filename path to the ICC proof - profile you wish to use for this transform, or a profile object - :param inMode: String, as a valid PIL mode that the appropriate profile - also supports (i.e. "RGB", "RGBA", "CMYK", etc.) - :param outMode: String, as a valid PIL mode that the appropriate profile - also supports (i.e. "RGB", "RGBA", "CMYK", etc.) - :param renderingIntent: Integer (0-3) specifying the rendering intent you - wish to use for the input->proof (simulated) transform - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :param proofRenderingIntent: Integer (0-3) specifying the rendering intent - you wish to use for proof->output transform - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :param flags: Integer (0-...) specifying additional flags - :returns: A CmsTransform class object. - :exception PyCMSError: - """ - - if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): - msg = "renderingIntent must be an integer between 0 and 3" - raise PyCMSError(msg) - - if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - msg = f"flags must be an integer between 0 and {_MAX_FLAG}" - raise PyCMSError(msg) - - try: - if not isinstance(inputProfile, ImageCmsProfile): - inputProfile = ImageCmsProfile(inputProfile) - if not isinstance(outputProfile, ImageCmsProfile): - outputProfile = ImageCmsProfile(outputProfile) - if not isinstance(proofProfile, ImageCmsProfile): - proofProfile = ImageCmsProfile(proofProfile) - return ImageCmsTransform( - inputProfile, - outputProfile, - inMode, - outMode, - renderingIntent, - proofProfile, - proofRenderingIntent, - flags, - ) - except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -buildTransformFromOpenProfiles = buildTransform -buildProofTransformFromOpenProfiles = buildProofTransform - - -def applyTransform( - im: Image.Image, transform: ImageCmsTransform, inPlace: bool = False -) -> Image.Image | None: - """ - (pyCMS) Applies a transform to a given image. - - If ``im.mode != transform.input_mode``, a :exc:`PyCMSError` is raised. - - If ``inPlace`` is ``True`` and ``transform.input_mode != transform.output_mode``, a - :exc:`PyCMSError` is raised. - - If ``im.mode``, ``transform.input_mode`` or ``transform.output_mode`` is not - supported by pyCMSdll or the profiles you used for the transform, a - :exc:`PyCMSError` is raised. - - If an error occurs while the transform is being applied, - a :exc:`PyCMSError` is raised. - - This function applies a pre-calculated transform (from - ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) - to an image. The transform can be used for multiple images, saving - considerable calculation time if doing the same conversion multiple times. - - If you want to modify im in-place instead of receiving a new image as - the return value, set ``inPlace`` to ``True``. This can only be done if - ``transform.input_mode`` and ``transform.output_mode`` are the same, because we - can't change the mode in-place (the buffer sizes for some modes are - different). The default behavior is to return a new :py:class:`~PIL.Image.Image` - object of the same dimensions in mode ``transform.output_mode``. - - :param im: An :py:class:`~PIL.Image.Image` object, and ``im.mode`` must be the same - as the ``input_mode`` supported by the transform. - :param transform: A valid CmsTransform class object - :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is - returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the - transform applied is returned (and ``im`` is not changed). The default is - ``False``. - :returns: Either ``None``, or a new :py:class:`~PIL.Image.Image` object, - depending on the value of ``inPlace``. The profile will be returned in - the image's ``info['icc_profile']``. - :exception PyCMSError: - """ - - try: - if inPlace: - transform.apply_in_place(im) - imOut = None - else: - imOut = transform.apply(im) - except (TypeError, ValueError) as v: - raise PyCMSError(v) from v - - return imOut - - -def createProfile( - colorSpace: Literal["LAB", "XYZ", "sRGB"], colorTemp: SupportsFloat = 0 -) -> core.CmsProfile: - """ - (pyCMS) Creates a profile. - - If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, - a :exc:`PyCMSError` is raised. - - If using LAB and ``colorTemp`` is not a positive integer, - a :exc:`PyCMSError` is raised. - - If an error occurs while creating the profile, - a :exc:`PyCMSError` is raised. - - Use this function to create common profiles on-the-fly instead of - having to supply a profile on disk and knowing the path to it. It - returns a normal CmsProfile object that can be passed to - ImageCms.buildTransformFromOpenProfiles() to create a transform to apply - to images. - - :param colorSpace: String, the color space of the profile you wish to - create. - Currently only "LAB", "XYZ", and "sRGB" are supported. - :param colorTemp: Positive number for the white point for the profile, in - degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 - illuminant if omitted (5000k). colorTemp is ONLY applied to LAB - profiles, and is ignored for XYZ and sRGB. - :returns: A CmsProfile class object - :exception PyCMSError: - """ - - if colorSpace not in ["LAB", "XYZ", "sRGB"]: - msg = ( - f"Color space not supported for on-the-fly profile creation ({colorSpace})" - ) - raise PyCMSError(msg) - - if colorSpace == "LAB": - try: - colorTemp = float(colorTemp) - except (TypeError, ValueError) as e: - msg = f'Color temperature must be numeric, "{colorTemp}" not valid' - raise PyCMSError(msg) from e - - try: - return core.createProfile(colorSpace, colorTemp) - except (TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileName(profile: _CmsProfileCompatible) -> str: - """ - - (pyCMS) Gets the internal product name for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, - a :exc:`PyCMSError` is raised If an error occurs while trying - to obtain the name tag, a :exc:`PyCMSError` is raised. - - Use this function to obtain the INTERNAL name of the profile (stored - in an ICC tag in the profile itself), usually the one used when the - profile was originally created. Sometimes this tag also contains - additional information supplied by the creator. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal name of the profile as stored - in an ICC tag. - :exception PyCMSError: - """ - - try: - # add an extra newline to preserve pyCMS compatibility - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - # do it in python, not c. - # // name was "%s - %s" (model, manufacturer) || Description , - # // but if the Model and Manufacturer were the same or the model - # // was long, Just the model, in 1.x - model = profile.profile.model - manufacturer = profile.profile.manufacturer - - if not (model or manufacturer): - return (profile.profile.profile_description or "") + "\n" - if not manufacturer or (model and len(model) > 30): - return f"{model}\n" - return f"{model} - {manufacturer}\n" - - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileInfo(profile: _CmsProfileCompatible) -> str: - """ - (pyCMS) Gets the internal product information for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, - a :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the info tag, - a :exc:`PyCMSError` is raised. - - Use this function to obtain the information stored in the profile's - info tag. This often contains details about the profile, and how it - was created, as supplied by the creator. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal profile information stored in - an ICC tag. - :exception PyCMSError: - """ - - try: - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - # add an extra newline to preserve pyCMS compatibility - # Python, not C. the white point bits weren't working well, - # so skipping. - # info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint - description = profile.profile.profile_description - cpright = profile.profile.copyright - elements = [element for element in (description, cpright) if element] - return "\r\n\r\n".join(elements) + "\r\n\r\n" - - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileCopyright(profile: _CmsProfileCompatible) -> str: - """ - (pyCMS) Gets the copyright for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the copyright tag, - a :exc:`PyCMSError` is raised. - - Use this function to obtain the information stored in the profile's - copyright tag. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal profile information stored in - an ICC tag. - :exception PyCMSError: - """ - try: - # add an extra newline to preserve pyCMS compatibility - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - return (profile.profile.copyright or "") + "\n" - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileManufacturer(profile: _CmsProfileCompatible) -> str: - """ - (pyCMS) Gets the manufacturer for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the manufacturer tag, a - :exc:`PyCMSError` is raised. - - Use this function to obtain the information stored in the profile's - manufacturer tag. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal profile information stored in - an ICC tag. - :exception PyCMSError: - """ - try: - # add an extra newline to preserve pyCMS compatibility - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - return (profile.profile.manufacturer or "") + "\n" - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileModel(profile: _CmsProfileCompatible) -> str: - """ - (pyCMS) Gets the model for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the model tag, - a :exc:`PyCMSError` is raised. - - Use this function to obtain the information stored in the profile's - model tag. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal profile information stored in - an ICC tag. - :exception PyCMSError: - """ - - try: - # add an extra newline to preserve pyCMS compatibility - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - return (profile.profile.model or "") + "\n" - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileDescription(profile: _CmsProfileCompatible) -> str: - """ - (pyCMS) Gets the description for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the description tag, - a :exc:`PyCMSError` is raised. - - Use this function to obtain the information stored in the profile's - description tag. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal profile information stored in an - ICC tag. - :exception PyCMSError: - """ - - try: - # add an extra newline to preserve pyCMS compatibility - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - return (profile.profile.profile_description or "") + "\n" - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getDefaultIntent(profile: _CmsProfileCompatible) -> int: - """ - (pyCMS) Gets the default intent name for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the default intent, a - :exc:`PyCMSError` is raised. - - Use this function to determine the default (and usually best optimized) - rendering intent for this profile. Most profiles support multiple - rendering intents, but are intended mostly for one type of conversion. - If you wish to use a different intent than returned, use - ImageCms.isIntentSupported() to verify it will work first. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: Integer 0-3 specifying the default rendering intent for this - profile. - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :exception PyCMSError: - """ - - try: - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - return profile.profile.rendering_intent - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def isIntentSupported( - profile: _CmsProfileCompatible, intent: Intent, direction: Direction -) -> Literal[-1, 1]: - """ - (pyCMS) Checks if a given intent is supported. - - Use this function to verify that you can use your desired - ``intent`` with ``profile``, and that ``profile`` can be used for the - input/output/proof profile as you desire. - - Some profiles are created specifically for one "direction", can cannot - be used for others. Some profiles can only be used for certain - rendering intents, so it's best to either verify this before trying - to create a transform with them (using this function), or catch the - potential :exc:`PyCMSError` that will occur if they don't - support the modes you select. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :param intent: Integer (0-3) specifying the rendering intent you wish to - use with this profile - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :param direction: Integer specifying if the profile is to be used for - input, output, or proof - - INPUT = 0 (or use ImageCms.Direction.INPUT) - OUTPUT = 1 (or use ImageCms.Direction.OUTPUT) - PROOF = 2 (or use ImageCms.Direction.PROOF) - - :returns: 1 if the intent/direction are supported, -1 if they are not. - :exception PyCMSError: - """ - - try: - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - # FIXME: I get different results for the same data w. different - # compilers. Bug in LittleCMS or in the binding? - if profile.profile.is_intent_supported(intent, direction): - return 1 - else: - return -1 - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageColor.py b/.venv/lib/python3.12/site-packages/PIL/ImageColor.py deleted file mode 100644 index 9a15a8eb..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageColor.py +++ /dev/null @@ -1,320 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# map CSS3-style colour description strings to RGB -# -# History: -# 2002-10-24 fl Added support for CSS-style color strings -# 2002-12-15 fl Added RGBA support -# 2004-03-27 fl Fixed remaining int() problems for Python 1.5.2 -# 2004-07-19 fl Fixed gray/grey spelling issues -# 2009-03-05 fl Fixed rounding error in grayscale calculation -# -# Copyright (c) 2002-2004 by Secret Labs AB -# Copyright (c) 2002-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re -from functools import lru_cache - -from . import Image - - -@lru_cache -def getrgb(color: str) -> tuple[int, int, int] | tuple[int, int, int, int]: - """ - Convert a color string to an RGB or RGBA tuple. If the string cannot be - parsed, this function raises a :py:exc:`ValueError` exception. - - .. versionadded:: 1.1.4 - - :param color: A color string - :return: ``(red, green, blue[, alpha])`` - """ - if len(color) > 100: - msg = "color specifier is too long" - raise ValueError(msg) - color = color.lower() - - rgb = colormap.get(color, None) - if rgb: - if isinstance(rgb, tuple): - return rgb - rgb_tuple = getrgb(rgb) - assert len(rgb_tuple) == 3 - colormap[color] = rgb_tuple - return rgb_tuple - - # check for known string formats - if re.match("#[a-f0-9]{3}$", color): - return int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16) - - if re.match("#[a-f0-9]{4}$", color): - return ( - int(color[1] * 2, 16), - int(color[2] * 2, 16), - int(color[3] * 2, 16), - int(color[4] * 2, 16), - ) - - if re.match("#[a-f0-9]{6}$", color): - return int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16) - - if re.match("#[a-f0-9]{8}$", color): - return ( - int(color[1:3], 16), - int(color[3:5], 16), - int(color[5:7], 16), - int(color[7:9], 16), - ) - - m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) - if m: - return int(m.group(1)), int(m.group(2)), int(m.group(3)) - - m = re.match(r"rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) - if m: - return ( - int((int(m.group(1)) * 255) / 100.0 + 0.5), - int((int(m.group(2)) * 255) / 100.0 + 0.5), - int((int(m.group(3)) * 255) / 100.0 + 0.5), - ) - - m = re.match( - r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color - ) - if m: - from colorsys import hls_to_rgb - - rgb_floats = hls_to_rgb( - float(m.group(1)) / 360.0, - float(m.group(3)) / 100.0, - float(m.group(2)) / 100.0, - ) - return ( - int(rgb_floats[0] * 255 + 0.5), - int(rgb_floats[1] * 255 + 0.5), - int(rgb_floats[2] * 255 + 0.5), - ) - - m = re.match( - r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color - ) - if m: - from colorsys import hsv_to_rgb - - rgb_floats = hsv_to_rgb( - float(m.group(1)) / 360.0, - float(m.group(2)) / 100.0, - float(m.group(3)) / 100.0, - ) - return ( - int(rgb_floats[0] * 255 + 0.5), - int(rgb_floats[1] * 255 + 0.5), - int(rgb_floats[2] * 255 + 0.5), - ) - - m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) - if m: - return int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)) - msg = f"unknown color specifier: {repr(color)}" - raise ValueError(msg) - - -@lru_cache -def getcolor(color: str, mode: str) -> int | tuple[int, ...]: - """ - Same as :py:func:`~PIL.ImageColor.getrgb` for most modes. However, if - ``mode`` is HSV, converts the RGB value to a HSV value, or if ``mode`` is - not color or a palette image, converts the RGB value to a grayscale value. - If the string cannot be parsed, this function raises a :py:exc:`ValueError` - exception. - - .. versionadded:: 1.1.4 - - :param color: A color string - :param mode: Convert result to this mode - :return: ``graylevel, (graylevel, alpha) or (red, green, blue[, alpha])`` - """ - # same as getrgb, but converts the result to the given mode - rgb, alpha = getrgb(color), 255 - if len(rgb) == 4: - alpha = rgb[3] - rgb = rgb[:3] - - if mode == "HSV": - from colorsys import rgb_to_hsv - - r, g, b = rgb - h, s, v = rgb_to_hsv(r / 255, g / 255, b / 255) - return int(h * 255), int(s * 255), int(v * 255) - elif Image.getmodebase(mode) == "L": - r, g, b = rgb - # ITU-R Recommendation 601-2 for nonlinear RGB - # scaled to 24 bits to match the convert's implementation. - graylevel = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16 - if mode[-1] == "A": - return graylevel, alpha - return graylevel - elif mode[-1] == "A": - return rgb + (alpha,) - return rgb - - -colormap: dict[str, str | tuple[int, int, int]] = { - # X11 colour table from https://drafts.csswg.org/css-color-4/, with - # gray/grey spelling issues fixed. This is a superset of HTML 4.0 - # colour names used in CSS 1. - "aliceblue": "#f0f8ff", - "antiquewhite": "#faebd7", - "aqua": "#00ffff", - "aquamarine": "#7fffd4", - "azure": "#f0ffff", - "beige": "#f5f5dc", - "bisque": "#ffe4c4", - "black": "#000000", - "blanchedalmond": "#ffebcd", - "blue": "#0000ff", - "blueviolet": "#8a2be2", - "brown": "#a52a2a", - "burlywood": "#deb887", - "cadetblue": "#5f9ea0", - "chartreuse": "#7fff00", - "chocolate": "#d2691e", - "coral": "#ff7f50", - "cornflowerblue": "#6495ed", - "cornsilk": "#fff8dc", - "crimson": "#dc143c", - "cyan": "#00ffff", - "darkblue": "#00008b", - "darkcyan": "#008b8b", - "darkgoldenrod": "#b8860b", - "darkgray": "#a9a9a9", - "darkgrey": "#a9a9a9", - "darkgreen": "#006400", - "darkkhaki": "#bdb76b", - "darkmagenta": "#8b008b", - "darkolivegreen": "#556b2f", - "darkorange": "#ff8c00", - "darkorchid": "#9932cc", - "darkred": "#8b0000", - "darksalmon": "#e9967a", - "darkseagreen": "#8fbc8f", - "darkslateblue": "#483d8b", - "darkslategray": "#2f4f4f", - "darkslategrey": "#2f4f4f", - "darkturquoise": "#00ced1", - "darkviolet": "#9400d3", - "deeppink": "#ff1493", - "deepskyblue": "#00bfff", - "dimgray": "#696969", - "dimgrey": "#696969", - "dodgerblue": "#1e90ff", - "firebrick": "#b22222", - "floralwhite": "#fffaf0", - "forestgreen": "#228b22", - "fuchsia": "#ff00ff", - "gainsboro": "#dcdcdc", - "ghostwhite": "#f8f8ff", - "gold": "#ffd700", - "goldenrod": "#daa520", - "gray": "#808080", - "grey": "#808080", - "green": "#008000", - "greenyellow": "#adff2f", - "honeydew": "#f0fff0", - "hotpink": "#ff69b4", - "indianred": "#cd5c5c", - "indigo": "#4b0082", - "ivory": "#fffff0", - "khaki": "#f0e68c", - "lavender": "#e6e6fa", - "lavenderblush": "#fff0f5", - "lawngreen": "#7cfc00", - "lemonchiffon": "#fffacd", - "lightblue": "#add8e6", - "lightcoral": "#f08080", - "lightcyan": "#e0ffff", - "lightgoldenrodyellow": "#fafad2", - "lightgreen": "#90ee90", - "lightgray": "#d3d3d3", - "lightgrey": "#d3d3d3", - "lightpink": "#ffb6c1", - "lightsalmon": "#ffa07a", - "lightseagreen": "#20b2aa", - "lightskyblue": "#87cefa", - "lightslategray": "#778899", - "lightslategrey": "#778899", - "lightsteelblue": "#b0c4de", - "lightyellow": "#ffffe0", - "lime": "#00ff00", - "limegreen": "#32cd32", - "linen": "#faf0e6", - "magenta": "#ff00ff", - "maroon": "#800000", - "mediumaquamarine": "#66cdaa", - "mediumblue": "#0000cd", - "mediumorchid": "#ba55d3", - "mediumpurple": "#9370db", - "mediumseagreen": "#3cb371", - "mediumslateblue": "#7b68ee", - "mediumspringgreen": "#00fa9a", - "mediumturquoise": "#48d1cc", - "mediumvioletred": "#c71585", - "midnightblue": "#191970", - "mintcream": "#f5fffa", - "mistyrose": "#ffe4e1", - "moccasin": "#ffe4b5", - "navajowhite": "#ffdead", - "navy": "#000080", - "oldlace": "#fdf5e6", - "olive": "#808000", - "olivedrab": "#6b8e23", - "orange": "#ffa500", - "orangered": "#ff4500", - "orchid": "#da70d6", - "palegoldenrod": "#eee8aa", - "palegreen": "#98fb98", - "paleturquoise": "#afeeee", - "palevioletred": "#db7093", - "papayawhip": "#ffefd5", - "peachpuff": "#ffdab9", - "peru": "#cd853f", - "pink": "#ffc0cb", - "plum": "#dda0dd", - "powderblue": "#b0e0e6", - "purple": "#800080", - "rebeccapurple": "#663399", - "red": "#ff0000", - "rosybrown": "#bc8f8f", - "royalblue": "#4169e1", - "saddlebrown": "#8b4513", - "salmon": "#fa8072", - "sandybrown": "#f4a460", - "seagreen": "#2e8b57", - "seashell": "#fff5ee", - "sienna": "#a0522d", - "silver": "#c0c0c0", - "skyblue": "#87ceeb", - "slateblue": "#6a5acd", - "slategray": "#708090", - "slategrey": "#708090", - "snow": "#fffafa", - "springgreen": "#00ff7f", - "steelblue": "#4682b4", - "tan": "#d2b48c", - "teal": "#008080", - "thistle": "#d8bfd8", - "tomato": "#ff6347", - "turquoise": "#40e0d0", - "violet": "#ee82ee", - "wheat": "#f5deb3", - "white": "#ffffff", - "whitesmoke": "#f5f5f5", - "yellow": "#ffff00", - "yellowgreen": "#9acd32", -} diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageDraw.py b/.venv/lib/python3.12/site-packages/PIL/ImageDraw.py deleted file mode 100644 index 9b0864d1..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageDraw.py +++ /dev/null @@ -1,1035 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# drawing interface operations -# -# History: -# 1996-04-13 fl Created (experimental) -# 1996-08-07 fl Filled polygons, ellipses. -# 1996-08-13 fl Added text support -# 1998-06-28 fl Handle I and F images -# 1998-12-29 fl Added arc; use arc primitive to draw ellipses -# 1999-01-10 fl Added shape stuff (experimental) -# 1999-02-06 fl Added bitmap support -# 1999-02-11 fl Changed all primitives to take options -# 1999-02-20 fl Fixed backwards compatibility -# 2000-10-12 fl Copy on write, when necessary -# 2001-02-18 fl Use default ink for bitmap/text also in fill mode -# 2002-10-24 fl Added support for CSS-style color strings -# 2002-12-10 fl Added experimental support for RGBA-on-RGB drawing -# 2002-12-11 fl Refactored low-level drawing API (work in progress) -# 2004-08-26 fl Made Draw() a factory function, added getdraw() support -# 2004-09-04 fl Added width support to line primitive -# 2004-09-10 fl Added font mode handling -# 2006-06-19 fl Added font bearing support (getmask2) -# -# Copyright (c) 1997-2006 by Secret Labs AB -# Copyright (c) 1996-2006 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import math -import struct -from collections.abc import Sequence -from typing import cast - -from . import Image, ImageColor, ImageText - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from types import ModuleType - from typing import Any, AnyStr - - from . import ImageDraw2, ImageFont - from ._typing import Coords, _Ink - -# experimental access to the outline API -Outline: Callable[[], Image.core._Outline] = Image.core.outline - -""" -A simple 2D drawing interface for PIL images. -

-Application code should use the Draw factory, instead of -directly. -""" - - -class ImageDraw: - font: ( - ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont | None - ) = None - - def __init__(self, im: Image.Image, mode: str | None = None) -> None: - """ - Create a drawing instance. - - :param im: The image to draw in. - :param mode: Optional mode to use for color values. For RGB - images, this argument can be RGB or RGBA (to blend the - drawing into the image). For all other modes, this argument - must be the same as the image mode. If omitted, the mode - defaults to the mode of the image. - """ - im._ensure_mutable() - blend = 0 - if mode is None: - mode = im.mode - if mode != im.mode: - if mode == "RGBA" and im.mode == "RGB": - blend = 1 - else: - msg = "mode mismatch" - raise ValueError(msg) - if mode == "P": - self.palette = im.palette - else: - self.palette = None - self._image = im - self.im = im.im - self.draw = Image.core.draw(self.im, blend) - self.mode = mode - if mode in ("I", "F"): - self.ink = self.draw.draw_ink(1) - else: - self.ink = self.draw.draw_ink(-1) - if mode in ("1", "P", "I", "F"): - # FIXME: fix Fill2 to properly support matte for I+F images - self.fontmode = "1" - else: - self.fontmode = "L" # aliasing is okay for other modes - self.fill = False - - def getfont( - self, - ) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont: - """ - Get the current default font. - - To set the default font for this ImageDraw instance:: - - from PIL import ImageDraw, ImageFont - draw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") - - To set the default font for all future ImageDraw instances:: - - from PIL import ImageDraw, ImageFont - ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") - - If the current default font is ``None``, - it is initialized with ``ImageFont.load_default()``. - - :returns: An image font.""" - if not self.font: - # FIXME: should add a font repository - from . import ImageFont - - self.font = ImageFont.load_default() - return self.font - - def _getfont( - self, font_size: float | None - ) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont: - if font_size is not None: - from . import ImageFont - - return ImageFont.load_default(font_size) - else: - return self.getfont() - - def _getink( - self, ink: _Ink | None, fill: _Ink | None = None - ) -> tuple[int | None, int | None]: - result_ink = None - result_fill = None - if ink is None and fill is None: - if self.fill: - result_fill = self.ink - else: - result_ink = self.ink - else: - if ink is not None: - if isinstance(ink, str): - ink = ImageColor.getcolor(ink, self.mode) - if self.palette and isinstance(ink, tuple): - ink = self.palette.getcolor(ink, self._image) - result_ink = self.draw.draw_ink(ink) - if fill is not None: - if isinstance(fill, str): - fill = ImageColor.getcolor(fill, self.mode) - if self.palette and isinstance(fill, tuple): - fill = self.palette.getcolor(fill, self._image) - result_fill = self.draw.draw_ink(fill) - return result_ink, result_fill - - def arc( - self, - xy: Coords, - start: float, - end: float, - fill: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw an arc.""" - ink, fill = self._getink(fill) - if ink is not None: - self.draw.draw_arc(xy, start, end, ink, width) - - def bitmap( - self, xy: Sequence[int], bitmap: Image.Image, fill: _Ink | None = None - ) -> None: - """Draw a bitmap.""" - bitmap.load() - ink, fill = self._getink(fill) - if ink is None: - ink = fill - if ink is not None: - self.draw.draw_bitmap(xy, bitmap.im, ink) - - def chord( - self, - xy: Coords, - start: float, - end: float, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a chord.""" - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_chord(xy, start, end, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - self.draw.draw_chord(xy, start, end, ink, 0, width) - - def ellipse( - self, - xy: Coords, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw an ellipse.""" - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_ellipse(xy, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - self.draw.draw_ellipse(xy, ink, 0, width) - - def circle( - self, - xy: Sequence[float], - radius: float, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a circle given center coordinates and a radius.""" - ellipse_xy = (xy[0] - radius, xy[1] - radius, xy[0] + radius, xy[1] + radius) - self.ellipse(ellipse_xy, fill, outline, width) - - def line( - self, - xy: Coords, - fill: _Ink | None = None, - width: int = 0, - joint: str | None = None, - ) -> None: - """Draw a line, or a connected sequence of line segments.""" - ink = self._getink(fill)[0] - if ink is not None: - self.draw.draw_lines(xy, ink, width) - if joint == "curve" and width > 4: - points: Sequence[Sequence[float]] - if isinstance(xy[0], (list, tuple)): - points = cast(Sequence[Sequence[float]], xy) - else: - points = [ - cast(Sequence[float], tuple(xy[i : i + 2])) - for i in range(0, len(xy), 2) - ] - for i in range(1, len(points) - 1): - point = points[i] - angles = [ - math.degrees(math.atan2(end[0] - start[0], start[1] - end[1])) - % 360 - for start, end in ( - (points[i - 1], point), - (point, points[i + 1]), - ) - ] - if angles[0] == angles[1]: - # This is a straight line, so no joint is required - continue - - def coord_at_angle( - coord: Sequence[float], angle: float - ) -> tuple[float, ...]: - x, y = coord - angle -= 90 - distance = width / 2 - 1 - return tuple( - p + (math.floor(p_d) if p_d > 0 else math.ceil(p_d)) - for p, p_d in ( - (x, distance * math.cos(math.radians(angle))), - (y, distance * math.sin(math.radians(angle))), - ) - ) - - flipped = ( - angles[1] > angles[0] and angles[1] - 180 > angles[0] - ) or (angles[1] < angles[0] and angles[1] + 180 > angles[0]) - coords = [ - (point[0] - width / 2 + 1, point[1] - width / 2 + 1), - (point[0] + width / 2 - 1, point[1] + width / 2 - 1), - ] - if flipped: - start, end = (angles[1] + 90, angles[0] + 90) - else: - start, end = (angles[0] - 90, angles[1] - 90) - self.pieslice(coords, start - 90, end - 90, fill) - - if width > 8: - # Cover potential gaps between the line and the joint - if flipped: - gap_coords = [ - coord_at_angle(point, angles[0] + 90), - point, - coord_at_angle(point, angles[1] + 90), - ] - else: - gap_coords = [ - coord_at_angle(point, angles[0] - 90), - point, - coord_at_angle(point, angles[1] - 90), - ] - self.line(gap_coords, fill, width=3) - - def shape( - self, - shape: Image.core._Outline, - fill: _Ink | None = None, - outline: _Ink | None = None, - ) -> None: - """(Experimental) Draw a shape.""" - shape.close() - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_outline(shape, fill_ink, 1) - if ink is not None and ink != fill_ink: - self.draw.draw_outline(shape, ink, 0) - - def pieslice( - self, - xy: Coords, - start: float, - end: float, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a pieslice.""" - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_pieslice(xy, start, end, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - self.draw.draw_pieslice(xy, start, end, ink, 0, width) - - def point(self, xy: Coords, fill: _Ink | None = None) -> None: - """Draw one or more individual pixels.""" - ink, fill = self._getink(fill) - if ink is not None: - self.draw.draw_points(xy, ink) - - def polygon( - self, - xy: Coords, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a polygon.""" - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_polygon(xy, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - if width == 1: - self.draw.draw_polygon(xy, ink, 0, width) - elif self.im is not None: - # To avoid expanding the polygon outwards, - # use the fill as a mask - mask = Image.new("1", self.im.size) - mask_ink = self._getink(1)[0] - draw = Draw(mask) - draw.draw.draw_polygon(xy, mask_ink, 1) - - self.draw.draw_polygon(xy, ink, 0, width * 2 - 1, mask.im) - - def regular_polygon( - self, - bounding_circle: Sequence[Sequence[float] | float], - n_sides: int, - rotation: float = 0, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a regular polygon.""" - xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) - self.polygon(xy, fill, outline, width) - - def rectangle( - self, - xy: Coords, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a rectangle.""" - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_rectangle(xy, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - self.draw.draw_rectangle(xy, ink, 0, width) - - def rounded_rectangle( - self, - xy: Coords, - radius: float = 0, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - *, - corners: tuple[bool, bool, bool, bool] | None = None, - ) -> None: - """Draw a rounded rectangle.""" - if isinstance(xy[0], (list, tuple)): - (x0, y0), (x1, y1) = cast(Sequence[Sequence[float]], xy) - else: - x0, y0, x1, y1 = cast(Sequence[float], xy) - if x1 < x0: - msg = "x1 must be greater than or equal to x0" - raise ValueError(msg) - if y1 < y0: - msg = "y1 must be greater than or equal to y0" - raise ValueError(msg) - if corners is None: - corners = (True, True, True, True) - - d = radius * 2 - - x0 = round(x0) - y0 = round(y0) - x1 = round(x1) - y1 = round(y1) - full_x, full_y = False, False - if all(corners): - full_x = d >= x1 - x0 - 1 - if full_x: - # The two left and two right corners are joined - d = x1 - x0 - full_y = d >= y1 - y0 - 1 - if full_y: - # The two top and two bottom corners are joined - d = y1 - y0 - if full_x and full_y: - # If all corners are joined, that is a circle - return self.ellipse(xy, fill, outline, width) - - if d == 0 or not any(corners): - # If the corners have no curve, - # or there are no corners, - # that is a rectangle - return self.rectangle(xy, fill, outline, width) - - r = int(d // 2) - ink, fill_ink = self._getink(outline, fill) - - def draw_corners(pieslice: bool) -> None: - parts: tuple[tuple[tuple[float, float, float, float], int, int], ...] - if full_x: - # Draw top and bottom halves - parts = ( - ((x0, y0, x0 + d, y0 + d), 180, 360), - ((x0, y1 - d, x0 + d, y1), 0, 180), - ) - elif full_y: - # Draw left and right halves - parts = ( - ((x0, y0, x0 + d, y0 + d), 90, 270), - ((x1 - d, y0, x1, y0 + d), 270, 90), - ) - else: - # Draw four separate corners - parts = tuple( - part - for i, part in enumerate( - ( - ((x0, y0, x0 + d, y0 + d), 180, 270), - ((x1 - d, y0, x1, y0 + d), 270, 360), - ((x1 - d, y1 - d, x1, y1), 0, 90), - ((x0, y1 - d, x0 + d, y1), 90, 180), - ) - ) - if corners[i] - ) - for part in parts: - if pieslice: - self.draw.draw_pieslice(*(part + (fill_ink, 1))) - else: - self.draw.draw_arc(*(part + (ink, width))) - - if fill_ink is not None: - draw_corners(True) - - if full_x: - self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill_ink, 1) - elif x1 - r - 1 >= x0 + r + 1: - self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill_ink, 1) - if not full_x and not full_y: - left = [x0, y0, x0 + r, y1] - if corners[0]: - left[1] += r + 1 - if corners[3]: - left[3] -= r + 1 - self.draw.draw_rectangle(left, fill_ink, 1) - - right = [x1 - r, y0, x1, y1] - if corners[1]: - right[1] += r + 1 - if corners[2]: - right[3] -= r + 1 - self.draw.draw_rectangle(right, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - draw_corners(False) - - if not full_x: - top = [x0, y0, x1, y0 + width - 1] - if corners[0]: - top[0] += r + 1 - if corners[1]: - top[2] -= r + 1 - self.draw.draw_rectangle(top, ink, 1) - - bottom = [x0, y1 - width + 1, x1, y1] - if corners[3]: - bottom[0] += r + 1 - if corners[2]: - bottom[2] -= r + 1 - self.draw.draw_rectangle(bottom, ink, 1) - if not full_y: - left = [x0, y0, x0 + width - 1, y1] - if corners[0]: - left[1] += r + 1 - if corners[3]: - left[3] -= r + 1 - self.draw.draw_rectangle(left, ink, 1) - - right = [x1 - width + 1, y0, x1, y1] - if corners[1]: - right[1] += r + 1 - if corners[2]: - right[3] -= r + 1 - self.draw.draw_rectangle(right, ink, 1) - - def text( - self, - xy: tuple[float, float], - text: AnyStr | ImageText.Text[AnyStr], - fill: _Ink | None = None, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - anchor: str | None = None, - spacing: float = 4, - align: str = "left", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - stroke_fill: _Ink | None = None, - embedded_color: bool = False, - *args: Any, - **kwargs: Any, - ) -> None: - """Draw text.""" - if isinstance(text, ImageText.Text): - image_text = text - else: - if font is None: - font = self._getfont(kwargs.get("font_size")) - image_text = ImageText.Text( - text, font, self.mode, spacing, direction, features, language - ) - if embedded_color: - image_text.embed_color() - if stroke_width: - image_text.stroke(stroke_width, stroke_fill) - - def getink(fill: _Ink | None) -> int: - ink, fill_ink = self._getink(fill) - if ink is None: - assert fill_ink is not None - return fill_ink - return ink - - ink = getink(fill) - if ink is None: - return - - stroke_ink = None - if image_text.stroke_width: - stroke_ink = ( - getink(image_text.stroke_fill) - if image_text.stroke_fill is not None - else ink - ) - - for line in image_text._split(xy, anchor, align): - - def draw_text(ink: int, stroke_width: float = 0) -> None: - mode = self.fontmode - if stroke_width == 0 and embedded_color: - mode = "RGBA" - x = int(line.x) - y = int(line.y) - start = (math.modf(line.x)[0], math.modf(line.y)[0]) - try: - mask, offset = image_text.font.getmask2( # type: ignore[union-attr,misc] - line.text, - mode, - direction=direction, - features=features, - language=language, - stroke_width=stroke_width, - stroke_filled=True, - anchor=line.anchor, - ink=ink, - start=start, - *args, - **kwargs, - ) - x += offset[0] - y += offset[1] - except AttributeError: - try: - mask = image_text.font.getmask( # type: ignore[misc] - line.text, - mode, - direction, - features, - language, - stroke_width, - line.anchor, - ink, - start=start, - *args, - **kwargs, - ) - except TypeError: - mask = image_text.font.getmask(line.text) - if mode == "RGBA": - # image_text.font.getmask2(mode="RGBA") - # returns color in RGB bands and mask in A - # extract mask and set text alpha - color, mask = mask, mask.getband(3) - ink_alpha = struct.pack("i", ink)[3] - color.fillband(3, ink_alpha) - if self.im is not None: - self.im.paste( - color, (x, y, x + mask.size[0], y + mask.size[1]), mask - ) - else: - self.draw.draw_bitmap((x, y), mask, ink) - - if stroke_ink is not None: - # Draw stroked text - draw_text(stroke_ink, image_text.stroke_width) - - # Draw normal text - if ink != stroke_ink: - draw_text(ink) - else: - # Only draw normal text - draw_text(ink) - - def multiline_text( - self, - xy: tuple[float, float], - text: AnyStr, - fill: _Ink | None = None, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - anchor: str | None = None, - spacing: float = 4, - align: str = "left", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - stroke_fill: _Ink | None = None, - embedded_color: bool = False, - *, - font_size: float | None = None, - ) -> None: - return self.text( - xy, - text, - fill, - font, - anchor, - spacing, - align, - direction, - features, - language, - stroke_width, - stroke_fill, - embedded_color, - font_size=font_size, - ) - - def textlength( - self, - text: AnyStr, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - embedded_color: bool = False, - *, - font_size: float | None = None, - ) -> float: - """Get the length of a given string, in pixels with 1/64 precision.""" - if font is None: - font = self._getfont(font_size) - image_text = ImageText.Text( - text, - font, - self.mode, - direction=direction, - features=features, - language=language, - ) - if embedded_color: - image_text.embed_color() - return image_text.get_length() - - def textbbox( - self, - xy: tuple[float, float], - text: AnyStr, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - anchor: str | None = None, - spacing: float = 4, - align: str = "left", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - embedded_color: bool = False, - *, - font_size: float | None = None, - ) -> tuple[float, float, float, float]: - """Get the bounding box of a given string, in pixels.""" - if font is None: - font = self._getfont(font_size) - image_text = ImageText.Text( - text, font, self.mode, spacing, direction, features, language - ) - if embedded_color: - image_text.embed_color() - if stroke_width: - image_text.stroke(stroke_width) - return image_text.get_bbox(xy, anchor, align) - - def multiline_textbbox( - self, - xy: tuple[float, float], - text: AnyStr, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - anchor: str | None = None, - spacing: float = 4, - align: str = "left", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - embedded_color: bool = False, - *, - font_size: float | None = None, - ) -> tuple[float, float, float, float]: - return self.textbbox( - xy, - text, - font, - anchor, - spacing, - align, - direction, - features, - language, - stroke_width, - embedded_color, - font_size=font_size, - ) - - -def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw: - """ - A simple 2D drawing interface for PIL images. - - :param im: The image to draw in. - :param mode: Optional mode to use for color values. For RGB - images, this argument can be RGB or RGBA (to blend the - drawing into the image). For all other modes, this argument - must be the same as the image mode. If omitted, the mode - defaults to the mode of the image. - """ - try: - return getattr(im, "getdraw")(mode) - except AttributeError: - return ImageDraw(im, mode) - - -def getdraw(im: Image.Image | None = None) -> tuple[ImageDraw2.Draw | None, ModuleType]: - """ - :param im: The image to draw in. - :returns: A (drawing context, drawing resource factory) tuple. - """ - from . import ImageDraw2 - - draw = ImageDraw2.Draw(im) if im is not None else None - return draw, ImageDraw2 - - -def floodfill( - image: Image.Image, - xy: tuple[int, int], - value: float | tuple[int, ...], - border: float | tuple[int, ...] | None = None, - thresh: float = 0, -) -> None: - """ - .. warning:: This method is experimental. - - Fills a bounded region with a given color. - - :param image: Target image. - :param xy: Seed position (a 2-item coordinate tuple). See - :ref:`coordinate-system`. - :param value: Fill color. - :param border: Optional border value. If given, the region consists of - pixels with a color different from the border color. If not given, - the region consists of pixels having the same color as the seed - pixel. - :param thresh: Optional threshold value which specifies a maximum - tolerable difference of a pixel value from the 'background' in - order for it to be replaced. Useful for filling regions of - non-homogeneous, but similar, colors. - """ - # based on an implementation by Eric S. Raymond - # amended by yo1995 @20180806 - pixel = image.load() - assert pixel is not None - x, y = xy - try: - background = pixel[x, y] - if _color_diff(value, background) <= thresh: - return # seed point already has fill color - pixel[x, y] = value - except (ValueError, IndexError): - return # seed point outside image - edge = {(x, y)} - # use a set to keep record of current and previous edge pixels - # to reduce memory consumption - full_edge = set() - while edge: - new_edge = set() - for x, y in edge: # 4 adjacent method - for s, t in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)): - # If already processed, or if a coordinate is negative, skip - if (s, t) in full_edge or s < 0 or t < 0: - continue - try: - p = pixel[s, t] - except (ValueError, IndexError): - pass - else: - full_edge.add((s, t)) - if border is None: - fill = _color_diff(p, background) <= thresh - else: - fill = p not in (value, border) - if fill: - pixel[s, t] = value - new_edge.add((s, t)) - full_edge = edge # discard pixels processed - edge = new_edge - - -def _compute_regular_polygon_vertices( - bounding_circle: Sequence[Sequence[float] | float], n_sides: int, rotation: float -) -> list[tuple[float, float]]: - """ - Generate a list of vertices for a 2D regular polygon. - - :param bounding_circle: The bounding circle is a sequence defined - by a point and radius. The polygon is inscribed in this circle. - (e.g. ``bounding_circle=(x, y, r)`` or ``((x, y), r)``) - :param n_sides: Number of sides - (e.g. ``n_sides=3`` for a triangle, ``6`` for a hexagon) - :param rotation: Apply an arbitrary rotation to the polygon - (e.g. ``rotation=90``, applies a 90 degree rotation) - :return: List of regular polygon vertices - (e.g. ``[(25, 50), (50, 50), (50, 25), (25, 25)]``) - - How are the vertices computed? - 1. Compute the following variables - - theta: Angle between the apothem & the nearest polygon vertex - - side_length: Length of each polygon edge - - centroid: Center of bounding circle (1st, 2nd elements of bounding_circle) - - polygon_radius: Polygon radius (last element of bounding_circle) - - angles: Location of each polygon vertex in polar grid - (e.g. A square with 0 degree rotation => [225.0, 315.0, 45.0, 135.0]) - - 2. For each angle in angles, get the polygon vertex at that angle - The vertex is computed using the equation below. - X= xcos(φ) + ysin(φ) - Y= −xsin(φ) + ycos(φ) - - Note: - φ = angle in degrees - x = 0 - y = polygon_radius - - The formula above assumes rotation around the origin. - In our case, we are rotating around the centroid. - To account for this, we use the formula below - X = xcos(φ) + ysin(φ) + centroid_x - Y = −xsin(φ) + ycos(φ) + centroid_y - """ - # 1. Error Handling - # 1.1 Check `n_sides` has an appropriate value - if not isinstance(n_sides, int): - msg = "n_sides should be an int" # type: ignore[unreachable] - raise TypeError(msg) - if n_sides < 3: - msg = "n_sides should be an int > 2" - raise ValueError(msg) - - # 1.2 Check `bounding_circle` has an appropriate value - if not isinstance(bounding_circle, (list, tuple)): - msg = "bounding_circle should be a sequence" - raise TypeError(msg) - - if len(bounding_circle) == 3: - if not all(isinstance(i, (int, float)) for i in bounding_circle): - msg = "bounding_circle should only contain numeric data" - raise ValueError(msg) - - *centroid, polygon_radius = cast(list[float], list(bounding_circle)) - elif len(bounding_circle) == 2 and isinstance(bounding_circle[0], (list, tuple)): - if not all( - isinstance(i, (int, float)) for i in bounding_circle[0] - ) or not isinstance(bounding_circle[1], (int, float)): - msg = "bounding_circle should only contain numeric data" - raise ValueError(msg) - - if len(bounding_circle[0]) != 2: - msg = "bounding_circle centre should contain 2D coordinates (e.g. (x, y))" - raise ValueError(msg) - - centroid = cast(list[float], list(bounding_circle[0])) - polygon_radius = cast(float, bounding_circle[1]) - else: - msg = ( - "bounding_circle should contain 2D coordinates " - "and a radius (e.g. (x, y, r) or ((x, y), r) )" - ) - raise ValueError(msg) - - if polygon_radius <= 0: - msg = "bounding_circle radius should be > 0" - raise ValueError(msg) - - # 1.3 Check `rotation` has an appropriate value - if not isinstance(rotation, (int, float)): - msg = "rotation should be an int or float" # type: ignore[unreachable] - raise ValueError(msg) - - # 2. Define Helper Functions - def _apply_rotation(point: list[float], degrees: float) -> tuple[float, float]: - return ( - round( - point[0] * math.cos(math.radians(360 - degrees)) - - point[1] * math.sin(math.radians(360 - degrees)) - + centroid[0], - 2, - ), - round( - point[1] * math.cos(math.radians(360 - degrees)) - + point[0] * math.sin(math.radians(360 - degrees)) - + centroid[1], - 2, - ), - ) - - def _compute_polygon_vertex(angle: float) -> tuple[float, float]: - start_point = [polygon_radius, 0] - return _apply_rotation(start_point, angle) - - def _get_angles(n_sides: int, rotation: float) -> list[float]: - angles = [] - degrees = 360 / n_sides - # Start with the bottom left polygon vertex - current_angle = (270 - 0.5 * degrees) + rotation - for _ in range(n_sides): - angles.append(current_angle) - current_angle += degrees - if current_angle > 360: - current_angle -= 360 - return angles - - # 3. Variable Declarations - angles = _get_angles(n_sides, rotation) - - # 4. Compute Vertices - return [_compute_polygon_vertex(angle) for angle in angles] - - -def _color_diff( - color1: float | tuple[int, ...], color2: float | tuple[int, ...] -) -> float: - """ - Uses 1-norm distance to calculate difference between two values. - """ - first = color1 if isinstance(color1, tuple) else (color1,) - second = color2 if isinstance(color2, tuple) else (color2,) - - return sum(abs(first[i] - second[i]) for i in range(len(second))) diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageDraw2.py b/.venv/lib/python3.12/site-packages/PIL/ImageDraw2.py deleted file mode 100644 index 2c9e39b2..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageDraw2.py +++ /dev/null @@ -1,244 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# WCK-style drawing interface operations -# -# History: -# 2003-12-07 fl created -# 2005-05-15 fl updated; added to PIL as ImageDraw2 -# 2005-05-15 fl added text support -# 2005-05-20 fl added arc/chord/pieslice support -# -# Copyright (c) 2003-2005 by Secret Labs AB -# Copyright (c) 2003-2005 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - - -""" -(Experimental) WCK-style drawing interface operations - -.. seealso:: :py:mod:`PIL.ImageDraw` -""" - -from __future__ import annotations - -from typing import Any, AnyStr, BinaryIO - -from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath -from ._typing import Coords, StrOrBytesPath - - -class Pen: - """Stores an outline color and width.""" - - def __init__(self, color: str, width: int = 1, opacity: int = 255) -> None: - self.color = ImageColor.getrgb(color) - self.width = width - - -class Brush: - """Stores a fill color""" - - def __init__(self, color: str, opacity: int = 255) -> None: - self.color = ImageColor.getrgb(color) - - -class Font: - """Stores a TrueType font and color""" - - def __init__( - self, color: str, file: StrOrBytesPath | BinaryIO, size: float = 12 - ) -> None: - # FIXME: add support for bitmap fonts - self.color = ImageColor.getrgb(color) - self.font = ImageFont.truetype(file, size) - - -class Draw: - """ - (Experimental) WCK-style drawing interface - """ - - def __init__( - self, - image: Image.Image | str, - size: tuple[int, int] | list[int] | None = None, - color: float | tuple[float, ...] | str | None = None, - ) -> None: - if isinstance(image, str): - if size is None: - msg = "If image argument is mode string, size must be a list or tuple" - raise ValueError(msg) - image = Image.new(image, size, color) - self.draw = ImageDraw.Draw(image) - self.image = image - self.transform: tuple[float, float, float, float, float, float] | None = None - - def flush(self) -> Image.Image: - return self.image - - def render( - self, - op: str, - xy: Coords, - pen: Pen | Brush | None, - brush: Brush | Pen | None = None, - **kwargs: Any, - ) -> None: - # handle color arguments - outline = fill = None - width = 1 - if isinstance(pen, Pen): - outline = pen.color - width = pen.width - elif isinstance(brush, Pen): - outline = brush.color - width = brush.width - if isinstance(brush, Brush): - fill = brush.color - elif isinstance(pen, Brush): - fill = pen.color - # handle transformation - if self.transform: - path = ImagePath.Path(xy) - path.transform(self.transform) - xy = path - # render the item - if op in ("arc", "line"): - kwargs.setdefault("fill", outline) - else: - kwargs.setdefault("fill", fill) - kwargs.setdefault("outline", outline) - if op == "line": - kwargs.setdefault("width", width) - getattr(self.draw, op)(xy, **kwargs) - - def settransform(self, offset: tuple[float, float]) -> None: - """Sets a transformation offset.""" - xoffset, yoffset = offset - self.transform = (1, 0, xoffset, 0, 1, yoffset) - - def arc( - self, - xy: Coords, - pen: Pen | Brush | None, - start: float, - end: float, - *options: Any, - ) -> None: - """ - Draws an arc (a portion of a circle outline) between the start and end - angles, inside the given bounding box. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.arc` - """ - self.render("arc", xy, pen, *options, start=start, end=end) - - def chord( - self, - xy: Coords, - pen: Pen | Brush | None, - start: float, - end: float, - *options: Any, - ) -> None: - """ - Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points - with a straight line. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord` - """ - self.render("chord", xy, pen, *options, start=start, end=end) - - def ellipse(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: - """ - Draws an ellipse inside the given bounding box. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse` - """ - self.render("ellipse", xy, pen, *options) - - def line(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: - """ - Draws a line between the coordinates in the ``xy`` list. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line` - """ - self.render("line", xy, pen, *options) - - def pieslice( - self, - xy: Coords, - pen: Pen | Brush | None, - start: float, - end: float, - *options: Any, - ) -> None: - """ - Same as arc, but also draws straight lines between the end points and the - center of the bounding box. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.pieslice` - """ - self.render("pieslice", xy, pen, *options, start=start, end=end) - - def polygon(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: - """ - Draws a polygon. - - The polygon outline consists of straight lines between the given - coordinates, plus a straight line between the last and the first - coordinate. - - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.polygon` - """ - self.render("polygon", xy, pen, *options) - - def rectangle(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: - """ - Draws a rectangle. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle` - """ - self.render("rectangle", xy, pen, *options) - - def text(self, xy: tuple[float, float], text: AnyStr, font: Font) -> None: - """ - Draws the string at the given position. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text` - """ - if self.transform: - path = ImagePath.Path(xy) - path.transform(self.transform) - xy = path - self.draw.text(xy, text, font=font.font, fill=font.color) - - def textbbox( - self, xy: tuple[float, float], text: AnyStr, font: Font - ) -> tuple[float, float, float, float]: - """ - Returns bounding box (in pixels) of given text. - - :return: ``(left, top, right, bottom)`` bounding box - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textbbox` - """ - if self.transform: - path = ImagePath.Path(xy) - path.transform(self.transform) - xy = path - return self.draw.textbbox(xy, text, font=font.font) - - def textlength(self, text: AnyStr, font: Font) -> float: - """ - Returns length (in pixels) of given text. - This is the amount by which following text should be offset. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textlength` - """ - return self.draw.textlength(text, font=font.font) diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageEnhance.py b/.venv/lib/python3.12/site-packages/PIL/ImageEnhance.py deleted file mode 100644 index 0e7e6dd8..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageEnhance.py +++ /dev/null @@ -1,113 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# image enhancement classes -# -# For a background, see "Image Processing By Interpolation and -# Extrapolation", Paul Haeberli and Douglas Voorhies. Available -# at http://www.graficaobscura.com/interp/index.html -# -# History: -# 1996-03-23 fl Created -# 2009-06-16 fl Fixed mean calculation -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFilter, ImageStat - - -class _Enhance: - image: Image.Image - degenerate: Image.Image - - def enhance(self, factor: float) -> Image.Image: - """ - Returns an enhanced image. - - :param factor: A floating point value controlling the enhancement. - Factor 1.0 always returns a copy of the original image, - lower factors mean less color (brightness, contrast, - etc), and higher values more. There are no restrictions - on this value. - :rtype: :py:class:`~PIL.Image.Image` - """ - return Image.blend(self.degenerate, self.image, factor) - - -class Color(_Enhance): - """Adjust image color balance. - - This class can be used to adjust the colour balance of an image, in - a manner similar to the controls on a colour TV set. An enhancement - factor of 0.0 gives a black and white image. A factor of 1.0 gives - the original image. - """ - - def __init__(self, image: Image.Image) -> None: - self.image = image - self.intermediate_mode = "L" - if "A" in image.getbands(): - self.intermediate_mode = "LA" - - if self.intermediate_mode != image.mode: - image = image.convert(self.intermediate_mode).convert(image.mode) - self.degenerate = image - - -class Contrast(_Enhance): - """Adjust image contrast. - - This class can be used to control the contrast of an image, similar - to the contrast control on a TV set. An enhancement factor of 0.0 - gives a solid gray image. A factor of 1.0 gives the original image. - """ - - def __init__(self, image: Image.Image) -> None: - self.image = image - if image.mode != "L": - image = image.convert("L") - mean = int(ImageStat.Stat(image).mean[0] + 0.5) - self.degenerate = Image.new("L", image.size, mean) - if self.degenerate.mode != self.image.mode: - self.degenerate = self.degenerate.convert(self.image.mode) - - if "A" in self.image.getbands(): - self.degenerate.putalpha(self.image.getchannel("A")) - - -class Brightness(_Enhance): - """Adjust image brightness. - - This class can be used to control the brightness of an image. An - enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the - original image. - """ - - def __init__(self, image: Image.Image) -> None: - self.image = image - self.degenerate = Image.new(image.mode, image.size, 0) - - if "A" in image.getbands(): - self.degenerate.putalpha(image.getchannel("A")) - - -class Sharpness(_Enhance): - """Adjust image sharpness. - - This class can be used to adjust the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the - original image, and a factor of 2.0 gives a sharpened image. - """ - - def __init__(self, image: Image.Image) -> None: - self.image = image - self.degenerate = image.filter(ImageFilter.SMOOTH) - - if "A" in image.getbands(): - self.degenerate.putalpha(image.getchannel("A")) diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageFile.py b/.venv/lib/python3.12/site-packages/PIL/ImageFile.py deleted file mode 100644 index c70d93f3..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageFile.py +++ /dev/null @@ -1,935 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# base class for image file handlers -# -# history: -# 1995-09-09 fl Created -# 1996-03-11 fl Fixed load mechanism. -# 1996-04-15 fl Added pcx/xbm decoders. -# 1996-04-30 fl Added encoders. -# 1996-12-14 fl Added load helpers -# 1997-01-11 fl Use encode_to_file where possible -# 1997-08-27 fl Flush output in _save -# 1998-03-05 fl Use memory mapping for some modes -# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B" -# 1999-05-31 fl Added image parser -# 2000-10-12 fl Set readonly flag on memory-mapped images -# 2002-03-20 fl Use better messages for common decoder errors -# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available -# 2003-10-30 fl Added StubImageFile class -# 2004-02-25 fl Made incremental parser more robust -# -# Copyright (c) 1997-2004 by Secret Labs AB -# Copyright (c) 1995-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import abc -import io -import itertools -import logging -import os -import struct -from typing import IO, Any, NamedTuple, cast - -from . import ExifTags, Image -from ._util import DeferredError, is_path - -TYPE_CHECKING = False -if TYPE_CHECKING: - from ._typing import StrOrBytesPath - -logger = logging.getLogger(__name__) - -MAXBLOCK = 65536 -""" -By default, Pillow processes image data in blocks. This helps to prevent excessive use -of resources. Codecs may disable this behaviour with ``_pulls_fd`` or ``_pushes_fd``. - -When reading an image, this is the number of bytes to read at once. - -When writing an image, this is the number of bytes to write at once. -If the image width times 4 is greater, then that will be used instead. -Plugins may also set a greater number. - -User code may set this to another number. -""" - -SAFEBLOCK = 1024 * 1024 - -LOAD_TRUNCATED_IMAGES = False -"""Whether or not to load truncated image files. User code may change this.""" - -ERRORS = { - -1: "image buffer overrun error", - -2: "decoding error", - -3: "unknown error", - -8: "bad configuration", - -9: "out of memory error", -} -""" -Dict of known error codes returned from :meth:`.PyDecoder.decode`, -:meth:`.PyEncoder.encode` :meth:`.PyEncoder.encode_to_pyfd` and -:meth:`.PyEncoder.encode_to_file`. -""" - - -# -# -------------------------------------------------------------------- -# Helpers - - -def _get_oserror(error: int, *, encoder: bool) -> OSError: - try: - msg = Image.core.getcodecstatus(error) - except AttributeError: - msg = ERRORS.get(error) - if not msg: - msg = f"{'encoder' if encoder else 'decoder'} error {error}" - msg += f" when {'writing' if encoder else 'reading'} image file" - return OSError(msg) - - -def _tilesort(t: _Tile) -> int: - # sort on offset - return t[2] - - -class _Tile(NamedTuple): - codec_name: str - extents: tuple[int, int, int, int] | None - offset: int = 0 - args: tuple[Any, ...] | str | None = None - - -# -# -------------------------------------------------------------------- -# ImageFile base class - - -class ImageFile(Image.Image): - """Base class for image file format handlers.""" - - def __init__( - self, fp: StrOrBytesPath | IO[bytes], filename: str | bytes | None = None - ) -> None: - super().__init__() - - self._min_frame = 0 - - self.custom_mimetype: str | None = None - - self.tile: list[_Tile] = [] - """ A list of tile descriptors """ - - self.readonly = 1 # until we know better - - self.decoderconfig: tuple[Any, ...] = () - self.decodermaxblock = MAXBLOCK - - self.fp: IO[bytes] | None - self._fp: IO[bytes] | DeferredError - if is_path(fp): - # filename - self.fp = open(fp, "rb") - self.filename = os.fspath(fp) - self._exclusive_fp = True - else: - # stream - self.fp = cast(IO[bytes], fp) - self.filename = filename if filename is not None else "" - # can be overridden - self._exclusive_fp = False - - try: - try: - self._open() - - if isinstance(self, StubImageFile): - if loader := self._load(): - loader.open(self) - except ( - IndexError, # end of data - TypeError, # end of data (ord) - KeyError, # unsupported mode - EOFError, # got header but not the first frame - struct.error, - ) as v: - raise SyntaxError(v) from v - - if not self.mode or self.size[0] <= 0 or self.size[1] <= 0: - msg = "not identified by this driver" - raise SyntaxError(msg) - except BaseException: - # close the file only if we have opened it this constructor - if self._exclusive_fp: - self.fp.close() - raise - - def _open(self) -> None: - pass - - # Context manager support - def __enter__(self) -> ImageFile: - return self - - def _close_fp(self) -> None: - if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError): - if self._fp != self.fp: - self._fp.close() - self._fp = DeferredError(ValueError("Operation on closed image")) - if self.fp: - self.fp.close() - - def __exit__(self, *args: object) -> None: - if getattr(self, "_exclusive_fp", False): - self._close_fp() - self.fp = None - - def close(self) -> None: - """ - Closes the file pointer, if possible. - - This operation will destroy the image core and release its memory. - The image data will be unusable afterward. - - This function is required to close images that have multiple frames or - have not had their file read and closed by the - :py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for - more information. - """ - try: - self._close_fp() - self.fp = None - except Exception as msg: - logger.debug("Error closing: %s", msg) - - super().close() - - def get_child_images(self) -> list[ImageFile]: - child_images = [] - exif = self.getexif() - ifds = [] - if ExifTags.Base.SubIFDs in exif: - subifd_offsets = exif[ExifTags.Base.SubIFDs] - if subifd_offsets: - if not isinstance(subifd_offsets, tuple): - subifd_offsets = (subifd_offsets,) - ifds = [ - (exif._get_ifd_dict(subifd_offset), subifd_offset) - for subifd_offset in subifd_offsets - ] - ifd1 = exif.get_ifd(ExifTags.IFD.IFD1) - if ifd1 and ifd1.get(ExifTags.Base.JpegIFOffset): - assert exif._info is not None - ifds.append((ifd1, exif._info.next)) - - offset = None - for ifd, ifd_offset in ifds: - assert self.fp is not None - current_offset = self.fp.tell() - if offset is None: - offset = current_offset - - fp = self.fp - if ifd is not None: - thumbnail_offset = ifd.get(ExifTags.Base.JpegIFOffset) - if thumbnail_offset is not None: - thumbnail_offset += getattr(self, "_exif_offset", 0) - self.fp.seek(thumbnail_offset) - - length = ifd.get(ExifTags.Base.JpegIFByteCount) - assert isinstance(length, int) - data = self.fp.read(length) - fp = io.BytesIO(data) - - with Image.open(fp) as im: - from . import TiffImagePlugin - - if thumbnail_offset is None and isinstance( - im, TiffImagePlugin.TiffImageFile - ): - im._frame_pos = [ifd_offset] - im._seek(0) - im.load() - child_images.append(im) - - if offset is not None: - assert self.fp is not None - self.fp.seek(offset) - return child_images - - def get_format_mimetype(self) -> str | None: - if self.custom_mimetype: - return self.custom_mimetype - if self.format is not None: - return Image.MIME.get(self.format.upper()) - return None - - def __getstate__(self) -> list[Any]: - return super().__getstate__() + [self.filename] - - def __setstate__(self, state: list[Any]) -> None: - self.tile = [] - if len(state) > 5: - self.filename = state[5] - super().__setstate__(state) - - def verify(self) -> None: - """Check file integrity""" - - # raise exception if something's wrong. must be called - # directly after open, and closes file when finished. - if self._exclusive_fp and self.fp: - self.fp.close() - self.fp = None - - def load(self) -> Image.core.PixelAccess | None: - """Load image data based on tile list""" - - if not self.tile and self._im is None: - msg = "cannot load this image" - raise OSError(msg) - - pixel = Image.Image.load(self) - if not self.tile: - return pixel - - self.map: mmap.mmap | None = None - use_mmap = self.filename and len(self.tile) == 1 - - assert self.fp is not None - readonly = 0 - - # look for read/seek overrides - if hasattr(self, "load_read"): - read = self.load_read - # don't use mmap if there are custom read/seek functions - use_mmap = False - else: - read = self.fp.read - - if hasattr(self, "load_seek"): - seek = self.load_seek - use_mmap = False - else: - seek = self.fp.seek - - if use_mmap: - # try memory mapping - decoder_name, extents, offset, args = self.tile[0] - if isinstance(args, str): - args = (args, 0, 1) - if ( - decoder_name == "raw" - and isinstance(args, tuple) - and len(args) >= 3 - and args[0] == self.mode - and args[0] in Image._MAPMODES - ): - if offset < 0: - msg = "Tile offset cannot be negative" - raise ValueError(msg) - try: - # use mmap, if possible - import mmap - - with open(self.filename) as fp: - self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) - if offset + self.size[1] * args[1] > self.map.size(): - msg = "buffer is not large enough" - raise OSError(msg) - self.im = Image.core.map_buffer( - self.map, self.size, decoder_name, offset, args - ) - readonly = 1 - # After trashing self.im, - # we might need to reload the palette data. - if self.palette: - self.palette.dirty = 1 - except (AttributeError, OSError, ImportError): - self.map = None - - self.load_prepare() - err_code = -3 # initialize to unknown error - if not self.map: - # sort tiles in file order - self.tile.sort(key=_tilesort) - - # FIXME: This is a hack to handle TIFF's JpegTables tag. - prefix = getattr(self, "tile_prefix", b"") - - # Remove consecutive duplicates that only differ by their offset - self.tile = [ - list(tiles)[-1] - for _, tiles in itertools.groupby( - self.tile, lambda tile: (tile[0], tile[1], tile[3]) - ) - ] - for i, (decoder_name, extents, offset, args) in enumerate(self.tile): - seek(offset) - decoder = Image._getdecoder( - self.mode, decoder_name, args, self.decoderconfig - ) - try: - decoder.setimage(self.im, extents) - if decoder.pulls_fd: - decoder.setfd(self.fp) - err_code = decoder.decode(b"")[1] - else: - b = prefix - while True: - read_bytes = self.decodermaxblock - if i + 1 < len(self.tile): - next_offset = self.tile[i + 1].offset - if next_offset > offset: - read_bytes = next_offset - offset - try: - s = read(read_bytes) - except (IndexError, struct.error) as e: - # truncated png/gif - if LOAD_TRUNCATED_IMAGES: - break - else: - msg = "image file is truncated" - raise OSError(msg) from e - - if not s: # truncated jpeg - if LOAD_TRUNCATED_IMAGES: - break - else: - msg = ( - "image file is truncated " - f"({len(b)} bytes not processed)" - ) - raise OSError(msg) - - b = b + s - n, err_code = decoder.decode(b) - if n < 0: - break - b = b[n:] - finally: - # Need to cleanup here to prevent leaks - decoder.cleanup() - - self.tile = [] - self.readonly = readonly - - self.load_end() - - if self._exclusive_fp and self._close_exclusive_fp_after_loading: - self.fp.close() - self.fp = None - - if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0: - # still raised if decoder fails to return anything - raise _get_oserror(err_code, encoder=False) - - return Image.Image.load(self) - - def load_prepare(self) -> None: - # create image memory if necessary - if self._im is None: - self.im = Image.core.new(self.mode, self.size) - # create palette (optional) - if self.mode == "P": - Image.Image.load(self) - - def load_end(self) -> None: - # may be overridden - pass - - # may be defined for contained formats - # def load_seek(self, pos: int) -> None: - # pass - - # may be defined for blocked formats (e.g. PNG) - # def load_read(self, read_bytes: int) -> bytes: - # pass - - def _seek_check(self, frame: int) -> bool: - if ( - frame < self._min_frame - # Only check upper limit on frames if additional seek operations - # are not required to do so - or ( - not (hasattr(self, "_n_frames") and self._n_frames is None) - and frame >= getattr(self, "n_frames") + self._min_frame - ) - ): - msg = "attempt to seek outside sequence" - raise EOFError(msg) - - return self.tell() != frame - - -class StubHandler(abc.ABC): - def open(self, im: StubImageFile) -> None: - pass - - @abc.abstractmethod - def load(self, im: StubImageFile) -> Image.Image: - pass - - -class StubImageFile(ImageFile, metaclass=abc.ABCMeta): - """ - Base class for stub image loaders. - - A stub loader is an image loader that can identify files of a - certain format, but relies on external code to load the file. - """ - - @abc.abstractmethod - def _open(self) -> None: - pass - - def load(self) -> Image.core.PixelAccess | None: - loader = self._load() - if loader is None: - msg = f"cannot find loader for this {self.format} file" - raise OSError(msg) - image = loader.load(self) - assert image is not None - # become the other object (!) - self.__class__ = image.__class__ # type: ignore[assignment] - self.__dict__ = image.__dict__ - return image.load() - - @abc.abstractmethod - def _load(self) -> StubHandler | None: - """(Hook) Find actual image loader.""" - pass - - -class Parser: - """ - Incremental image parser. This class implements the standard - feed/close consumer interface. - """ - - incremental = None - image: Image.Image | None = None - data: bytes | None = None - decoder: Image.core.ImagingDecoder | PyDecoder | None = None - offset = 0 - finished = 0 - - def reset(self) -> None: - """ - (Consumer) Reset the parser. Note that you can only call this - method immediately after you've created a parser; parser - instances cannot be reused. - """ - assert self.data is None, "cannot reuse parsers" - - def feed(self, data: bytes) -> None: - """ - (Consumer) Feed data to the parser. - - :param data: A string buffer. - :exception OSError: If the parser failed to parse the image file. - """ - # collect data - - if self.finished: - return - - if self.data is None: - self.data = data - else: - self.data = self.data + data - - # parse what we have - if self.decoder: - if self.offset > 0: - # skip header - skip = min(len(self.data), self.offset) - self.data = self.data[skip:] - self.offset = self.offset - skip - if self.offset > 0 or not self.data: - return - - n, e = self.decoder.decode(self.data) - - if n < 0: - # end of stream - self.data = None - self.finished = 1 - if e < 0: - # decoding error - self.image = None - raise _get_oserror(e, encoder=False) - else: - # end of image - return - self.data = self.data[n:] - - elif self.image: - # if we end up here with no decoder, this file cannot - # be incrementally parsed. wait until we've gotten all - # available data - pass - - else: - # attempt to open this file - try: - with io.BytesIO(self.data) as fp: - im = Image.open(fp) - except OSError: - pass # not enough data - else: - flag = hasattr(im, "load_seek") or hasattr(im, "load_read") - if not flag and len(im.tile) == 1: - # initialize decoder - im.load_prepare() - d, e, o, a = im.tile[0] - im.tile = [] - self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig) - self.decoder.setimage(im.im, e) - - # calculate decoder offset - self.offset = o - if self.offset <= len(self.data): - self.data = self.data[self.offset :] - self.offset = 0 - - self.image = im - - def __enter__(self) -> Parser: - return self - - def __exit__(self, *args: object) -> None: - self.close() - - def close(self) -> Image.Image: - """ - (Consumer) Close the stream. - - :returns: An image object. - :exception OSError: If the parser failed to parse the image file either - because it cannot be identified or cannot be - decoded. - """ - # finish decoding - if self.decoder: - # get rid of what's left in the buffers - self.feed(b"") - self.data = self.decoder = None - if not self.finished: - msg = "image was incomplete" - raise OSError(msg) - if not self.image: - msg = "cannot parse this image" - raise OSError(msg) - if self.data: - # incremental parsing not possible; reopen the file - # not that we have all data - with io.BytesIO(self.data) as fp: - try: - self.image = Image.open(fp) - finally: - self.image.load() - return self.image - - -# -------------------------------------------------------------------- - - -def _save(im: Image.Image, fp: IO[bytes], tile: list[_Tile], bufsize: int = 0) -> None: - """Helper to save image based on tile list - - :param im: Image object. - :param fp: File object. - :param tile: Tile list. - :param bufsize: Optional buffer size - """ - - im.load() - if not hasattr(im, "encoderconfig"): - im.encoderconfig = () - tile.sort(key=_tilesort) - # FIXME: make MAXBLOCK a configuration parameter - # It would be great if we could have the encoder specify what it needs - # But, it would need at least the image size in most cases. RawEncode is - # a tricky case. - bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c - try: - fh = fp.fileno() - fp.flush() - _encode_tile(im, fp, tile, bufsize, fh) - except (AttributeError, io.UnsupportedOperation) as exc: - _encode_tile(im, fp, tile, bufsize, None, exc) - if hasattr(fp, "flush"): - fp.flush() - - -def _encode_tile( - im: Image.Image, - fp: IO[bytes], - tile: list[_Tile], - bufsize: int, - fh: int | None, - exc: BaseException | None = None, -) -> None: - for encoder_name, extents, offset, args in tile: - if offset > 0: - fp.seek(offset) - encoder = Image._getencoder(im.mode, encoder_name, args, im.encoderconfig) - try: - encoder.setimage(im.im, extents) - if encoder.pushes_fd: - encoder.setfd(fp) - errcode = encoder.encode_to_pyfd()[1] - else: - if exc: - # compress to Python file-compatible object - while True: - errcode, data = encoder.encode(bufsize)[1:] - fp.write(data) - if errcode: - break - else: - # slight speedup: compress to real file object - assert fh is not None - errcode = encoder.encode_to_file(fh, bufsize) - if errcode < 0: - raise _get_oserror(errcode, encoder=True) from exc - finally: - encoder.cleanup() - - -def _safe_read(fp: IO[bytes], size: int) -> bytes: - """ - Reads large blocks in a safe way. Unlike fp.read(n), this function - doesn't trust the user. If the requested size is larger than - SAFEBLOCK, the file is read block by block. - - :param fp: File handle. Must implement a read method. - :param size: Number of bytes to read. - :returns: A string containing size bytes of data. - - Raises an OSError if the file is truncated and the read cannot be completed - - """ - if size <= 0: - return b"" - if size <= SAFEBLOCK: - data = fp.read(size) - if len(data) < size: - msg = "Truncated File Read" - raise OSError(msg) - return data - blocks: list[bytes] = [] - remaining_size = size - while remaining_size > 0: - block = fp.read(min(remaining_size, SAFEBLOCK)) - if not block: - break - blocks.append(block) - remaining_size -= len(block) - if sum(len(block) for block in blocks) < size: - msg = "Truncated File Read" - raise OSError(msg) - return b"".join(blocks) - - -class PyCodecState: - def __init__(self) -> None: - self.xsize = 0 - self.ysize = 0 - self.xoff = 0 - self.yoff = 0 - - def extents(self) -> tuple[int, int, int, int]: - return self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize - - -class PyCodec: - fd: IO[bytes] | None - - def __init__(self, mode: str, *args: Any) -> None: - self.im: Image.core.ImagingCore | None = None - self.state = PyCodecState() - self.fd = None - self.mode = mode - self.init(args) - - def init(self, args: tuple[Any, ...]) -> None: - """ - Override to perform codec specific initialization - - :param args: Tuple of arg items from the tile entry - :returns: None - """ - self.args = args - - def cleanup(self) -> None: - """ - Override to perform codec specific cleanup - - :returns: None - """ - pass - - def setfd(self, fd: IO[bytes]) -> None: - """ - Called from ImageFile to set the Python file-like object - - :param fd: A Python file-like object - :returns: None - """ - self.fd = fd - - def setimage( - self, - im: Image.core.ImagingCore, - extents: tuple[int, int, int, int] | None = None, - ) -> None: - """ - Called from ImageFile to set the core output image for the codec - - :param im: A core image object - :param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle - for this tile - :returns: None - """ - - # following c code - self.im = im - - if extents: - x0, y0, x1, y1 = extents - - if x0 < 0 or y0 < 0 or x1 > self.im.size[0] or y1 > self.im.size[1]: - msg = "Tile cannot extend outside image" - raise ValueError(msg) - - self.state.xoff = x0 - self.state.yoff = y0 - self.state.xsize = x1 - x0 - self.state.ysize = y1 - y0 - else: - self.state.xsize, self.state.ysize = self.im.size - - if self.state.xsize <= 0 or self.state.ysize <= 0: - msg = "Size must be positive" - raise ValueError(msg) - - -class PyDecoder(PyCodec): - """ - Python implementation of a format decoder. Override this class and - add the decoding logic in the :meth:`decode` method. - - See :ref:`Writing Your Own File Codec in Python` - """ - - _pulls_fd = False - - @property - def pulls_fd(self) -> bool: - return self._pulls_fd - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - """ - Override to perform the decoding process. - - :param buffer: A bytes object with the data to be decoded. - :returns: A tuple of ``(bytes consumed, errcode)``. - If finished with decoding return -1 for the bytes consumed. - Err codes are from :data:`.ImageFile.ERRORS`. - """ - msg = "unavailable in base decoder" - raise NotImplementedError(msg) - - def set_as_raw( - self, data: bytes, rawmode: str | None = None, extra: tuple[Any, ...] = () - ) -> None: - """ - Convenience method to set the internal image from a stream of raw data - - :param data: Bytes to be set - :param rawmode: The rawmode to be used for the decoder. - If not specified, it will default to the mode of the image - :param extra: Extra arguments for the decoder. - :returns: None - """ - - if not rawmode: - rawmode = self.mode - d = Image._getdecoder(self.mode, "raw", rawmode, extra) - assert self.im is not None - d.setimage(self.im, self.state.extents()) - s = d.decode(data) - - if s[0] >= 0: - msg = "not enough image data" - raise ValueError(msg) - if s[1] != 0: - msg = "cannot decode image data" - raise ValueError(msg) - - -class PyEncoder(PyCodec): - """ - Python implementation of a format encoder. Override this class and - add the decoding logic in the :meth:`encode` method. - - See :ref:`Writing Your Own File Codec in Python` - """ - - _pushes_fd = False - - @property - def pushes_fd(self) -> bool: - return self._pushes_fd - - def encode(self, bufsize: int) -> tuple[int, int, bytes]: - """ - Override to perform the encoding process. - - :param bufsize: Buffer size. - :returns: A tuple of ``(bytes encoded, errcode, bytes)``. - If finished with encoding return 1 for the error code. - Err codes are from :data:`.ImageFile.ERRORS`. - """ - msg = "unavailable in base encoder" - raise NotImplementedError(msg) - - def encode_to_pyfd(self) -> tuple[int, int]: - """ - If ``pushes_fd`` is ``True``, then this method will be used, - and ``encode()`` will only be called once. - - :returns: A tuple of ``(bytes consumed, errcode)``. - Err codes are from :data:`.ImageFile.ERRORS`. - """ - if not self.pushes_fd: - return 0, -8 # bad configuration - bytes_consumed, errcode, data = self.encode(0) - if data: - assert self.fd is not None - self.fd.write(data) - return bytes_consumed, errcode - - def encode_to_file(self, fh: int, bufsize: int) -> int: - """ - :param fh: File handle. - :param bufsize: Buffer size. - - :returns: If finished successfully, return 0. - Otherwise, return an error code. Err codes are from - :data:`.ImageFile.ERRORS`. - """ - errcode = 0 - while errcode == 0: - status, errcode, buf = self.encode(bufsize) - if status > 0: - os.write(fh, buf[status:]) - return errcode diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageFilter.py b/.venv/lib/python3.12/site-packages/PIL/ImageFilter.py deleted file mode 100644 index 9326eeed..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageFilter.py +++ /dev/null @@ -1,607 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard filters -# -# History: -# 1995-11-27 fl Created -# 2002-06-08 fl Added rank and mode filters -# 2003-09-15 fl Fixed rank calculation in rank filter; added expand call -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2002 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import abc -import functools -from collections.abc import Sequence -from typing import cast - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from types import ModuleType - from typing import Any - - from . import _imaging - from ._typing import NumpyArray - - -class Filter(abc.ABC): - @abc.abstractmethod - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - pass - - -class MultibandFilter(Filter): - pass - - -class BuiltinFilter(MultibandFilter): - filterargs: tuple[Any, ...] - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - if image.mode == "P": - msg = "cannot filter palette images" - raise ValueError(msg) - return image.filter(*self.filterargs) - - -class Kernel(BuiltinFilter): - """ - Create a convolution kernel. This only supports 3x3 and 5x5 integer and floating - point kernels. - - Kernels can only be applied to "L" and "RGB" images. - - :param size: Kernel size, given as (width, height). This must be (3,3) or (5,5). - :param kernel: A sequence containing kernel weights. The kernel will be flipped - vertically before being applied to the image. - :param scale: Scale factor. If given, the result for each pixel is divided by this - value. The default is the sum of the kernel weights. - :param offset: Offset. If given, this value is added to the result, after it has - been divided by the scale factor. - """ - - name = "Kernel" - - def __init__( - self, - size: tuple[int, int], - kernel: Sequence[float], - scale: float | None = None, - offset: float = 0, - ) -> None: - if scale is None: - # default scale is sum of kernel - scale = functools.reduce(lambda a, b: a + b, kernel) - if size[0] * size[1] != len(kernel): - msg = "not enough coefficients in kernel" - raise ValueError(msg) - self.filterargs = size, scale, offset, kernel - - -class RankFilter(Filter): - """ - Create a rank filter. The rank filter sorts all pixels in - a window of the given size, and returns the ``rank``'th value. - - :param size: The kernel size, in pixels. - :param rank: What pixel value to pick. Use 0 for a min filter, - ``size * size / 2`` for a median filter, ``size * size - 1`` - for a max filter, etc. - """ - - name = "Rank" - - def __init__(self, size: int, rank: int) -> None: - self.size = size - self.rank = rank - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - if image.mode == "P": - msg = "cannot filter palette images" - raise ValueError(msg) - image = image.expand(self.size // 2, self.size // 2) - return image.rankfilter(self.size, self.rank) - - -class MedianFilter(RankFilter): - """ - Create a median filter. Picks the median pixel value in a window with the - given size. - - :param size: The kernel size, in pixels. - """ - - name = "Median" - - def __init__(self, size: int = 3) -> None: - self.size = size - self.rank = size * size // 2 - - -class MinFilter(RankFilter): - """ - Create a min filter. Picks the lowest pixel value in a window with the - given size. - - :param size: The kernel size, in pixels. - """ - - name = "Min" - - def __init__(self, size: int = 3) -> None: - self.size = size - self.rank = 0 - - -class MaxFilter(RankFilter): - """ - Create a max filter. Picks the largest pixel value in a window with the - given size. - - :param size: The kernel size, in pixels. - """ - - name = "Max" - - def __init__(self, size: int = 3) -> None: - self.size = size - self.rank = size * size - 1 - - -class ModeFilter(Filter): - """ - Create a mode filter. Picks the most frequent pixel value in a box with the - given size. Pixel values that occur only once or twice are ignored; if no - pixel value occurs more than twice, the original pixel value is preserved. - - :param size: The kernel size, in pixels. - """ - - name = "Mode" - - def __init__(self, size: int = 3) -> None: - self.size = size - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - return image.modefilter(self.size) - - -class GaussianBlur(MultibandFilter): - """Blurs the image with a sequence of extended box filters, which - approximates a Gaussian kernel. For details on accuracy see - - - :param radius: Standard deviation of the Gaussian kernel. Either a sequence of two - numbers for x and y, or a single number for both. - """ - - name = "GaussianBlur" - - def __init__(self, radius: float | Sequence[float] = 2) -> None: - self.radius = radius - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - xy = self.radius - if isinstance(xy, (int, float)): - xy = (xy, xy) - if xy == (0, 0): - return image.copy() - return image.gaussian_blur(xy) - - -class BoxBlur(MultibandFilter): - """Blurs the image by setting each pixel to the average value of the pixels - in a square box extending radius pixels in each direction. - Supports float radius of arbitrary size. Uses an optimized implementation - which runs in linear time relative to the size of the image - for any radius value. - - :param radius: Size of the box in a direction. Either a sequence of two numbers for - x and y, or a single number for both. - - Radius 0 does not blur, returns an identical image. - Radius 1 takes 1 pixel in each direction, i.e. 9 pixels in total. - """ - - name = "BoxBlur" - - def __init__(self, radius: float | Sequence[float]) -> None: - xy = radius if isinstance(radius, (tuple, list)) else (radius, radius) - if xy[0] < 0 or xy[1] < 0: - msg = "radius must be >= 0" - raise ValueError(msg) - self.radius = radius - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - xy = self.radius - if isinstance(xy, (int, float)): - xy = (xy, xy) - if xy == (0, 0): - return image.copy() - return image.box_blur(xy) - - -class UnsharpMask(MultibandFilter): - """Unsharp mask filter. - - See Wikipedia's entry on `digital unsharp masking`_ for an explanation of - the parameters. - - :param radius: Blur Radius - :param percent: Unsharp strength, in percent - :param threshold: Threshold controls the minimum brightness change that - will be sharpened - - .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking - - """ - - name = "UnsharpMask" - - def __init__( - self, radius: float = 2, percent: int = 150, threshold: int = 3 - ) -> None: - self.radius = radius - self.percent = percent - self.threshold = threshold - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - return image.unsharp_mask(self.radius, self.percent, self.threshold) - - -class BLUR(BuiltinFilter): - name = "Blur" - # fmt: off - filterargs = (5, 5), 16, 0, ( - 1, 1, 1, 1, 1, - 1, 0, 0, 0, 1, - 1, 0, 0, 0, 1, - 1, 0, 0, 0, 1, - 1, 1, 1, 1, 1, - ) - # fmt: on - - -class CONTOUR(BuiltinFilter): - name = "Contour" - # fmt: off - filterargs = (3, 3), 1, 255, ( - -1, -1, -1, - -1, 8, -1, - -1, -1, -1, - ) - # fmt: on - - -class DETAIL(BuiltinFilter): - name = "Detail" - # fmt: off - filterargs = (3, 3), 6, 0, ( - 0, -1, 0, - -1, 10, -1, - 0, -1, 0, - ) - # fmt: on - - -class EDGE_ENHANCE(BuiltinFilter): - name = "Edge-enhance" - # fmt: off - filterargs = (3, 3), 2, 0, ( - -1, -1, -1, - -1, 10, -1, - -1, -1, -1, - ) - # fmt: on - - -class EDGE_ENHANCE_MORE(BuiltinFilter): - name = "Edge-enhance More" - # fmt: off - filterargs = (3, 3), 1, 0, ( - -1, -1, -1, - -1, 9, -1, - -1, -1, -1, - ) - # fmt: on - - -class EMBOSS(BuiltinFilter): - name = "Emboss" - # fmt: off - filterargs = (3, 3), 1, 128, ( - -1, 0, 0, - 0, 1, 0, - 0, 0, 0, - ) - # fmt: on - - -class FIND_EDGES(BuiltinFilter): - name = "Find Edges" - # fmt: off - filterargs = (3, 3), 1, 0, ( - -1, -1, -1, - -1, 8, -1, - -1, -1, -1, - ) - # fmt: on - - -class SHARPEN(BuiltinFilter): - name = "Sharpen" - # fmt: off - filterargs = (3, 3), 16, 0, ( - -2, -2, -2, - -2, 32, -2, - -2, -2, -2, - ) - # fmt: on - - -class SMOOTH(BuiltinFilter): - name = "Smooth" - # fmt: off - filterargs = (3, 3), 13, 0, ( - 1, 1, 1, - 1, 5, 1, - 1, 1, 1, - ) - # fmt: on - - -class SMOOTH_MORE(BuiltinFilter): - name = "Smooth More" - # fmt: off - filterargs = (5, 5), 100, 0, ( - 1, 1, 1, 1, 1, - 1, 5, 5, 5, 1, - 1, 5, 44, 5, 1, - 1, 5, 5, 5, 1, - 1, 1, 1, 1, 1, - ) - # fmt: on - - -class Color3DLUT(MultibandFilter): - """Three-dimensional color lookup table. - - Transforms 3-channel pixels using the values of the channels as coordinates - in the 3D lookup table and interpolating the nearest elements. - - This method allows you to apply almost any color transformation - in constant time by using pre-calculated decimated tables. - - .. versionadded:: 5.2.0 - - :param size: Size of the table. One int or tuple of (int, int, int). - Minimal size in any dimension is 2, maximum is 65. - :param table: Flat lookup table. A list of ``channels * size**3`` - float elements or a list of ``size**3`` channels-sized - tuples with floats. Channels are changed first, - then first dimension, then second, then third. - Value 0.0 corresponds lowest value of output, 1.0 highest. - :param channels: Number of channels in the table. Could be 3 or 4. - Default is 3. - :param target_mode: A mode for the result image. Should have not less - than ``channels`` channels. Default is ``None``, - which means that mode wouldn't be changed. - """ - - name = "Color 3D LUT" - - def __init__( - self, - size: int | tuple[int, int, int], - table: Sequence[float] | Sequence[Sequence[int]] | NumpyArray, - channels: int = 3, - target_mode: str | None = None, - **kwargs: bool, - ) -> None: - if channels not in (3, 4): - msg = "Only 3 or 4 output channels are supported" - raise ValueError(msg) - self.size = size = self._check_size(size) - self.channels = channels - self.mode = target_mode - - # Hidden flag `_copy_table=False` could be used to avoid extra copying - # of the table if the table is specially made for the constructor. - copy_table = kwargs.get("_copy_table", True) - items = size[0] * size[1] * size[2] - wrong_size = False - - numpy: ModuleType | None = None - if hasattr(table, "shape"): - try: - import numpy - except ImportError: - pass - - if numpy and isinstance(table, numpy.ndarray): - numpy_table: NumpyArray = table - if copy_table: - numpy_table = numpy_table.copy() - - if numpy_table.shape in [ - (items * channels,), - (items, channels), - (size[2], size[1], size[0], channels), - ]: - table = numpy_table.reshape(items * channels) - else: - wrong_size = True - - else: - if copy_table: - table = list(table) - - # Convert to a flat list - if table and isinstance(table[0], (list, tuple)): - raw_table = cast(Sequence[Sequence[int]], table) - flat_table: list[int] = [] - for pixel in raw_table: - if len(pixel) != channels: - msg = ( - "The elements of the table should " - f"have a length of {channels}." - ) - raise ValueError(msg) - flat_table.extend(pixel) - table = flat_table - - if wrong_size or len(table) != items * channels: - msg = ( - "The table should have either channels * size**3 float items " - "or size**3 items of channels-sized tuples with floats. " - f"Table should be: {channels}x{size[0]}x{size[1]}x{size[2]}. " - f"Actual length: {len(table)}" - ) - raise ValueError(msg) - self.table = table - - @staticmethod - def _check_size(size: Any) -> tuple[int, int, int]: - try: - _, _, _ = size - except ValueError as e: - msg = "Size should be either an integer or a tuple of three integers." - raise ValueError(msg) from e - except TypeError: - size = (size, size, size) - size = tuple(int(x) for x in size) - for size_1d in size: - if not 2 <= size_1d <= 65: - msg = "Size should be in [2, 65] range." - raise ValueError(msg) - return size - - @classmethod - def generate( - cls, - size: int | tuple[int, int, int], - callback: Callable[[float, float, float], tuple[float, ...]], - channels: int = 3, - target_mode: str | None = None, - ) -> Color3DLUT: - """Generates new LUT using provided callback. - - :param size: Size of the table. Passed to the constructor. - :param callback: Function with three parameters which correspond - three color channels. Will be called ``size**3`` - times with values from 0.0 to 1.0 and should return - a tuple with ``channels`` elements. - :param channels: The number of channels which should return callback. - :param target_mode: Passed to the constructor of the resulting - lookup table. - """ - size_1d, size_2d, size_3d = cls._check_size(size) - if channels not in (3, 4): - msg = "Only 3 or 4 output channels are supported" - raise ValueError(msg) - - table: list[float] = [0] * (size_1d * size_2d * size_3d * channels) - idx_out = 0 - for b in range(size_3d): - for g in range(size_2d): - for r in range(size_1d): - table[idx_out : idx_out + channels] = callback( - r / (size_1d - 1), g / (size_2d - 1), b / (size_3d - 1) - ) - idx_out += channels - - return cls( - (size_1d, size_2d, size_3d), - table, - channels=channels, - target_mode=target_mode, - _copy_table=False, - ) - - def transform( - self, - callback: Callable[..., tuple[float, ...]], - with_normals: bool = False, - channels: int | None = None, - target_mode: str | None = None, - ) -> Color3DLUT: - """Transforms the table values using provided callback and returns - a new LUT with altered values. - - :param callback: A function which takes old lookup table values - and returns a new set of values. The number - of arguments which function should take is - ``self.channels`` or ``3 + self.channels`` - if ``with_normals`` flag is set. - Should return a tuple of ``self.channels`` or - ``channels`` elements if it is set. - :param with_normals: If true, ``callback`` will be called with - coordinates in the color cube as the first - three arguments. Otherwise, ``callback`` - will be called only with actual color values. - :param channels: The number of channels in the resulting lookup table. - :param target_mode: Passed to the constructor of the resulting - lookup table. - """ - if channels not in (None, 3, 4): - msg = "Only 3 or 4 output channels are supported" - raise ValueError(msg) - ch_in = self.channels - ch_out = channels or ch_in - size_1d, size_2d, size_3d = self.size - - table: list[float] = [0] * (size_1d * size_2d * size_3d * ch_out) - idx_in = 0 - idx_out = 0 - for b in range(size_3d): - for g in range(size_2d): - for r in range(size_1d): - values = self.table[idx_in : idx_in + ch_in] - if with_normals: - values = callback( - r / (size_1d - 1), - g / (size_2d - 1), - b / (size_3d - 1), - *values, - ) - else: - values = callback(*values) - table[idx_out : idx_out + ch_out] = values - idx_in += ch_in - idx_out += ch_out - - return type(self)( - self.size, - table, - channels=ch_out, - target_mode=target_mode or self.mode, - _copy_table=False, - ) - - def __repr__(self) -> str: - r = [ - f"{self.__class__.__name__} from {self.table.__class__.__name__}", - "size={:d}x{:d}x{:d}".format(*self.size), - f"channels={self.channels:d}", - ] - if self.mode: - r.append(f"target_mode={self.mode}") - return "<{}>".format(" ".join(r)) - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - from . import Image - - return image.color_lut_3d( - self.mode or image.mode, - Image.Resampling.BILINEAR, - self.channels, - self.size, - self.table, - ) diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageFont.py b/.venv/lib/python3.12/site-packages/PIL/ImageFont.py deleted file mode 100644 index 06ea0359..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageFont.py +++ /dev/null @@ -1,1309 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PIL raster font management -# -# History: -# 1996-08-07 fl created (experimental) -# 1997-08-25 fl minor adjustments to handle fonts from pilfont 0.3 -# 1999-02-06 fl rewrote most font management stuff in C -# 1999-03-17 fl take pth files into account in load_path (from Richard Jones) -# 2001-02-17 fl added freetype support -# 2001-05-09 fl added TransposedFont wrapper class -# 2002-03-04 fl make sure we have a "L" or "1" font -# 2002-12-04 fl skip non-directory entries in the system path -# 2003-04-29 fl add embedded default font -# 2003-09-27 fl added support for truetype charmap encodings -# -# Todo: -# Adapt to PILFONT2 format (16-bit fonts, compressed, single file) -# -# Copyright (c) 1997-2003 by Secret Labs AB -# Copyright (c) 1996-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - -from __future__ import annotations - -import base64 -import os -import sys -import warnings -from enum import IntEnum -from io import BytesIO -from types import ModuleType -from typing import IO, Any, BinaryIO, TypedDict, cast - -from . import Image -from ._typing import StrOrBytesPath -from ._util import DeferredError, is_path - -TYPE_CHECKING = False -if TYPE_CHECKING: - from . import ImageFile - from ._imaging import ImagingFont - from ._imagingft import Font - - -class Axis(TypedDict): - minimum: int | None - default: int | None - maximum: int | None - name: bytes | None - - -class Layout(IntEnum): - BASIC = 0 - RAQM = 1 - - -MAX_STRING_LENGTH = 1_000_000 - - -core: ModuleType | DeferredError -try: - from . import _imagingft as core -except ImportError as ex: - core = DeferredError.new(ex) - - -def _string_length_check(text: str | bytes | bytearray) -> None: - if MAX_STRING_LENGTH is not None and len(text) > MAX_STRING_LENGTH: - msg = "too many characters in string" - raise ValueError(msg) - - -# FIXME: add support for pilfont2 format (see FontFile.py) - -# -------------------------------------------------------------------- -# Font metrics format: -# "PILfont" LF -# fontdescriptor LF -# (optional) key=value... LF -# "DATA" LF -# binary data: 256*10*2 bytes (dx, dy, dstbox, srcbox) -# -# To place a character, cut out srcbox and paste at dstbox, -# relative to the character position. Then move the character -# position according to dx, dy. -# -------------------------------------------------------------------- - - -class ImageFont: - """PIL font wrapper""" - - font: ImagingFont - - def _load_pilfont(self, filename: str) -> None: - with open(filename, "rb") as fp: - image: ImageFile.ImageFile | None = None - root = os.path.splitext(filename)[0] - - for ext in (".png", ".gif", ".pbm"): - if image: - image.close() - try: - fullname = root + ext - image = Image.open(fullname) - except Exception: - pass - else: - if image.mode in ("1", "L"): - break - else: - if image: - image.close() - - msg = f"cannot find glyph data file {root}.{{gif|pbm|png}}" - raise OSError(msg) - - self.file = fullname - - self._load_pilfont_data(fp, image) - image.close() - - def _load_pilfont_data(self, file: IO[bytes], image: Image.Image) -> None: - # check image - if image.mode not in ("1", "L"): - image.close() - - msg = "invalid font image mode" - raise TypeError(msg) - - # read PILfont header - if file.read(8) != b"PILfont\n": - image.close() - - msg = "Not a PILfont file" - raise SyntaxError(msg) - file.readline() - self.info = [] # FIXME: should be a dictionary - while True: - s = file.readline() - if not s or s == b"DATA\n": - break - self.info.append(s) - - # read PILfont metrics - data = file.read(256 * 20) - - self._load(image, data) - - def _load(self, image: Image.Image, data: bytes) -> None: - image.load() - - self.font = Image.core.font(image.im, data) - - def getmask( - self, text: str | bytes, mode: str = "", *args: Any, **kwargs: Any - ) -> Image.core.ImagingCore: - """ - Create a bitmap for the text. - - If the font uses antialiasing, the bitmap should have mode ``L`` and use a - maximum value of 255. Otherwise, it should have mode ``1``. - - :param text: Text to render. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. - - .. versionadded:: 1.1.5 - - :return: An internal PIL storage memory instance as defined by the - :py:mod:`PIL.Image.core` interface module. - """ - _string_length_check(text) - Image._decompression_bomb_check(self.font.getsize(text)) - return self.font.getmask(text, mode) - - def getbbox( - self, text: str | bytes | bytearray, *args: Any, **kwargs: Any - ) -> tuple[int, int, int, int]: - """ - Returns bounding box (in pixels) of given text. - - .. versionadded:: 9.2.0 - - :param text: Text to render. - - :return: ``(left, top, right, bottom)`` bounding box - """ - _string_length_check(text) - width, height = self.font.getsize(text) - return 0, 0, width, height - - def getlength( - self, text: str | bytes | bytearray, *args: Any, **kwargs: Any - ) -> int: - """ - Returns length (in pixels) of given text. - This is the amount by which following text should be offset. - - .. versionadded:: 9.2.0 - """ - _string_length_check(text) - width, height = self.font.getsize(text) - return width - - -## -# Wrapper for FreeType fonts. Application code should use the -# truetype factory function to create font objects. - - -class FreeTypeFont: - """FreeType font wrapper (requires _imagingft service)""" - - font: Font - font_bytes: bytes - - def __init__( - self, - font: StrOrBytesPath | BinaryIO, - size: float = 10, - index: int = 0, - encoding: str = "", - layout_engine: Layout | None = None, - ) -> None: - # FIXME: use service provider instead - - if isinstance(core, DeferredError): - raise core.ex - - if size <= 0: - msg = f"font size must be greater than 0, not {size}" - raise ValueError(msg) - - self.path = font - self.size = size - self.index = index - self.encoding = encoding - - if layout_engine not in (Layout.BASIC, Layout.RAQM): - layout_engine = Layout.BASIC - if core.HAVE_RAQM: - layout_engine = Layout.RAQM - elif layout_engine == Layout.RAQM and not core.HAVE_RAQM: - warnings.warn( - "Raqm layout was requested, but Raqm is not available. " - "Falling back to basic layout." - ) - layout_engine = Layout.BASIC - - self.layout_engine = layout_engine - - def load_from_bytes(f: IO[bytes]) -> None: - self.font_bytes = f.read() - self.font = core.getfont( - "", size, index, encoding, self.font_bytes, layout_engine - ) - - if is_path(font): - font = os.fspath(font) - if sys.platform == "win32": - font_bytes_path = font if isinstance(font, bytes) else font.encode() - try: - font_bytes_path.decode("ascii") - except UnicodeDecodeError: - # FreeType cannot load fonts with non-ASCII characters on Windows - # So load it into memory first - with open(font, "rb") as f: - load_from_bytes(f) - return - self.font = core.getfont( - font, size, index, encoding, layout_engine=layout_engine - ) - else: - load_from_bytes(cast(IO[bytes], font)) - - def __getstate__(self) -> list[Any]: - return [self.path, self.size, self.index, self.encoding, self.layout_engine] - - def __setstate__(self, state: list[Any]) -> None: - path, size, index, encoding, layout_engine = state - FreeTypeFont.__init__(self, path, size, index, encoding, layout_engine) - - def getname(self) -> tuple[str | None, str | None]: - """ - :return: A tuple of the font family (e.g. Helvetica) and the font style - (e.g. Bold) - """ - return self.font.family, self.font.style - - def getmetrics(self) -> tuple[int, int]: - """ - :return: A tuple of the font ascent (the distance from the baseline to - the highest outline point) and descent (the distance from the - baseline to the lowest outline point, a negative value) - """ - return self.font.ascent, self.font.descent - - def getlength( - self, - text: str | bytes, - mode: str = "", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - ) -> float: - """ - Returns length (in pixels with 1/64 precision) of given text when rendered - in font with provided direction, features, and language. - - This is the amount by which following text should be offset. - Text bounding box may extend past the length in some fonts, - e.g. when using italics or accents. - - The result is returned as a float; it is a whole number if using basic layout. - - Note that the sum of two lengths may not equal the length of a concatenated - string due to kerning. If you need to adjust for kerning, include the following - character and subtract its length. - - For example, instead of :: - - hello = font.getlength("Hello") - world = font.getlength("World") - hello_world = hello + world # not adjusted for kerning - assert hello_world == font.getlength("HelloWorld") # may fail - - use :: - - hello = font.getlength("HelloW") - font.getlength("W") # adjusted for kerning - world = font.getlength("World") - hello_world = hello + world # adjusted for kerning - assert hello_world == font.getlength("HelloWorld") # True - - or disable kerning with (requires libraqm) :: - - hello = draw.textlength("Hello", font, features=["-kern"]) - world = draw.textlength("World", font, features=["-kern"]) - hello_world = hello + world # kerning is disabled, no need to adjust - assert hello_world == draw.textlength("HelloWorld", font, features=["-kern"]) - - .. versionadded:: 8.0.0 - - :param text: Text to measure. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. - - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). - Requires libraqm. - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' - to disable kerning. To get all supported - features, see - https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist - Requires libraqm. - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - `_ - Requires libraqm. - - :return: Either width for horizontal text, or height for vertical text. - """ - _string_length_check(text) - return self.font.getlength(text, mode, direction, features, language) / 64 - - def getbbox( - self, - text: str | bytes, - mode: str = "", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - anchor: str | None = None, - ) -> tuple[float, float, float, float]: - """ - Returns bounding box (in pixels) of given text relative to given anchor - when rendered in font with provided direction, features, and language. - - Use :py:meth:`getlength()` to get the offset of following text with - 1/64 pixel precision. The bounding box includes extra margins for - some fonts, e.g. italics or accents. - - .. versionadded:: 8.0.0 - - :param text: Text to render. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. - - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). - Requires libraqm. - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' - to disable kerning. To get all supported - features, see - https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist - Requires libraqm. - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - `_ - Requires libraqm. - - :param stroke_width: The width of the text stroke. - - :param anchor: The text anchor alignment. Determines the relative location of - the anchor to the text. The default alignment is top left, - specifically ``la`` for horizontal text and ``lt`` for - vertical text. See :ref:`text-anchors` for details. - - :return: ``(left, top, right, bottom)`` bounding box - """ - _string_length_check(text) - size, offset = self.font.getsize( - text, mode, direction, features, language, anchor - ) - left, top = offset[0] - stroke_width, offset[1] - stroke_width - width, height = size[0] + 2 * stroke_width, size[1] + 2 * stroke_width - return left, top, left + width, top + height - - def getmask( - self, - text: str | bytes, - mode: str = "", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - anchor: str | None = None, - ink: int = 0, - start: tuple[float, float] | None = None, - ) -> Image.core.ImagingCore: - """ - Create a bitmap for the text. - - If the font uses antialiasing, the bitmap should have mode ``L`` and use a - maximum value of 255. If the font has embedded color data, the bitmap - should have mode ``RGBA``. Otherwise, it should have mode ``1``. - - :param text: Text to render. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. - - .. versionadded:: 1.1.5 - - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' - to disable kerning. To get all supported - features, see - https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - `_ - Requires libraqm. - - .. versionadded:: 6.0.0 - - :param stroke_width: The width of the text stroke. - - .. versionadded:: 6.2.0 - - :param anchor: The text anchor alignment. Determines the relative location of - the anchor to the text. The default alignment is top left, - specifically ``la`` for horizontal text and ``lt`` for - vertical text. See :ref:`text-anchors` for details. - - .. versionadded:: 8.0.0 - - :param ink: Foreground ink for rendering in RGBA mode. - - .. versionadded:: 8.0.0 - - :param start: Tuple of horizontal and vertical offset, as text may render - differently when starting at fractional coordinates. - - .. versionadded:: 9.4.0 - - :return: An internal PIL storage memory instance as defined by the - :py:mod:`PIL.Image.core` interface module. - """ - return self.getmask2( - text, - mode, - direction=direction, - features=features, - language=language, - stroke_width=stroke_width, - anchor=anchor, - ink=ink, - start=start, - )[0] - - def getmask2( - self, - text: str | bytes, - mode: str = "", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - anchor: str | None = None, - ink: int = 0, - start: tuple[float, float] | None = None, - *args: Any, - **kwargs: Any, - ) -> tuple[Image.core.ImagingCore, tuple[int, int]]: - """ - Create a bitmap for the text. - - If the font uses antialiasing, the bitmap should have mode ``L`` and use a - maximum value of 255. If the font has embedded color data, the bitmap - should have mode ``RGBA``. Otherwise, it should have mode ``1``. - - :param text: Text to render. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. - - .. versionadded:: 1.1.5 - - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' - to disable kerning. To get all supported - features, see - https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - `_ - Requires libraqm. - - .. versionadded:: 6.0.0 - - :param stroke_width: The width of the text stroke. - - .. versionadded:: 6.2.0 - - :param anchor: The text anchor alignment. Determines the relative location of - the anchor to the text. The default alignment is top left, - specifically ``la`` for horizontal text and ``lt`` for - vertical text. See :ref:`text-anchors` for details. - - .. versionadded:: 8.0.0 - - :param ink: Foreground ink for rendering in RGBA mode. - - .. versionadded:: 8.0.0 - - :param start: Tuple of horizontal and vertical offset, as text may render - differently when starting at fractional coordinates. - - .. versionadded:: 9.4.0 - - :return: A tuple of an internal PIL storage memory instance as defined by the - :py:mod:`PIL.Image.core` interface module, and the text offset, the - gap between the starting coordinate and the first marking - """ - _string_length_check(text) - if start is None: - start = (0, 0) - - def fill(width: int, height: int) -> Image.core.ImagingCore: - size = (width, height) - Image._decompression_bomb_check(size) - return Image.core.fill("RGBA" if mode == "RGBA" else "L", size) - - return self.font.render( - text, - fill, - mode, - direction, - features, - language, - stroke_width, - kwargs.get("stroke_filled", False), - anchor, - ink, - start, - ) - - def font_variant( - self, - font: StrOrBytesPath | BinaryIO | None = None, - size: float | None = None, - index: int | None = None, - encoding: str | None = None, - layout_engine: Layout | None = None, - ) -> FreeTypeFont: - """ - Create a copy of this FreeTypeFont object, - using any specified arguments to override the settings. - - Parameters are identical to the parameters used to initialize this - object. - - :return: A FreeTypeFont object. - """ - if font is None: - try: - font = BytesIO(self.font_bytes) - except AttributeError: - font = self.path - return FreeTypeFont( - font=font, - size=self.size if size is None else size, - index=self.index if index is None else index, - encoding=self.encoding if encoding is None else encoding, - layout_engine=layout_engine or self.layout_engine, - ) - - def get_variation_names(self) -> list[bytes]: - """ - :returns: A list of the named styles in a variation font. - :exception OSError: If the font is not a variation font. - """ - names = [] - for name in self.font.getvarnames(): - name = name.replace(b"\x00", b"") - if name not in names: - names.append(name) - return names - - def set_variation_by_name(self, name: str | bytes) -> None: - """ - :param name: The name of the style. - :exception OSError: If the font is not a variation font. - """ - names = self.get_variation_names() - if not isinstance(name, bytes): - name = name.encode() - index = names.index(name) + 1 - - if index == getattr(self, "_last_variation_index", None): - # When the same name is set twice in a row, - # there is an 'unknown freetype error' - # https://savannah.nongnu.org/bugs/?56186 - return - self._last_variation_index = index - - self.font.setvarname(index) - - def get_variation_axes(self) -> list[Axis]: - """ - :returns: A list of the axes in a variation font. - :exception OSError: If the font is not a variation font. - """ - axes = self.font.getvaraxes() - for axis in axes: - if axis["name"]: - axis["name"] = axis["name"].replace(b"\x00", b"") - return axes - - def set_variation_by_axes(self, axes: list[float]) -> None: - """ - :param axes: A list of values for each axis. - :exception OSError: If the font is not a variation font. - """ - self.font.setvaraxes(axes) - - -class TransposedFont: - """Wrapper for writing rotated or mirrored text""" - - def __init__( - self, font: ImageFont | FreeTypeFont, orientation: Image.Transpose | None = None - ): - """ - Wrapper that creates a transposed font from any existing font - object. - - :param font: A font object. - :param orientation: An optional orientation. If given, this should - be one of Image.Transpose.FLIP_LEFT_RIGHT, Image.Transpose.FLIP_TOP_BOTTOM, - Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_180, or - Image.Transpose.ROTATE_270. - """ - self.font = font - self.orientation = orientation # any 'transpose' argument, or None - - def getmask( - self, text: str | bytes, mode: str = "", *args: Any, **kwargs: Any - ) -> Image.core.ImagingCore: - im = self.font.getmask(text, mode, *args, **kwargs) - if self.orientation is not None: - return im.transpose(self.orientation) - return im - - def getbbox( - self, text: str | bytes, *args: Any, **kwargs: Any - ) -> tuple[int, int, float, float]: - # TransposedFont doesn't support getmask2, move top-left point to (0, 0) - # this has no effect on ImageFont and simulates anchor="lt" for FreeTypeFont - left, top, right, bottom = self.font.getbbox(text, *args, **kwargs) - width = right - left - height = bottom - top - if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): - return 0, 0, height, width - return 0, 0, width, height - - def getlength(self, text: str | bytes, *args: Any, **kwargs: Any) -> float: - if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): - msg = "text length is undefined for text rotated by 90 or 270 degrees" - raise ValueError(msg) - return self.font.getlength(text, *args, **kwargs) - - -def load(filename: str) -> ImageFont: - """ - Load a font file. This function loads a font object from the given - bitmap font file, and returns the corresponding font object. For loading TrueType - or OpenType fonts instead, see :py:func:`~PIL.ImageFont.truetype`. - - :param filename: Name of font file. - :return: A font object. - :exception OSError: If the file could not be read. - """ - f = ImageFont() - f._load_pilfont(filename) - return f - - -def truetype( - font: StrOrBytesPath | BinaryIO, - size: float = 10, - index: int = 0, - encoding: str = "", - layout_engine: Layout | None = None, -) -> FreeTypeFont: - """ - Load a TrueType or OpenType font from a file or file-like object, - and create a font object. This function loads a font object from the given - file or file-like object, and creates a font object for a font of the given - size. For loading bitmap fonts instead, see :py:func:`~PIL.ImageFont.load` - and :py:func:`~PIL.ImageFont.load_path`. - - Pillow uses FreeType to open font files. On Windows, be aware that FreeType - will keep the file open as long as the FreeTypeFont object exists. Windows - limits the number of files that can be open in C at once to 512, so if many - fonts are opened simultaneously and that limit is approached, an - ``OSError`` may be thrown, reporting that FreeType "cannot open resource". - A workaround would be to copy the file(s) into memory, and open that instead. - - This function requires the _imagingft service. - - :param font: A filename or file-like object containing a TrueType font. - If the file is not found in this filename, the loader may also - search in other directories, such as: - - * The :file:`fonts/` directory on Windows, - * :file:`/Library/Fonts/`, :file:`/System/Library/Fonts/` - and :file:`~/Library/Fonts/` on macOS. - * :file:`~/.local/share/fonts`, :file:`/usr/local/share/fonts`, - and :file:`/usr/share/fonts` on Linux; or those specified by - the ``XDG_DATA_HOME`` and ``XDG_DATA_DIRS`` environment variables - for user-installed and system-wide fonts, respectively. - - :param size: The requested size, in pixels. - :param index: Which font face to load (default is first available face). - :param encoding: Which font encoding to use (default is Unicode). Possible - encodings include (see the FreeType documentation for more - information): - - * "unic" (Unicode) - * "symb" (Microsoft Symbol) - * "ADOB" (Adobe Standard) - * "ADBE" (Adobe Expert) - * "ADBC" (Adobe Custom) - * "armn" (Apple Roman) - * "sjis" (Shift JIS) - * "gb " (PRC) - * "big5" - * "wans" (Extended Wansung) - * "joha" (Johab) - * "lat1" (Latin-1) - - This specifies the character set to use. It does not alter the - encoding of any text provided in subsequent operations. - :param layout_engine: Which layout engine to use, if available: - :attr:`.ImageFont.Layout.BASIC` or :attr:`.ImageFont.Layout.RAQM`. - If it is available, Raqm layout will be used by default. - Otherwise, basic layout will be used. - - Raqm layout is recommended for all non-English text. If Raqm layout - is not required, basic layout will have better performance. - - You can check support for Raqm layout using - :py:func:`PIL.features.check_feature` with ``feature="raqm"``. - - .. versionadded:: 4.2.0 - :return: A font object. - :exception OSError: If the file could not be read. - :exception ValueError: If the font size is not greater than zero. - """ - - def freetype(font: StrOrBytesPath | BinaryIO) -> FreeTypeFont: - return FreeTypeFont(font, size, index, encoding, layout_engine) - - try: - return freetype(font) - except OSError: - if not is_path(font): - raise - ttf_filename = os.path.basename(font) - - dirs = [] - if sys.platform == "win32": - # check the windows font repository - # NOTE: must use uppercase WINDIR, to work around bugs in - # 1.5.2's os.environ.get() - windir = os.environ.get("WINDIR") - if windir: - dirs.append(os.path.join(windir, "fonts")) - elif sys.platform in ("linux", "linux2"): - data_home = os.environ.get("XDG_DATA_HOME") - if not data_home: - # The freedesktop spec defines the following default directory for - # when XDG_DATA_HOME is unset or empty. This user-level directory - # takes precedence over system-level directories. - data_home = os.path.expanduser("~/.local/share") - xdg_dirs = [data_home] - - data_dirs = os.environ.get("XDG_DATA_DIRS") - if not data_dirs: - # Similarly, defaults are defined for the system-level directories - data_dirs = "/usr/local/share:/usr/share" - xdg_dirs += data_dirs.split(":") - - dirs += [os.path.join(xdg_dir, "fonts") for xdg_dir in xdg_dirs] - elif sys.platform == "darwin": - dirs += [ - "/Library/Fonts", - "/System/Library/Fonts", - os.path.expanduser("~/Library/Fonts"), - ] - - ext = os.path.splitext(ttf_filename)[1] - first_font_with_a_different_extension = None - for directory in dirs: - for walkroot, walkdir, walkfilenames in os.walk(directory): - for walkfilename in walkfilenames: - if ext and walkfilename == ttf_filename: - return freetype(os.path.join(walkroot, walkfilename)) - elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename: - fontpath = os.path.join(walkroot, walkfilename) - if os.path.splitext(fontpath)[1] == ".ttf": - return freetype(fontpath) - if not ext and first_font_with_a_different_extension is None: - first_font_with_a_different_extension = fontpath - if first_font_with_a_different_extension: - return freetype(first_font_with_a_different_extension) - raise - - -def load_path(filename: str | bytes) -> ImageFont: - """ - Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a - bitmap font along the Python path. - - :param filename: Name of font file. - :return: A font object. - :exception OSError: If the file could not be read. - """ - if not isinstance(filename, str): - filename = filename.decode("utf-8") - for directory in sys.path: - try: - return load(os.path.join(directory, filename)) - except OSError: # noqa: PERF203 - pass - msg = f'cannot find font file "{filename}" in sys.path' - if os.path.exists(filename): - msg += f', did you mean ImageFont.load("{filename}") instead?' - - raise OSError(msg) - - -def load_default_imagefont() -> ImageFont: - f = ImageFont() - f._load_pilfont_data( - # courB08 - BytesIO(base64.b64decode(b""" -UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA -BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL -AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA -AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB -ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A -BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB -//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA -AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH -AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA -ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv -AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/ -/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5 -AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA -AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG -AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA -BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA -AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA -2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF -AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA//// -+gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA -////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA -BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv -AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA -AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA -AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA -BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP// -//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA -AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF -AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB -mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn -AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA -AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7 -AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA -Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB -//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA -AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ -AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC -DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ -AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/ -+wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5 -AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/ -///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG -AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA -BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA -Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC -eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG -AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA//// -+gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA -////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA -BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT -AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A -AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA -Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA -Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP// -//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA -AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ -AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA -LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5 -AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA -AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5 -AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA -AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG -AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA -EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK -AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA -pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG -AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// -+QAGAAIAzgAKANUAEw== -""")), - Image.open(BytesIO(base64.b64decode(b""" -iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u -Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 -M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g -LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F -IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA -Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791 -NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx -in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9 -SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY -AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt -y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG -ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY -lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H -/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3 -AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47 -c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/ -/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw -pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv -oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR -evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA -AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v// -Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR -w7IkEbzhVQAAAABJRU5ErkJggg== -"""))), - ) - return f - - -def load_default(size: float | None = None) -> FreeTypeFont | ImageFont: - """If FreeType support is available, load a version of Aileron Regular, - https://dotcolon.net/fonts/aileron, with a more limited character set. - - Otherwise, load a "better than nothing" font. - - .. versionadded:: 1.1.4 - - :param size: The font size of Aileron Regular. - - .. versionadded:: 10.1.0 - - :return: A font object. - """ - if isinstance(core, ModuleType) or size is not None: - return truetype( - BytesIO(base64.b64decode(b""" -AAEAAAAPAIAAAwBwRkZUTYwDlUAAADFoAAAAHEdERUYAqADnAAAo8AAAACRHUE9ThhmITwAAKfgAA -AduR1NVQnHxefoAACkUAAAA4k9TLzJovoHLAAABeAAAAGBjbWFw5lFQMQAAA6gAAAGqZ2FzcP//AA -MAACjoAAAACGdseWYmRXoPAAAGQAAAHfhoZWFkE18ayQAAAPwAAAA2aGhlYQboArEAAAE0AAAAJGh -tdHjjERZ8AAAB2AAAAdBsb2NhuOexrgAABVQAAADqbWF4cAC7AEYAAAFYAAAAIG5hbWUr+h5lAAAk -OAAAA6Jwb3N0D3oPTQAAJ9wAAAEKAAEAAAABGhxJDqIhXw889QALA+gAAAAA0Bqf2QAAAADhCh2h/ -2r/LgOxAyAAAAAIAAIAAAAAAAAAAQAAA8r/GgAAA7j/av9qA7EAAQAAAAAAAAAAAAAAAAAAAHQAAQ -AAAHQAQwAFAAAAAAACAAAAAQABAAAAQAAAAAAAAAADAfoBkAAFAAgCigJYAAAASwKKAlgAAAFeADI -BPgAAAAAFAAAAAAAAAAAAAAcAAAAAAAAAAAAAAABVS1dOAEAAIPsCAwL/GgDIA8oA5iAAAJMAAAAA -AhICsgAAACAAAwH0AAAAAAAAAU0AAADYAAAA8gA5AVMAVgJEAEYCRAA1AuQAKQKOAEAAsAArATsAZ -AE7AB4CMABVAkQAUADc/+EBEgAgANwAJQEv//sCRAApAkQAggJEADwCRAAtAkQAIQJEADkCRAArAk -QAMgJEACwCRAAxANwAJQDc/+ECRABnAkQAUAJEAEQB8wAjA1QANgJ/AB0CcwBkArsALwLFAGQCSwB -kAjcAZALGAC8C2gBkAQgAZAIgADcCYQBkAj8AZANiAGQCzgBkAuEALwJWAGQC3QAvAmsAZAJJADQC -ZAAiAqoAXgJuACADuAAaAnEAGQJFABMCTwAuATMAYgEv//sBJwAiAkQAUAH0ADIBLAApAhMAJAJjA -EoCEQAeAmcAHgIlAB4BIgAVAmcAHgJRAEoA7gA+AOn/8wIKAEoA9wBGA1cASgJRAEoCSgAeAmMASg -JnAB4BSgBKAcsAGAE5ABQCUABCAgIAAQMRAAEB4v/6AgEAAQHOABQBLwBAAPoAYAEvACECRABNA0Y -AJAItAHgBKgAcAkQAUAEsAHQAygAgAi0AOQD3ADYA9wAWAaEANgGhABYCbAAlAYMAeAGDADkA6/9q -AhsAFAIKABUB/QAVAAAAAwAAAAMAAAAcAAEAAAAAAKQAAwABAAAAHAAEAIgAAAAeABAAAwAOAH4Aq -QCrALEAtAC3ALsgGSAdICYgOiBEISL7Av//AAAAIACpAKsAsAC0ALcAuyAYIBwgJiA5IEQhIvsB// -//4/+5/7j/tP+y/7D/reBR4E/gR+A14CzfTwVxAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAEGAAABAAAAAAAAAAECAAAAAgAAAAAAAAAAAAAAAAAAAAEAAAMEBQYHCAkKCwwNDg8QERIT -FBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMT -U5PUFFSU1RVVldYWVpbXF1eX2BhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAA -AAAAAAYnFmAAAAAABlAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2htAAAAAAAAAABrbGlqAAAAAHAAbm9 -ycwBnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmACYAJgAmAD4AUgCCAMoBCgFO -AVwBcgGIAaYBvAHKAdYB6AH2AgwCIAJKAogCpgLWAw4DIgNkA5wDugPUA+gD/AQQBEYEogS8BPoFJ -gVSBWoFgAWwBcoF1gX6BhQGJAZMBmgGiga0BuIHGgdUB2YHkAeiB8AH3AfyCAoIHAgqCDoITghcCG -oIogjSCPoJKglYCXwJwgnqCgIKKApACl4Klgq8CtwLDAs8C1YLjAuyC9oL7gwMDCYMSAxgDKAMrAz -qDQoNTA1mDYQNoA2uDcAN2g3oDfYODA4iDkoOXA5sDnoOnA7EDvwAAAAFAAAAAAH0ArwAAwAGAAkA -DAAPAAAxESERAxMhExcRASELARETAfT6qv6syKr+jgFUqsiqArz9RAGLAP/+1P8B/v3VAP8BLP4CA -P8AAgA5//IAuQKyAAMACwAANyMDMwIyFhQGIiY0oE4MZk84JCQ4JLQB/v3AJDgkJDgAAgBWAeUBPA -LfAAMABwAAEyMnMxcjJzOmRgpagkYKWgHl+vr6AAAAAAIARgAAAf4CsgAbAB8AAAEHMxUjByM3Iwc -jNyM1MzcjNTM3MwczNzMHMxUrAQczAZgdZXEvOi9bLzovWmYdZXEvOi9bLzovWp9bHlsBn4w429vb -2ziMONvb29s4jAAAAAMANf+mAg4DDAAfACYALAAAJRQGBxUjNS4BJzMeARcRLgE0Njc1MxUeARcjJ -icVHgEBFBYXNQ4BExU+ATU0Ag5xWDpgcgRcBz41Xl9oVTpVYwpcC1ttXP6cLTQuM5szOrVRZwlOTQ -ZqVzZECAEAGlukZAlOTQdrUG8O7iNlAQgxNhDlCDj+8/YGOjReAAAAAAUAKf/yArsCvAAHAAsAFQA -dACcAABIyFhQGIiY0EyMBMwQiBhUUFjI2NTQSMhYUBiImNDYiBhUUFjI2NTR5iFBQiFCVVwHAV/5c -OiMjOiPmiFBQiFCxOiMjOiMCvFaSVlaS/ZoCsjIzMC80NC8w/uNWklZWkhozMC80NC8wAAAAAgBA/ -/ICbgLAACIALgAAARUjEQYjIiY1NDY3LgE1NDYzMhcVJiMiBhUUFhcWOwE1MxUFFBYzMjc1IyIHDg -ECbmBcYYOOVkg7R4hsQjY4Q0RNRD4SLDxW/pJUXzksPCkUUk0BgUb+zBVUZ0BkDw5RO1huCkULQzp -COAMBcHDHRz0J/AIHRQAAAAEAKwHlAIUC3wADAAATIycze0YKWgHl+gAAAAABAGT/sAEXAwwACQAA -EzMGEBcjLgE0Nt06dXU6OUBAAwzG/jDGVePs4wAAAAEAHv+wANEDDAAJAAATMx4BFAYHIzYQHjo5Q -EA5OnUDDFXj7ONVxgHQAAAAAQBVAFIB2wHbAA4AAAE3FwcXBycHJzcnNxcnMwEtmxOfcTJjYzJxnx -ObCj4BKD07KYolmZkliik7PbMAAQBQAFUB9AIlAAsAAAEjFSM1IzUzNTMVMwH0tTq1tTq1AR/Kyjj -OzgAAAAAB/+H/iACMAGQABAAANwcjNzOMWlFOXVrS3AAAAQAgAP8A8gE3AAMAABMjNTPy0tIA/zgA -AQAl//IApQByAAcAADYyFhQGIiY0STgkJDgkciQ4JCQ4AAAAAf/7/+IBNALQAAMAABcjEzM5Pvs+H -gLuAAAAAAIAKf/yAhsCwAADAAcAABIgECA2IBAgKQHy/g5gATL+zgLA/TJEAkYAAAAAAQCCAAABlg -KyAAgAAAERIxEHNTc2MwGWVr6SIygCsv1OAldxW1sWAAEAPAAAAg4CwAAZAAA3IRUhNRM+ATU0JiM -iDwEjNz4BMzIWFRQGB7kBUv4x+kI2QTt+EAFWAQp8aGVtSl5GRjEA/0RVLzlLmAoKa3FsUkNxXQAA -AAEALf/yAhYCwAAqAAABHgEVFAYjIi8BMxceATMyNjU0KwE1MzI2NTQmIyIGDwEjNz4BMzIWFRQGA -YxBSZJo2RUBVgEHV0JBUaQREUBUQzc5TQcBVgEKfGhfcEMBbxJbQl1x0AoKRkZHPn9GSD80QUVCCg -pfbGBPOlgAAAACACEAAAIkArIACgAPAAAlIxUjNSE1ATMRMyMRBg8BAiRXVv6qAVZWV60dHLCurq4 -rAdn+QgFLMibzAAABADn/8gIZArIAHQAAATIWFRQGIyIvATMXFjMyNjU0JiMiByMTIRUhBzc2ATNv -d5Fl1RQBVgIad0VSTkVhL1IwAYj+vh8rMAHHgGdtgcUKCoFXTU5bYgGRRvAuHQAAAAACACv/8gITA -sAAFwAjAAABMhYVFAYjIhE0NjMyFh8BIycmIyIDNzYTMjY1NCYjIgYVFBYBLmp7imr0l3RZdAgBXA -IYZ5wKJzU6QVNJSz5SUAHSgWltiQFGxcNlVQoKdv7sPiz+ZF1LTmJbU0lhAAAAAQAyAAACGgKyAAY -AAAEVASMBITUCGv6oXAFL/oECsij9dgJsRgAAAAMALP/xAhgCwAAWACAALAAAAR4BFRQGIyImNTQ2 -Ny4BNTQ2MhYVFAYmIgYVFBYyNjU0AzI2NTQmIyIGFRQWAZQ5S5BmbIpPOjA7ecp5P2F8Q0J8RIVJS -0pLTEtOAW0TXTxpZ2ZqPF0SE1A3VWVlVTdQ/UU0N0RENzT9/ko+Ok1NOj1LAAIAMf/yAhkCwAAXAC -MAAAEyERQGIyImLwEzFxYzMhMHBiMiJjU0NhMyNjU0JiMiBhUUFgEl9Jd0WXQIAVwCGGecCic1SWp -7imo+UlBAQVNJAsD+usXDZVUKCnYBFD4sgWltif5kW1NJYV1LTmIAAAACACX/8gClAiAABwAPAAAS -MhYUBiImNBIyFhQGIiY0STgkJDgkJDgkJDgkAiAkOCQkOP52JDgkJDgAAAAC/+H/iAClAiAABwAMA -AASMhYUBiImNBMHIzczSTgkJDgkaFpSTl4CICQ4JCQ4/mba5gAAAQBnAB4B+AH0AAYAAAENARUlNS -UB+P6qAVb+bwGRAbCmpkbJRMkAAAIAUAC7AfQBuwADAAcAAAEhNSERITUhAfT+XAGk/lwBpAGDOP8 -AOAABAEQAHgHVAfQABgAAARUFNS0BNQHV/m8BVv6qAStEyUSmpkYAAAAAAgAj//IB1ALAABgAIAAA -ATIWFRQHDgEHIz4BNz4BNTQmIyIGByM+ARIyFhQGIiY0AQRibmktIAJWBSEqNig+NTlHBFoDezQ4J -CQ4JALAZ1BjaS03JS1DMD5LLDQ/SUVgcv2yJDgkJDgAAAAAAgA2/5gDFgKYADYAQgAAAQMGFRQzMj -Y1NCYjIg4CFRQWMzI2NxcGIyImNTQ+AjMyFhUUBiMiJwcGIyImNTQ2MzIfATcHNzYmIyIGFRQzMjY -Cej8EJjJJlnBAfGQ+oHtAhjUYg5OPx0h2k06Os3xRWQsVLjY5VHtdPBwJETcJDyUoOkZEJz8B0f74 -EQ8kZl6EkTFZjVOLlyknMVm1pmCiaTq4lX6CSCknTVRmmR8wPdYnQzxuSWVGAAIAHQAAAncCsgAHA -AoAACUjByMTMxMjATMDAcj+UVz4dO5d/sjPZPT0ArL9TgE6ATQAAAADAGQAAAJMArIAEAAbACcAAA -EeARUUBgcGKwERMzIXFhUUJRUzMjc2NTQnJiMTPgE1NCcmKwEVMzIBvkdHZkwiNt7LOSGq/oeFHBt -hahIlSTM+cB8Yj5UWAW8QT0VYYgwFArIEF5Fv1eMED2NfDAL93AU+N24PBP0AAAAAAQAv//ICjwLA -ABsAAAEyFh8BIycmIyIGFRQWMzI/ATMHDgEjIiY1NDYBdX+PCwFWAiKiaHx5ZaIiAlYBCpWBk6a0A -sCAagoKpqN/gaOmCgplhcicn8sAAAIAZAAAAp8CsgAMABkAAAEeARUUBgcGKwERMzITPgE1NCYnJi -sBETMyAY59lJp8IzXN0jUVWmdjWRs5d3I4Aq4QqJWUug8EArL9mQ+PeHGHDgX92gAAAAABAGQAAAI -vArIACwAAJRUhESEVIRUhFSEVAi/+NQHB/pUBTf6zRkYCskbwRvAAAAABAGQAAAIlArIACQAAExUh -FSERIxEhFboBQ/69VgHBAmzwRv7KArJGAAAAAAEAL//yAo8CwAAfAAABMxEjNQcGIyImNTQ2MzIWH -wEjJyYjIgYVFBYzMjY1IwGP90wfPnWTprSSf48LAVYCIqJofHllVG+hAU3+s3hARsicn8uAagoKpq -N/gaN1XAAAAAEAZAAAAowCsgALAAABESMRIREjETMRIRECjFb+hFZWAXwCsv1OAS7+0gKy/sQBPAA -AAAABAGQAAAC6ArIAAwAAMyMRM7pWVgKyAAABADf/8gHoArIAEwAAAREUBw4BIyImLwEzFxYzMjc2 -NREB6AIFcGpgbQIBVgIHfXQKAQKy/lYxIltob2EpKYyEFD0BpwAAAAABAGQAAAJ0ArIACwAACQEjA -wcVIxEzEQEzATsBJ3ntQlZWAVVlAWH+nwEnR+ACsv6RAW8AAQBkAAACLwKyAAUAACUVIREzEQIv/j -VWRkYCsv2UAAABAGQAAAMUArIAFAAAAREjETQ3BgcDIwMmJxYVESMRMxsBAxRWAiMxemx8NxsCVo7 -MywKy/U4BY7ZLco7+nAFmoFxLtP6dArL9lwJpAAAAAAEAZAAAAoACsgANAAAhIwEWFREjETMBJjUR -MwKAhP67A1aEAUUDVAJeeov+pwKy/aJ5jAFZAAAAAgAv//ICuwLAAAkAEwAAEiAWFRQGICY1NBIyN -jU0JiIGFRTbATSsrP7MrNrYenrYegLAxaKhxsahov47nIeIm5uIhwACAGQAAAJHArIADgAYAAABHg -EVFAYHBisBESMRMzITNjQnJisBETMyAZRUX2VOHzuAVtY7GlxcGDWIiDUCrgtnVlVpCgT+5gKy/rU -V1BUF/vgAAAACAC//zAK9AsAAEgAcAAAlFhcHJiMiBwYjIiY1NDYgFhUUJRQWMjY1NCYiBgI9PUMx -UDcfKh8omqysATSs/dR62Hp62HpICTg7NgkHxqGixcWitbWHnJyHiJubAAIAZAAAAlgCsgAXACMAA -CUWFyMmJyYnJisBESMRMzIXHgEVFAYHFiUzMjc+ATU0JyYrAQIqDCJfGQwNWhAhglbiOx9QXEY1Tv -6bhDATMj1lGSyMtYgtOXR0BwH+1wKyBApbU0BSESRAAgVAOGoQBAABADT/8gIoAsAAJQAAATIWFyM -uASMiBhUUFhceARUUBiMiJiczHgEzMjY1NCYnLgE1NDYBOmd2ClwGS0E6SUNRdW+HZnKKC1wPWkQ9 -Uk1cZGuEAsBwXUJHNjQ3OhIbZVZZbm5kREo+NT5DFRdYUFdrAAAAAAEAIgAAAmQCsgAHAAABIxEjE -SM1IQJk9lb2AkICbP2UAmxGAAEAXv/yAmQCsgAXAAABERQHDgEiJicmNREzERQXHgEyNjc2NRECZA -IIgfCBCAJWAgZYmlgGAgKy/k0qFFxzc1wUKgGz/lUrEkRQUEQSKwGrAAAAAAEAIAAAAnoCsgAGAAA -hIwMzGwEzAYJ07l3N1FwCsv2PAnEAAAEAGgAAA7ECsgAMAAABAyMLASMDMxsBMxsBA7HAcZyicrZi -kaB0nJkCsv1OAlP9rQKy/ZsCW/2kAmYAAAEAGQAAAm8CsgALAAAhCwEjEwMzGwEzAxMCCsrEY/bkY -re+Y/D6AST+3AFcAVb+5gEa/q3+oQAAAQATAAACUQKyAAgAAAERIxEDMxsBMwFdVvRjwLphARD+8A -EQAaL+sQFPAAABAC4AAAI5ArIACQAAJRUhNQEhNSEVAQI5/fUBof57Aen+YUZGQgIqRkX92QAAAAA -BAGL/sAEFAwwABwAAARUjETMVIxEBBWlpowMMOP0UOANcAAAB//v/4gE0AtAAAwAABSMDMwE0Pvs+ -HgLuAAAAAQAi/7AAxQMMAAcAABcjNTMRIzUzxaNpaaNQOALsOAABAFAA1wH0AmgABgAAJQsBIxMzE -wGwjY1GsESw1wFZ/qcBkf5vAAAAAQAy/6oBwv/iAAMAAAUhNSEBwv5wAZBWOAAAAAEAKQJEALYCsg -ADAAATIycztjhVUAJEbgAAAAACACT/8gHQAiAAHQAlAAAhJwcGIyImNTQ2OwE1NCcmIyIHIz4BMzI -XFh0BFBcnMjY9ASYVFAF6CR0wVUtgkJoiAgdgaQlaBm1Zrg4DCuQ9R+5MOSFQR1tbDiwUUXBUXowf -J8c9SjRORzYSgVwAAAAAAgBK//ICRQLfABEAHgAAATIWFRQGIyImLwEVIxEzETc2EzI2NTQmIyIGH -QEUFgFUcYCVbiNJEyNWVigySElcU01JXmECIJd4i5QTEDRJAt/+3jkq/hRuZV55ZWsdX14AAQAe// -IB9wIgABgAAAEyFhcjJiMiBhUUFjMyNjczDgEjIiY1NDYBF152DFocbEJXU0A1Rw1aE3pbaoKQAiB -oWH5qZm1tPDlaXYuLgZcAAAACAB7/8gIZAt8AEQAeAAABESM1BwYjIiY1NDYzMhYfAREDMjY9ATQm -IyIGFRQWAhlWKDJacYCVbiNJEyOnSV5hQUlcUwLf/SFVOSqXeIuUExA0ARb9VWVrHV9ebmVeeQACA -B7/8gH9AiAAFQAbAAABFAchHgEzMjY3Mw4BIyImNTQ2MzIWJyIGByEmAf0C/oAGUkA1SwlaD4FXbI -WObmt45UBVBwEqDQEYFhNjWD84W16Oh3+akU9aU60AAAEAFQAAARoC8gAWAAATBh0BMxUjESMRIzU -zNTQ3PgEzMhcVJqcDbW1WOTkDB0k8Hx5oAngVITRC/jQBzEIsJRs5PwVHEwAAAAIAHv8uAhkCIAAi -AC8AAAERFAcOASMiLwEzFx4BMzI2NzY9AQcGIyImNTQ2MzIWHwE1AzI2PQE0JiMiBhUUFgIZAQSEd -NwRAVcBBU5DTlUDASgyWnGAlW4jSRMjp0leYUFJXFMCEv5wSh1zeq8KCTI8VU0ZIQk5Kpd4i5QTED -RJ/iJlax1fXm5lXnkAAQBKAAACCgLkABcAAAEWFREjETQnLgEHDgEdASMRMxE3NjMyFgIIAlYCBDs -6RVRWViE5UVViAYUbQP7WASQxGzI7AQJyf+kC5P7TPSxUAAACAD4AAACsAsAABwALAAASMhYUBiIm -NBMjETNeLiAgLiBiVlYCwCAuICAu/WACEgAC//P/LgCnAsAABwAVAAASMhYUBiImNBcRFAcGIyInN -RY3NjURWS4gIC4gYgMLcRwNSgYCAsAgLiAgLo79wCUbZAJGBzMOHgJEAAAAAQBKAAACCALfAAsAAC -EnBxUjETMREzMHEwGTwTJWVvdu9/rgN6kC3/4oAQv6/ugAAQBG//wA3gLfAA8AABMRFBceATcVBiM -iJicmNRGcAQIcIxkkKi4CAQLf/bkhERoSBD4EJC8SNAJKAAAAAQBKAAADEAIgACQAAAEWFREjETQn -JiMiFREjETQnJiMiFREjETMVNzYzMhYXNzYzMhYDCwVWBAxedFYEDF50VlYiJko7ThAvJkpEVAGfI -jn+vAEcQyRZ1v76ARxDJFnW/voCEk08HzYtRB9HAAAAAAEASgAAAgoCIAAWAAABFhURIxE0JyYjIg -YdASMRMxU3NjMyFgIIAlYCCXBEVVZWITlRVWIBhRtA/tYBJDEbbHR/6QISWz0sVAAAAAACAB7/8gI -sAiAABwARAAASIBYUBiAmNBIyNjU0JiIGFRSlAQCHh/8Ah7ieWlqeWgIgn/Cfn/D+s3ZfYHV1YF8A -AgBK/zwCRQIgABEAHgAAATIWFRQGIyImLwERIxEzFTc2EzI2NTQmIyIGHQEUFgFUcYCVbiNJEyNWV -igySElcU01JXmECIJd4i5QTEDT+8wLWVTkq/hRuZV55ZWsdX14AAgAe/zwCGQIgABEAHgAAAREjEQ -cGIyImNTQ2MzIWHwE1AzI2PQE0JiMiBhUUFgIZVigyWnGAlW4jSRMjp0leYUFJXFMCEv0qARk5Kpd -4i5QTEDRJ/iJlax1fXm5lXnkAAQBKAAABPgIeAA0AAAEyFxUmBhURIxEzFTc2ARoWDkdXVlYwIwIe -B0EFVlf+0gISU0cYAAEAGP/yAa0CIAAjAAATMhYXIyYjIgYVFBYXHgEVFAYjIiYnMxYzMjY1NCYnL -gE1NDbkV2MJWhNdKy04PF1XbVhWbgxaE2ktOjlEUllkAiBaS2MrJCUoEBlPQkhOVFZoKCUmLhIWSE -BIUwAAAAEAFP/4ARQCiQAXAAATERQXHgE3FQYjIiYnJjURIzUzNTMVMxWxAQMmMx8qMjMEAUdHVmM -BzP7PGw4mFgY/BSwxDjQBNUJ7e0IAAAABAEL/8gICAhIAFwAAAREjNQcGIyImJyY1ETMRFBceATMy -Nj0BAgJWITlRT2EKBVYEBkA1RFECEv3uWj4qTToiOQE+/tIlJC43c4DpAAAAAAEAAQAAAfwCEgAGA -AABAyMDMxsBAfzJaclfop8CEv3uAhL+LQHTAAABAAEAAAMLAhIADAAAAQMjCwEjAzMbATMbAQMLqW -Z2dmapY3t0a3Z7AhL97gG+/kICEv5AAcD+QwG9AAAB//oAAAHWAhIACwAAARMjJwcjEwMzFzczARq -8ZIuKY763ZoWFYwEO/vLV1QEMAQbNzQAAAQAB/y4B+wISABEAAAEDDgEjIic1FjMyNj8BAzMbAQH7 -2iFZQB8NDRIpNhQH02GenQIS/cFVUAJGASozEwIt/i4B0gABABQAAAGxAg4ACQAAJRUhNQEhNSEVA -QGx/mMBNP7iAYL+zkREQgGIREX+ewAAAAABAED/sAEOAwwALAAAASMiBhUUFxYVFAYHHgEVFAcGFR -QWOwEVIyImNTQ3NjU0JzU2NTQnJjU0NjsBAQ4MKiMLDS4pKS4NCyMqDAtERAwLUlILDERECwLUGBk -WTlsgKzUFBTcrIFtOFhkYOC87GFVMIkUIOAhFIkxVGDsvAAAAAAEAYP84AJoDIAADAAAXIxEzmjo6 -yAPoAAEAIf+wAO8DDAAsAAATFQYVFBcWFRQGKwE1MzI2NTQnJjU0NjcuATU0NzY1NCYrATUzMhYVF -AcGFRTvUgsMREQLDCojCw0uKSkuDQsjKgwLREQMCwF6OAhFIkxVGDsvOBgZFk5bICs1BQU3KyBbTh -YZGDgvOxhVTCJFAAABAE0A3wH2AWQAEwAAATMUIyImJyYjIhUjNDMyFhcWMzIBvjhuGywtQR0xOG4 -bLC1BHTEBZIURGCNMhREYIwAAAwAk/94DIgLoAAcAEQApAAAAIBYQBiAmECQgBhUUFiA2NTQlMhYX -IyYjIgYUFjMyNjczDgEjIiY1NDYBAQFE3d3+vN0CB/7wubkBELn+xVBnD1wSWDo+QTcqOQZcEmZWX -HN2Aujg/rbg4AFKpr+Mjb6+jYxbWEldV5ZZNShLVn5na34AAgB4AFIB9AGeAAUACwAAAQcXIyc3Mw -cXIyc3AUqJiUmJifOJiUmJiQGepqampqampqYAAAIAHAHSAQ4CwAAHAA8AABIyFhQGIiY0NiIGFBY -yNjRgakREakSTNCEhNCECwEJqQkJqCiM4IyM4AAAAAAIAUAAAAfQCCwALAA8AAAEzFSMVIzUjNTM1 -MxMhNSEBP7W1OrW1OrX+XAGkAVs4tLQ4sP31OAAAAQB0AkQBAQKyAAMAABMjNzOsOD1QAkRuAAAAA -AEAIADsAKoBdgAHAAASMhYUBiImNEg6KCg6KAF2KDooKDoAAAIAOQBSAbUBngAFAAsAACUHIzcnMw -UHIzcnMwELiUmJiUkBM4lJiYlJ+KampqampqYAAAABADYB5QDhAt8ABAAAEzczByM2Xk1OXQHv8Po -AAQAWAeUAwQLfAAQAABMHIzczwV5NTl0C1fD6AAIANgHlAYsC3wAEAAkAABM3MwcjPwEzByM2Xk1O -XapeTU5dAe/w+grw+gAAAgAWAeUBawLfAAQACQAAEwcjNzMXByM3M8FeTU5dql5NTl0C1fD6CvD6A -AADACX/8gI1AHIABwAPABcAADYyFhQGIiY0NjIWFAYiJjQ2MhYUBiImNEk4JCQ4JOw4JCQ4JOw4JC -Q4JHIkOCQkOCQkOCQkOCQkOCQkOAAAAAEAeABSAUoBngAFAAABBxcjJzcBSomJSYmJAZ6mpqamAAA -AAAEAOQBSAQsBngAFAAAlByM3JzMBC4lJiYlJ+KampgAAAf9qAAABgQKyAAMAACsBATM/VwHAVwKy -AAAAAAIAFAHIAdwClAAHABQAABMVIxUjNSM1BRUjNwcjJxcjNTMXN9pKMkoByDICKzQqATJLKysCl -CmjoykBy46KiY3Lm5sAAQAVAAABvALyABgAAAERIxEjESMRIzUzNTQ3NjMyFxUmBgcGHQEBvFbCVj -k5AxHHHx5iVgcDAg798gHM/jQBzEIOJRuWBUcIJDAVIRYAAAABABX//AHkAvIAJQAAJR4BNxUGIyI -mJyY1ESYjIgcGHQEzFSMRIxEjNTM1NDc2MzIXERQBowIcIxkkKi4CAR4nXgwDbW1WLy8DEbNdOmYa -EQQ/BCQvEjQCFQZWFSEWQv40AcxCDiUblhP9uSEAAAAAAAAWAQ4AAQAAAAAAAAATACgAAQAAAAAAA -QAHAEwAAQAAAAAAAgAHAGQAAQAAAAAAAwAaAKIAAQAAAAAABAAHAM0AAQAAAAAABQA8AU8AAQAAAA -AABgAPAawAAQAAAAAACAALAdQAAQAAAAAACQALAfgAAQAAAAAACwAXAjQAAQAAAAAADAAXAnwAAwA -BBAkAAAAmAAAAAwABBAkAAQAOADwAAwABBAkAAgAOAFQAAwABBAkAAwA0AGwAAwABBAkABAAOAL0A -AwABBAkABQB4ANUAAwABBAkABgAeAYwAAwABBAkACAAWAbwAAwABBAkACQAWAeAAAwABBAkACwAuA -gQAAwABBAkADAAuAkwATgBvACAAUgBpAGcAaAB0AHMAIABSAGUAcwBlAHIAdgBlAGQALgAATm8gUm -lnaHRzIFJlc2VydmVkLgAAQQBpAGwAZQByAG8AbgAAQWlsZXJvbgAAUgBlAGcAdQBsAGEAcgAAUmV -ndWxhcgAAMQAuADEAMAAyADsAVQBLAFcATgA7AEEAaQBsAGUAcgBvAG4ALQBSAGUAZwB1AGwAYQBy -AAAxLjEwMjtVS1dOO0FpbGVyb24tUmVndWxhcgAAQQBpAGwAZQByAG8AbgAAQWlsZXJvbgAAVgBlA -HIAcwBpAG8AbgAgADEALgAxADAAMgA7AFAAUwAgADAAMAAxAC4AMQAwADIAOwBoAG8AdABjAG8Abg -B2ACAAMQAuADAALgA3ADAAOwBtAGEAawBlAG8AdABmAC4AbABpAGIAMgAuADUALgA1ADgAMwAyADk -AAFZlcnNpb24gMS4xMDI7UFMgMDAxLjEwMjtob3Rjb252IDEuMC43MDttYWtlb3RmLmxpYjIuNS41 -ODMyOQAAQQBpAGwAZQByAG8AbgAtAFIAZQBnAHUAbABhAHIAAEFpbGVyb24tUmVndWxhcgAAUwBvA -HIAYQAgAFMAYQBnAGEAbgBvAABTb3JhIFNhZ2FubwAAUwBvAHIAYQAgAFMAYQBnAGEAbgBvAABTb3 -JhIFNhZ2FubwAAaAB0AHQAcAA6AC8ALwB3AHcAdwAuAGQAbwB0AGMAbwBsAG8AbgAuAG4AZQB0AAB -odHRwOi8vd3d3LmRvdGNvbG9uLm5ldAAAaAB0AHQAcAA6AC8ALwB3AHcAdwAuAGQAbwB0AGMAbwBs -AG8AbgAuAG4AZQB0AABodHRwOi8vd3d3LmRvdGNvbG9uLm5ldAAAAAACAAAAAAAA/4MAMgAAAAAAA -AAAAAAAAAAAAAAAAAAAAHQAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATAB -QAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAA -xADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0A -TgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAIsAqQCDAJMAjQDDAKoAtgC3A -LQAtQCrAL4AvwC8AIwAwADBAAAAAAAB//8AAgABAAAADAAAABwAAAACAAIAAwBxAAEAcgBzAAIABA -AAAAIAAAABAAAACgBMAGYAAkRGTFQADmxhdG4AGgAEAAAAAP//AAEAAAAWAANDQVQgAB5NT0wgABZ -ST00gABYAAP//AAEAAAAA//8AAgAAAAEAAmxpZ2EADmxvY2wAFAAAAAEAAQAAAAEAAAACAAYAEAAG -AAAAAgASADQABAAAAAEATAADAAAAAgAQABYAAQAcAAAAAQABAE8AAQABAGcAAQABAE8AAwAAAAIAE -AAWAAEAHAAAAAEAAQAvAAEAAQBnAAEAAQAvAAEAGgABAAgAAgAGAAwAcwACAE8AcgACAEwAAQABAE -kAAAABAAAACgBGAGAAAkRGTFQADmxhdG4AHAAEAAAAAP//AAIAAAABABYAA0NBVCAAFk1PTCAAFlJ -PTSAAFgAA//8AAgAAAAEAAmNwc3AADmtlcm4AFAAAAAEAAAAAAAEAAQACAAYADgABAAAAAQASAAIA -AAACAB4ANgABAAoABQAFAAoAAgABACQAPQAAAAEAEgAEAAAAAQAMAAEAOP/nAAEAAQAkAAIGigAEA -AAFJAXKABoAGQAA//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAD/sv+4/+z/7v/MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9T/6AAAAAD/8QAA -ABD/vQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7gAAAAAAAAAAAAAAAAAA//MAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIAAAAAAAAAAP/5AAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAD/4AAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//L/9AAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAA/+gAAAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/zAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/mAAAAAAAAAAAAAAAAAAD -/4gAA//AAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+AAAAAAAAP/OAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/zv/qAAAAAP/0AAAACAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/ZAAD/egAA/1kAAAAA/5D/rgAAAAAAAAAAAA -AAAAAAAAAAAAAAAAD/9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAD/8AAA/7b/8P+wAAD/8P/E/98AAAAA/8P/+P/0//oAAAAAAAAAAAAA//gA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/w//C/9MAAP/SAAD/9wAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAD/yAAA/+kAAAAA//QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9wAAAAD//QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAP/cAAAAAAAAAAAAAAAA/7YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/6AAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAkAFAAEAAAAAQACwAAABcA -BgAAAAAAAAAIAA4AAAAAAAsAEgAAAAAAAAATABkAAwANAAAAAQAJAAAAAAAAAAAAAAAAAAAAGAAAA -AAABwAAAAAAAAAAAAAAFQAFAAAAAAAYABgAAAAUAAAACgAAAAwAAgAPABEAFgAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAEAEQBdAAYAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAcAAAAAAAAABwAAAAAACAAAAAAAAAAAAAcAAAAHAAAAEwAJ -ABUADgAPAAAACwAQAAAAAAAAAAAAAAAAAAUAGAACAAIAAgAAAAIAGAAXAAAAGAAAABYAFgACABYAA -gAWAAAAEQADAAoAFAAMAA0ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAAAAEgAGAAEAHgAkAC -YAJwApACoALQAuAC8AMgAzADcAOAA5ADoAPAA9AEUASABOAE8AUgBTAFUAVwBZAFoAWwBcAF0AcwA -AAAAAAQAAAADa3tfFAAAAANAan9kAAAAA4QodoQ== -""")), - 10 if size is None else size, - layout_engine=Layout.BASIC, - ) - return load_default_imagefont() diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageGrab.py b/.venv/lib/python3.12/site-packages/PIL/ImageGrab.py deleted file mode 100644 index 66ee6dd3..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageGrab.py +++ /dev/null @@ -1,231 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# screen grabber -# -# History: -# 2001-04-26 fl created -# 2001-09-17 fl use builtin driver, if present -# 2002-11-19 fl added grabclipboard support -# -# Copyright (c) 2001-2002 by Secret Labs AB -# Copyright (c) 2001-2002 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import shutil -import subprocess -import sys -import tempfile - -from . import Image - -TYPE_CHECKING = False -if TYPE_CHECKING: - from . import ImageWin - - -def grab( - bbox: tuple[int, int, int, int] | None = None, - include_layered_windows: bool = False, - all_screens: bool = False, - xdisplay: str | None = None, - window: int | ImageWin.HWND | None = None, -) -> Image.Image: - im: Image.Image - if xdisplay is None: - if sys.platform == "darwin": - fh, filepath = tempfile.mkstemp(".png") - os.close(fh) - args = ["screencapture"] - if window is not None: - args += ["-l", str(window)] - elif bbox: - left, top, right, bottom = bbox - args += ["-R", f"{left},{top},{right-left},{bottom-top}"] - args += ["-x", filepath] - retcode = subprocess.call(args) - if retcode: - raise subprocess.CalledProcessError(retcode, args) - im = Image.open(filepath) - im.load() - os.unlink(filepath) - if bbox: - if window is not None: - # Determine if the window was in Retina mode or not - # by capturing it without the shadow, - # and checking how different the width is - fh, filepath = tempfile.mkstemp(".png") - os.close(fh) - args = ["screencapture", "-l", str(window), "-o", "-x", filepath] - retcode = subprocess.call(args) - if retcode: - raise subprocess.CalledProcessError(retcode, args) - with Image.open(filepath) as im_no_shadow: - retina = im.width - im_no_shadow.width > 100 - os.unlink(filepath) - - # Since screencapture's -R does not work with -l, - # crop the image manually - if retina: - left, top, right, bottom = bbox - im_cropped = im.resize( - (right - left, bottom - top), - box=tuple(coord * 2 for coord in bbox), - ) - else: - im_cropped = im.crop(bbox) - im.close() - return im_cropped - else: - im_resized = im.resize((right - left, bottom - top)) - im.close() - return im_resized - return im - elif sys.platform == "win32": - if window is not None: - all_screens = -1 - offset, size, data = Image.core.grabscreen_win32( - include_layered_windows, - all_screens, - int(window) if window is not None else 0, - ) - im = Image.frombytes( - "RGB", - size, - data, - # RGB, 32-bit line padding, origin lower left corner - "raw", - "BGR", - (size[0] * 3 + 3) & -4, - -1, - ) - if bbox: - x0, y0 = offset - left, top, right, bottom = bbox - im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) - return im - # Cast to Optional[str] needed for Windows and macOS. - display_name: str | None = xdisplay - try: - if not Image.core.HAVE_XCB: - msg = "Pillow was built without XCB support" - raise OSError(msg) - size, data = Image.core.grabscreen_x11(display_name) - except OSError: - if display_name is None and sys.platform not in ("darwin", "win32"): - if shutil.which("gnome-screenshot"): - args = ["gnome-screenshot", "-f"] - elif shutil.which("grim"): - args = ["grim"] - elif shutil.which("spectacle"): - args = ["spectacle", "-n", "-b", "-f", "-o"] - else: - raise - fh, filepath = tempfile.mkstemp(".png") - os.close(fh) - args.append(filepath) - retcode = subprocess.call(args) - if retcode: - raise subprocess.CalledProcessError(retcode, args) - im = Image.open(filepath) - im.load() - os.unlink(filepath) - if bbox: - im_cropped = im.crop(bbox) - im.close() - return im_cropped - return im - else: - raise - else: - im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1) - if bbox: - im = im.crop(bbox) - return im - - -def grabclipboard() -> Image.Image | list[str] | None: - if sys.platform == "darwin": - p = subprocess.run( - ["osascript", "-e", "get the clipboard as «class PNGf»"], - capture_output=True, - ) - if p.returncode != 0: - return None - - import binascii - - data = io.BytesIO(binascii.unhexlify(p.stdout[11:-3])) - return Image.open(data) - elif sys.platform == "win32": - fmt, data = Image.core.grabclipboard_win32() - if fmt == "file": # CF_HDROP - import struct - - o = struct.unpack_from("I", data)[0] - if data[16] == 0: - files = data[o:].decode("mbcs").split("\0") - else: - files = data[o:].decode("utf-16le").split("\0") - return files[: files.index("")] - if isinstance(data, bytes): - data = io.BytesIO(data) - if fmt == "png": - from . import PngImagePlugin - - return PngImagePlugin.PngImageFile(data) - elif fmt == "DIB": - from . import BmpImagePlugin - - return BmpImagePlugin.DibImageFile(data) - return None - else: - if os.getenv("WAYLAND_DISPLAY"): - session_type = "wayland" - elif os.getenv("DISPLAY"): - session_type = "x11" - else: # Session type check failed - session_type = None - - if shutil.which("wl-paste") and session_type in ("wayland", None): - args = ["wl-paste", "-t", "image"] - elif shutil.which("xclip") and session_type in ("x11", None): - args = ["xclip", "-selection", "clipboard", "-t", "image/png", "-o"] - else: - msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux" - raise NotImplementedError(msg) - - p = subprocess.run(args, capture_output=True) - if p.returncode != 0: - err = p.stderr - for silent_error in [ - # wl-paste, when the clipboard is empty - b"Nothing is copied", - # Ubuntu/Debian wl-paste, when the clipboard is empty - b"No selection", - # Ubuntu/Debian wl-paste, when an image isn't available - b"No suitable type of content copied", - # wl-paste or Ubuntu/Debian xclip, when an image isn't available - b" not available", - # xclip, when an image isn't available - b"cannot convert ", - # xclip, when the clipboard isn't initialized - b"xclip: Error: There is no owner for the ", - ]: - if silent_error in err: - return None - msg = f"{args[0]} error" - if err: - msg += f": {err.strip().decode()}" - raise ChildProcessError(msg) - - data = io.BytesIO(p.stdout) - im = Image.open(data) - im.load() - return im diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageMath.py b/.venv/lib/python3.12/site-packages/PIL/ImageMath.py deleted file mode 100644 index dfdc50c0..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageMath.py +++ /dev/null @@ -1,314 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# a simple math add-on for the Python Imaging Library -# -# History: -# 1999-02-15 fl Original PIL Plus release -# 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6 -# 2005-09-12 fl Fixed int() and float() for Python 2.4.1 -# -# Copyright (c) 1999-2005 by Secret Labs AB -# Copyright (c) 2005 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import builtins - -from . import Image, _imagingmath - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from types import CodeType - from typing import Any - - -class _Operand: - """Wraps an image operand, providing standard operators""" - - def __init__(self, im: Image.Image): - self.im = im - - def __fixup(self, im1: _Operand | float) -> Image.Image: - # convert image to suitable mode - if isinstance(im1, _Operand): - # argument was an image. - if im1.im.mode in ("1", "L"): - return im1.im.convert("I") - elif im1.im.mode in ("I", "F"): - return im1.im - else: - msg = f"unsupported mode: {im1.im.mode}" - raise ValueError(msg) - else: - # argument was a constant - if isinstance(im1, (int, float)) and self.im.mode in ("1", "L", "I"): - return Image.new("I", self.im.size, im1) - else: - return Image.new("F", self.im.size, im1) - - def apply( - self, - op: str, - im1: _Operand | float, - im2: _Operand | float | None = None, - mode: str | None = None, - ) -> _Operand: - im_1 = self.__fixup(im1) - if im2 is None: - # unary operation - out = Image.new(mode or im_1.mode, im_1.size, None) - try: - op = getattr(_imagingmath, f"{op}_{im_1.mode}") - except AttributeError as e: - msg = f"bad operand type for '{op}'" - raise TypeError(msg) from e - _imagingmath.unop(op, out.getim(), im_1.getim()) - else: - # binary operation - im_2 = self.__fixup(im2) - if im_1.mode != im_2.mode: - # convert both arguments to floating point - if im_1.mode != "F": - im_1 = im_1.convert("F") - if im_2.mode != "F": - im_2 = im_2.convert("F") - if im_1.size != im_2.size: - # crop both arguments to a common size - size = ( - min(im_1.size[0], im_2.size[0]), - min(im_1.size[1], im_2.size[1]), - ) - if im_1.size != size: - im_1 = im_1.crop((0, 0) + size) - if im_2.size != size: - im_2 = im_2.crop((0, 0) + size) - out = Image.new(mode or im_1.mode, im_1.size, None) - try: - op = getattr(_imagingmath, f"{op}_{im_1.mode}") - except AttributeError as e: - msg = f"bad operand type for '{op}'" - raise TypeError(msg) from e - _imagingmath.binop(op, out.getim(), im_1.getim(), im_2.getim()) - return _Operand(out) - - # unary operators - def __bool__(self) -> bool: - # an image is "true" if it contains at least one non-zero pixel - return self.im.getbbox() is not None - - def __abs__(self) -> _Operand: - return self.apply("abs", self) - - def __pos__(self) -> _Operand: - return self - - def __neg__(self) -> _Operand: - return self.apply("neg", self) - - # binary operators - def __add__(self, other: _Operand | float) -> _Operand: - return self.apply("add", self, other) - - def __radd__(self, other: _Operand | float) -> _Operand: - return self.apply("add", other, self) - - def __sub__(self, other: _Operand | float) -> _Operand: - return self.apply("sub", self, other) - - def __rsub__(self, other: _Operand | float) -> _Operand: - return self.apply("sub", other, self) - - def __mul__(self, other: _Operand | float) -> _Operand: - return self.apply("mul", self, other) - - def __rmul__(self, other: _Operand | float) -> _Operand: - return self.apply("mul", other, self) - - def __truediv__(self, other: _Operand | float) -> _Operand: - return self.apply("div", self, other) - - def __rtruediv__(self, other: _Operand | float) -> _Operand: - return self.apply("div", other, self) - - def __mod__(self, other: _Operand | float) -> _Operand: - return self.apply("mod", self, other) - - def __rmod__(self, other: _Operand | float) -> _Operand: - return self.apply("mod", other, self) - - def __pow__(self, other: _Operand | float) -> _Operand: - return self.apply("pow", self, other) - - def __rpow__(self, other: _Operand | float) -> _Operand: - return self.apply("pow", other, self) - - # bitwise - def __invert__(self) -> _Operand: - return self.apply("invert", self) - - def __and__(self, other: _Operand | float) -> _Operand: - return self.apply("and", self, other) - - def __rand__(self, other: _Operand | float) -> _Operand: - return self.apply("and", other, self) - - def __or__(self, other: _Operand | float) -> _Operand: - return self.apply("or", self, other) - - def __ror__(self, other: _Operand | float) -> _Operand: - return self.apply("or", other, self) - - def __xor__(self, other: _Operand | float) -> _Operand: - return self.apply("xor", self, other) - - def __rxor__(self, other: _Operand | float) -> _Operand: - return self.apply("xor", other, self) - - def __lshift__(self, other: _Operand | float) -> _Operand: - return self.apply("lshift", self, other) - - def __rshift__(self, other: _Operand | float) -> _Operand: - return self.apply("rshift", self, other) - - # logical - def __eq__(self, other: _Operand | float) -> _Operand: # type: ignore[override] - return self.apply("eq", self, other) - - def __ne__(self, other: _Operand | float) -> _Operand: # type: ignore[override] - return self.apply("ne", self, other) - - def __lt__(self, other: _Operand | float) -> _Operand: - return self.apply("lt", self, other) - - def __le__(self, other: _Operand | float) -> _Operand: - return self.apply("le", self, other) - - def __gt__(self, other: _Operand | float) -> _Operand: - return self.apply("gt", self, other) - - def __ge__(self, other: _Operand | float) -> _Operand: - return self.apply("ge", self, other) - - -# conversions -def imagemath_int(self: _Operand) -> _Operand: - return _Operand(self.im.convert("I")) - - -def imagemath_float(self: _Operand) -> _Operand: - return _Operand(self.im.convert("F")) - - -# logical -def imagemath_equal(self: _Operand, other: _Operand | float | None) -> _Operand: - return self.apply("eq", self, other, mode="I") - - -def imagemath_notequal(self: _Operand, other: _Operand | float | None) -> _Operand: - return self.apply("ne", self, other, mode="I") - - -def imagemath_min(self: _Operand, other: _Operand | float | None) -> _Operand: - return self.apply("min", self, other) - - -def imagemath_max(self: _Operand, other: _Operand | float | None) -> _Operand: - return self.apply("max", self, other) - - -def imagemath_convert(self: _Operand, mode: str) -> _Operand: - return _Operand(self.im.convert(mode)) - - -ops = { - "int": imagemath_int, - "float": imagemath_float, - "equal": imagemath_equal, - "notequal": imagemath_notequal, - "min": imagemath_min, - "max": imagemath_max, - "convert": imagemath_convert, -} - - -def lambda_eval(expression: Callable[[dict[str, Any]], Any], **kw: Any) -> Any: - """ - Returns the result of an image function. - - :py:mod:`~PIL.ImageMath` only supports single-layer images. To process multi-band - images, use the :py:meth:`~PIL.Image.Image.split` method or - :py:func:`~PIL.Image.merge` function. - - :param expression: A function that receives a dictionary. - :param **kw: Values to add to the function's dictionary. - :return: The expression result. This is usually an image object, but can - also be an integer, a floating point value, or a pixel tuple, - depending on the expression. - """ - - args: dict[str, Any] = ops.copy() - args.update(kw) - for k, v in args.items(): - if isinstance(v, Image.Image): - args[k] = _Operand(v) - - out = expression(args) - try: - return out.im - except AttributeError: - return out - - -def unsafe_eval(expression: str, **kw: Any) -> Any: - """ - Evaluates an image expression. This uses Python's ``eval()`` function to process - the expression string, and carries the security risks of doing so. It is not - recommended to process expressions without considering this. - :py:meth:`~lambda_eval` is a more secure alternative. - - :py:mod:`~PIL.ImageMath` only supports single-layer images. To process multi-band - images, use the :py:meth:`~PIL.Image.Image.split` method or - :py:func:`~PIL.Image.merge` function. - - :param expression: A string containing a Python-style expression. - :param **kw: Values to add to the evaluation context. - :return: The evaluated expression. This is usually an image object, but can - also be an integer, a floating point value, or a pixel tuple, - depending on the expression. - """ - - # build execution namespace - args: dict[str, Any] = ops.copy() - for k in kw: - if "__" in k or hasattr(builtins, k): - msg = f"'{k}' not allowed" - raise ValueError(msg) - - args.update(kw) - for k, v in args.items(): - if isinstance(v, Image.Image): - args[k] = _Operand(v) - - compiled_code = compile(expression, "", "eval") - - def scan(code: CodeType) -> None: - for const in code.co_consts: - if type(const) is type(compiled_code): - scan(const) - - for name in code.co_names: - if name not in args and name != "abs": - msg = f"'{name}' not allowed" - raise ValueError(msg) - - scan(compiled_code) - out = builtins.eval(expression, {"__builtins": {"abs": abs}}, args) - try: - return out.im - except AttributeError: - return out diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageMode.py b/.venv/lib/python3.12/site-packages/PIL/ImageMode.py deleted file mode 100644 index b7c6c863..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageMode.py +++ /dev/null @@ -1,85 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard mode descriptors -# -# History: -# 2006-03-20 fl Added -# -# Copyright (c) 2006 by Secret Labs AB. -# Copyright (c) 2006 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import sys -from functools import lru_cache -from typing import NamedTuple - - -class ModeDescriptor(NamedTuple): - """Wrapper for mode strings.""" - - mode: str - bands: tuple[str, ...] - basemode: str - basetype: str - typestr: str - - def __str__(self) -> str: - return self.mode - - -@lru_cache -def getmode(mode: str) -> ModeDescriptor: - """Gets a mode descriptor for the given mode.""" - endian = "<" if sys.byteorder == "little" else ">" - - modes = { - # core modes - # Bits need to be extended to bytes - "1": ("L", "L", ("1",), "|b1"), - "L": ("L", "L", ("L",), "|u1"), - "I": ("L", "I", ("I",), f"{endian}i4"), - "F": ("L", "F", ("F",), f"{endian}f4"), - "P": ("P", "L", ("P",), "|u1"), - "RGB": ("RGB", "L", ("R", "G", "B"), "|u1"), - "RGBX": ("RGB", "L", ("R", "G", "B", "X"), "|u1"), - "RGBA": ("RGB", "L", ("R", "G", "B", "A"), "|u1"), - "CMYK": ("RGB", "L", ("C", "M", "Y", "K"), "|u1"), - "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr"), "|u1"), - # UNDONE - unsigned |u1i1i1 - "LAB": ("RGB", "L", ("L", "A", "B"), "|u1"), - "HSV": ("RGB", "L", ("H", "S", "V"), "|u1"), - # extra experimental modes - "RGBa": ("RGB", "L", ("R", "G", "B", "a"), "|u1"), - "LA": ("L", "L", ("L", "A"), "|u1"), - "La": ("L", "L", ("L", "a"), "|u1"), - "PA": ("RGB", "L", ("P", "A"), "|u1"), - } - if mode in modes: - base_mode, base_type, bands, type_str = modes[mode] - return ModeDescriptor(mode, bands, base_mode, base_type, type_str) - - mapping_modes = { - # I;16 == I;16L, and I;32 == I;32L - "I;16": "u2", - "I;16BS": ">i2", - "I;16N": f"{endian}u2", - "I;16NS": f"{endian}i2", - "I;32": "u4", - "I;32L": "i4", - "I;32LS": " -from __future__ import annotations - -import re - -from . import Image, _imagingmorph - -LUT_SIZE = 1 << 9 - -# fmt: off -ROTATION_MATRIX = [ - 6, 3, 0, - 7, 4, 1, - 8, 5, 2, -] -MIRROR_MATRIX = [ - 2, 1, 0, - 5, 4, 3, - 8, 7, 6, -] -# fmt: on - - -class LutBuilder: - """A class for building a MorphLut from a descriptive language - - The input patterns is a list of a strings sequences like these:: - - 4:(... - .1. - 111)->1 - - (whitespaces including linebreaks are ignored). The option 4 - describes a series of symmetry operations (in this case a - 4-rotation), the pattern is described by: - - - . or X - Ignore - - 1 - Pixel is on - - 0 - Pixel is off - - The result of the operation is described after "->" string. - - The default is to return the current pixel value, which is - returned if no other match is found. - - Operations: - - - 4 - 4 way rotation - - N - Negate - - 1 - Dummy op for no other operation (an op must always be given) - - M - Mirroring - - Example:: - - lb = LutBuilder(patterns = ["4:(... .1. 111)->1"]) - lut = lb.build_lut() - - """ - - def __init__( - self, patterns: list[str] | None = None, op_name: str | None = None - ) -> None: - """ - :param patterns: A list of input patterns, or None. - :param op_name: The name of a known pattern. One of "corner", "dilation4", - "dilation8", "erosion4", "erosion8" or "edge". - :exception Exception: If the op_name is not recognized. - """ - self.lut: bytearray | None = None - if op_name is not None: - known_patterns = { - "corner": ["1:(... ... ...)->0", "4:(00. 01. ...)->1"], - "dilation4": ["4:(... .0. .1.)->1"], - "dilation8": ["4:(... .0. .1.)->1", "4:(... .0. ..1)->1"], - "erosion4": ["4:(... .1. .0.)->0"], - "erosion8": ["4:(... .1. .0.)->0", "4:(... .1. ..0)->0"], - "edge": [ - "1:(... ... ...)->0", - "4:(.0. .1. ...)->1", - "4:(01. .1. ...)->1", - ], - } - if op_name not in known_patterns: - msg = f"Unknown pattern {op_name}!" - raise Exception(msg) - - self.patterns = known_patterns[op_name] - elif patterns is not None: - self.patterns = patterns - else: - self.patterns = [] - - def add_patterns(self, patterns: list[str]) -> None: - """ - Append to list of patterns. - - :param patterns: Additional patterns. - """ - self.patterns += patterns - - def build_default_lut(self) -> bytearray: - """ - Set the current LUT, and return it. - - This is the default LUT that patterns will be applied against when building. - """ - symbols = [0, 1] - m = 1 << 4 # pos of current pixel - self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE)) - return self.lut - - def get_lut(self) -> bytearray | None: - """ - Returns the current LUT - """ - return self.lut - - def _string_permute(self, pattern: str, permutation: list[int]) -> str: - """Takes a pattern and a permutation and returns the - string permuted according to the permutation list. - """ - assert len(permutation) == 9 - return "".join(pattern[p] for p in permutation) - - def _pattern_permute( - self, basic_pattern: str, options: str, basic_result: int - ) -> list[tuple[str, int]]: - """Takes a basic pattern and its result and clones - the pattern according to the modifications described in the $options - parameter. It returns a list of all cloned patterns.""" - patterns = [(basic_pattern, basic_result)] - - # rotations - if "4" in options: - res = patterns[-1][1] - for i in range(4): - patterns.append( - (self._string_permute(patterns[-1][0], ROTATION_MATRIX), res) - ) - # mirror - if "M" in options: - n = len(patterns) - for pattern, res in patterns[:n]: - patterns.append((self._string_permute(pattern, MIRROR_MATRIX), res)) - - # negate - if "N" in options: - n = len(patterns) - for pattern, res in patterns[:n]: - # Swap 0 and 1 - pattern = pattern.replace("0", "Z").replace("1", "0").replace("Z", "1") - res = 1 - int(res) - patterns.append((pattern, res)) - - return patterns - - def build_lut(self) -> bytearray: - """Compile all patterns into a morphology LUT, and return it. - - This is the data to be passed into MorphOp.""" - self.build_default_lut() - assert self.lut is not None - patterns = [] - - # Parse and create symmetries of the patterns strings - for p in self.patterns: - m = re.search(r"(\w):?\s*\((.+?)\)\s*->\s*(\d)", p.replace("\n", "")) - if not m: - msg = 'Syntax error in pattern "' + p + '"' - raise Exception(msg) - options = m.group(1) - pattern = m.group(2) - result = int(m.group(3)) - - # Get rid of spaces - pattern = pattern.replace(" ", "").replace("\n", "") - - patterns += self._pattern_permute(pattern, options, result) - - # Compile the patterns into regular expressions for speed - compiled_patterns = [] - for pattern in patterns: - p = pattern[0].replace(".", "X").replace("X", "[01]") - compiled_patterns.append((re.compile(p), pattern[1])) - - # Step through table and find patterns that match. - # Note that all the patterns are searched. The last one found takes priority - for i in range(LUT_SIZE): - # Build the bit pattern - bitpattern = bin(i)[2:] - bitpattern = ("0" * (9 - len(bitpattern)) + bitpattern)[::-1] - - for pattern, r in compiled_patterns: - if pattern.match(bitpattern): - self.lut[i] = [0, 1][r] - - return self.lut - - -class MorphOp: - """A class for binary morphological operators""" - - def __init__( - self, - lut: bytearray | None = None, - op_name: str | None = None, - patterns: list[str] | None = None, - ) -> None: - """Create a binary morphological operator. - - If the LUT is not provided, then it is built using LutBuilder from the op_name - or the patterns. - - :param lut: The LUT data. - :param patterns: A list of input patterns, or None. - :param op_name: The name of a known pattern. One of "corner", "dilation4", - "dilation8", "erosion4", "erosion8", "edge". - :exception Exception: If the op_name is not recognized. - """ - if patterns is None and op_name is None: - self.lut = lut - else: - self.lut = LutBuilder(patterns, op_name).build_lut() - - def apply(self, image: Image.Image) -> tuple[int, Image.Image]: - """Run a single morphological operation on an image. - - Returns a tuple of the number of changed pixels and the - morphed image. - - :param image: A 1-mode or L-mode image. - :exception Exception: If the current operator is None. - :exception ValueError: If the image is not 1 or L mode.""" - if self.lut is None: - msg = "No operator loaded" - raise Exception(msg) - - if image.mode not in ("1", "L"): - msg = "Image mode must be 1 or L" - raise ValueError(msg) - outimage = Image.new(image.mode, image.size) - count = _imagingmorph.apply(bytes(self.lut), image.getim(), outimage.getim()) - return count, outimage - - def match(self, image: Image.Image) -> list[tuple[int, int]]: - """Get a list of coordinates matching the morphological operation on - an image. - - Returns a list of tuples of (x,y) coordinates of all matching pixels. See - :ref:`coordinate-system`. - - :param image: A 1-mode or L-mode image. - :exception Exception: If the current operator is None. - :exception ValueError: If the image is not 1 or L mode.""" - if self.lut is None: - msg = "No operator loaded" - raise Exception(msg) - - if image.mode not in ("1", "L"): - msg = "Image mode must be 1 or L" - raise ValueError(msg) - return _imagingmorph.match(bytes(self.lut), image.getim()) - - def get_on_pixels(self, image: Image.Image) -> list[tuple[int, int]]: - """Get a list of all turned on pixels in a 1 or L mode image. - - Returns a list of tuples of (x,y) coordinates of all non-empty pixels. See - :ref:`coordinate-system`. - - :param image: A 1-mode or L-mode image. - :exception ValueError: If the image is not 1 or L mode.""" - - if image.mode not in ("1", "L"): - msg = "Image mode must be 1 or L" - raise ValueError(msg) - return _imagingmorph.get_on_pixels(image.getim()) - - def load_lut(self, filename: str) -> None: - """ - Load an operator from an mrl file - - :param filename: The file to read from. - :exception Exception: If the length of the file data is not 512. - """ - with open(filename, "rb") as f: - self.lut = bytearray(f.read()) - - if len(self.lut) != LUT_SIZE: - self.lut = None - msg = "Wrong size operator file!" - raise Exception(msg) - - def save_lut(self, filename: str) -> None: - """ - Save an operator to an mrl file. - - :param filename: The destination file. - :exception Exception: If the current operator is None. - """ - if self.lut is None: - msg = "No operator loaded" - raise Exception(msg) - with open(filename, "wb") as f: - f.write(self.lut) - - def set_lut(self, lut: bytearray | None) -> None: - """ - Set the LUT from an external source - - :param lut: A new LUT. - """ - self.lut = lut diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageOps.py b/.venv/lib/python3.12/site-packages/PIL/ImageOps.py deleted file mode 100644 index 42b10bd7..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageOps.py +++ /dev/null @@ -1,746 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard image operations -# -# History: -# 2001-10-20 fl Created -# 2001-10-23 fl Added autocontrast operator -# 2001-12-18 fl Added Kevin's fit operator -# 2004-03-14 fl Fixed potential division by zero in equalize -# 2005-05-05 fl Fixed equalize for low number of values -# -# Copyright (c) 2001-2004 by Secret Labs AB -# Copyright (c) 2001-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import functools -import operator -import re -from collections.abc import Sequence -from typing import Literal, Protocol, cast, overload - -from . import ExifTags, Image, ImagePalette - -# -# helpers - - -def _border(border: int | tuple[int, ...]) -> tuple[int, int, int, int]: - if isinstance(border, tuple): - if len(border) == 2: - left, top = right, bottom = border - elif len(border) == 4: - left, top, right, bottom = border - else: - left = top = right = bottom = border - return left, top, right, bottom - - -def _color(color: str | int | tuple[int, ...], mode: str) -> int | tuple[int, ...]: - if isinstance(color, str): - from . import ImageColor - - color = ImageColor.getcolor(color, mode) - return color - - -def _lut(image: Image.Image, lut: list[int]) -> Image.Image: - if image.mode == "P": - # FIXME: apply to lookup table, not image data - msg = "mode P support coming soon" - raise NotImplementedError(msg) - elif image.mode in ("L", "RGB"): - if image.mode == "RGB" and len(lut) == 256: - lut = lut + lut + lut - return image.point(lut) - else: - msg = f"not supported for mode {image.mode}" - raise OSError(msg) - - -# -# actions - - -def autocontrast( - image: Image.Image, - cutoff: float | tuple[float, float] = 0, - ignore: int | Sequence[int] | None = None, - mask: Image.Image | None = None, - preserve_tone: bool = False, -) -> Image.Image: - """ - Maximize (normalize) image contrast. This function calculates a - histogram of the input image (or mask region), removes ``cutoff`` percent of the - lightest and darkest pixels from the histogram, and remaps the image - so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - :param image: The image to process. - :param cutoff: The percent to cut off from the histogram on the low and - high ends. Either a tuple of (low, high), or a single - number for both. - :param ignore: The background pixel value (use None for no background). - :param mask: Histogram used in contrast operation is computed using pixels - within the mask. If no mask is given the entire image is used - for histogram computation. - :param preserve_tone: Preserve image tone in Photoshop-like style autocontrast. - - .. versionadded:: 8.2.0 - - :return: An image. - """ - if preserve_tone: - histogram = image.convert("L").histogram(mask) - else: - histogram = image.histogram(mask) - - lut = [] - for layer in range(0, len(histogram), 256): - h = histogram[layer : layer + 256] - if ignore is not None: - # get rid of outliers - if isinstance(ignore, int): - h[ignore] = 0 - else: - for ix in ignore: - h[ix] = 0 - if cutoff: - # cut off pixels from both ends of the histogram - if not isinstance(cutoff, tuple): - cutoff = (cutoff, cutoff) - # get number of pixels - n = 0 - for ix in range(256): - n = n + h[ix] - # remove cutoff% pixels from the low end - cut = int(n * cutoff[0] // 100) - for lo in range(256): - if cut > h[lo]: - cut = cut - h[lo] - h[lo] = 0 - else: - h[lo] -= cut - cut = 0 - if cut <= 0: - break - # remove cutoff% samples from the high end - cut = int(n * cutoff[1] // 100) - for hi in range(255, -1, -1): - if cut > h[hi]: - cut = cut - h[hi] - h[hi] = 0 - else: - h[hi] -= cut - cut = 0 - if cut <= 0: - break - # find lowest/highest samples after preprocessing - for lo in range(256): - if h[lo]: - break - for hi in range(255, -1, -1): - if h[hi]: - break - if hi <= lo: - # don't bother - lut.extend(list(range(256))) - else: - scale = 255.0 / (hi - lo) - offset = -lo * scale - for ix in range(256): - ix = int(ix * scale + offset) - if ix < 0: - ix = 0 - elif ix > 255: - ix = 255 - lut.append(ix) - return _lut(image, lut) - - -def colorize( - image: Image.Image, - black: str | tuple[int, ...], - white: str | tuple[int, ...], - mid: str | int | tuple[int, ...] | None = None, - blackpoint: int = 0, - whitepoint: int = 255, - midpoint: int = 127, -) -> Image.Image: - """ - Colorize grayscale image. - This function calculates a color wedge which maps all black pixels in - the source image to the first color and all white pixels to the - second color. If ``mid`` is specified, it uses three-color mapping. - The ``black`` and ``white`` arguments should be RGB tuples or color names; - optionally you can use three-color mapping by also specifying ``mid``. - Mapping positions for any of the colors can be specified - (e.g. ``blackpoint``), where these parameters are the integer - value corresponding to where the corresponding color should be mapped. - These parameters must have logical order, such that - ``blackpoint <= midpoint <= whitepoint`` (if ``mid`` is specified). - - :param image: The image to colorize. - :param black: The color to use for black input pixels. - :param white: The color to use for white input pixels. - :param mid: The color to use for midtone input pixels. - :param blackpoint: an int value [0, 255] for the black mapping. - :param whitepoint: an int value [0, 255] for the white mapping. - :param midpoint: an int value [0, 255] for the midtone mapping. - :return: An image. - """ - - # Initial asserts - assert image.mode == "L" - if mid is None: - assert 0 <= blackpoint <= whitepoint <= 255 - else: - assert 0 <= blackpoint <= midpoint <= whitepoint <= 255 - - # Define colors from arguments - rgb_black = cast(Sequence[int], _color(black, "RGB")) - rgb_white = cast(Sequence[int], _color(white, "RGB")) - rgb_mid = cast(Sequence[int], _color(mid, "RGB")) if mid is not None else None - - # Empty lists for the mapping - red = [] - green = [] - blue = [] - - # Create the low-end values - for i in range(blackpoint): - red.append(rgb_black[0]) - green.append(rgb_black[1]) - blue.append(rgb_black[2]) - - # Create the mapping (2-color) - if rgb_mid is None: - range_map = range(whitepoint - blackpoint) - - for i in range_map: - red.append( - rgb_black[0] + i * (rgb_white[0] - rgb_black[0]) // len(range_map) - ) - green.append( - rgb_black[1] + i * (rgb_white[1] - rgb_black[1]) // len(range_map) - ) - blue.append( - rgb_black[2] + i * (rgb_white[2] - rgb_black[2]) // len(range_map) - ) - - # Create the mapping (3-color) - else: - range_map1 = range(midpoint - blackpoint) - range_map2 = range(whitepoint - midpoint) - - for i in range_map1: - red.append( - rgb_black[0] + i * (rgb_mid[0] - rgb_black[0]) // len(range_map1) - ) - green.append( - rgb_black[1] + i * (rgb_mid[1] - rgb_black[1]) // len(range_map1) - ) - blue.append( - rgb_black[2] + i * (rgb_mid[2] - rgb_black[2]) // len(range_map1) - ) - for i in range_map2: - red.append(rgb_mid[0] + i * (rgb_white[0] - rgb_mid[0]) // len(range_map2)) - green.append( - rgb_mid[1] + i * (rgb_white[1] - rgb_mid[1]) // len(range_map2) - ) - blue.append(rgb_mid[2] + i * (rgb_white[2] - rgb_mid[2]) // len(range_map2)) - - # Create the high-end values - for i in range(256 - whitepoint): - red.append(rgb_white[0]) - green.append(rgb_white[1]) - blue.append(rgb_white[2]) - - # Return converted image - image = image.convert("RGB") - return _lut(image, red + green + blue) - - -def contain( - image: Image.Image, size: tuple[int, int], method: int = Image.Resampling.BICUBIC -) -> Image.Image: - """ - Returns a resized version of the image, set to the maximum width and height - within the requested size, while maintaining the original aspect ratio. - - :param image: The image to resize. - :param size: The requested output size in pixels, given as a - (width, height) tuple. - :param method: Resampling method to use. Default is - :py:attr:`~PIL.Image.Resampling.BICUBIC`. - See :ref:`concept-filters`. - :return: An image. - """ - - im_ratio = image.width / image.height - dest_ratio = size[0] / size[1] - - if im_ratio != dest_ratio: - if im_ratio > dest_ratio: - new_height = round(image.height / image.width * size[0]) - if new_height != size[1]: - size = (size[0], new_height) - else: - new_width = round(image.width / image.height * size[1]) - if new_width != size[0]: - size = (new_width, size[1]) - return image.resize(size, resample=method) - - -def cover( - image: Image.Image, size: tuple[int, int], method: int = Image.Resampling.BICUBIC -) -> Image.Image: - """ - Returns a resized version of the image, so that the requested size is - covered, while maintaining the original aspect ratio. - - :param image: The image to resize. - :param size: The requested output size in pixels, given as a - (width, height) tuple. - :param method: Resampling method to use. Default is - :py:attr:`~PIL.Image.Resampling.BICUBIC`. - See :ref:`concept-filters`. - :return: An image. - """ - - im_ratio = image.width / image.height - dest_ratio = size[0] / size[1] - - if im_ratio != dest_ratio: - if im_ratio < dest_ratio: - new_height = round(image.height / image.width * size[0]) - if new_height != size[1]: - size = (size[0], new_height) - else: - new_width = round(image.width / image.height * size[1]) - if new_width != size[0]: - size = (new_width, size[1]) - return image.resize(size, resample=method) - - -def pad( - image: Image.Image, - size: tuple[int, int], - method: int = Image.Resampling.BICUBIC, - color: str | int | tuple[int, ...] | None = None, - centering: tuple[float, float] = (0.5, 0.5), -) -> Image.Image: - """ - Returns a resized and padded version of the image, expanded to fill the - requested aspect ratio and size. - - :param image: The image to resize and crop. - :param size: The requested output size in pixels, given as a - (width, height) tuple. - :param method: Resampling method to use. Default is - :py:attr:`~PIL.Image.Resampling.BICUBIC`. - See :ref:`concept-filters`. - :param color: The background color of the padded image. - :param centering: Control the position of the original image within the - padded version. - - (0.5, 0.5) will keep the image centered - (0, 0) will keep the image aligned to the top left - (1, 1) will keep the image aligned to the bottom - right - :return: An image. - """ - - resized = contain(image, size, method) - if resized.size == size: - out = resized - else: - out = Image.new(image.mode, size, color) - if resized.palette: - palette = resized.getpalette() - if palette is not None: - out.putpalette(palette) - if resized.width != size[0]: - x = round((size[0] - resized.width) * max(0, min(centering[0], 1))) - out.paste(resized, (x, 0)) - else: - y = round((size[1] - resized.height) * max(0, min(centering[1], 1))) - out.paste(resized, (0, y)) - return out - - -def crop(image: Image.Image, border: int = 0) -> Image.Image: - """ - Remove border from image. The same amount of pixels are removed - from all four sides. This function works on all image modes. - - .. seealso:: :py:meth:`~PIL.Image.Image.crop` - - :param image: The image to crop. - :param border: The number of pixels to remove. - :return: An image. - """ - left, top, right, bottom = _border(border) - return image.crop((left, top, image.size[0] - right, image.size[1] - bottom)) - - -def scale( - image: Image.Image, factor: float, resample: int = Image.Resampling.BICUBIC -) -> Image.Image: - """ - Returns a rescaled image by a specific factor given in parameter. - A factor greater than 1 expands the image, between 0 and 1 contracts the - image. - - :param image: The image to rescale. - :param factor: The expansion factor, as a float. - :param resample: Resampling method to use. Default is - :py:attr:`~PIL.Image.Resampling.BICUBIC`. - See :ref:`concept-filters`. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - if factor == 1: - return image.copy() - elif factor <= 0: - msg = "the factor must be greater than 0" - raise ValueError(msg) - else: - size = (round(factor * image.width), round(factor * image.height)) - return image.resize(size, resample) - - -class SupportsGetMesh(Protocol): - """ - An object that supports the ``getmesh`` method, taking an image as an - argument, and returning a list of tuples. Each tuple contains two tuples, - the source box as a tuple of 4 integers, and a tuple of 8 integers for the - final quadrilateral, in order of top left, bottom left, bottom right, top - right. - """ - - def getmesh( - self, image: Image.Image - ) -> list[ - tuple[tuple[int, int, int, int], tuple[int, int, int, int, int, int, int, int]] - ]: ... - - -def deform( - image: Image.Image, - deformer: SupportsGetMesh, - resample: int = Image.Resampling.BILINEAR, -) -> Image.Image: - """ - Deform the image. - - :param image: The image to deform. - :param deformer: A deformer object. Any object that implements a - ``getmesh`` method can be used. - :param resample: An optional resampling filter. Same values possible as - in the PIL.Image.transform function. - :return: An image. - """ - return image.transform( - image.size, Image.Transform.MESH, deformer.getmesh(image), resample - ) - - -def equalize(image: Image.Image, mask: Image.Image | None = None) -> Image.Image: - """ - Equalize the image histogram. This function applies a non-linear - mapping to the input image, in order to create a uniform - distribution of grayscale values in the output image. - - :param image: The image to equalize. - :param mask: An optional mask. If given, only the pixels selected by - the mask are included in the analysis. - :return: An image. - """ - if image.mode == "P": - image = image.convert("RGB") - h = image.histogram(mask) - lut = [] - for b in range(0, len(h), 256): - histo = [_f for _f in h[b : b + 256] if _f] - if len(histo) <= 1: - lut.extend(list(range(256))) - else: - step = (functools.reduce(operator.add, histo) - histo[-1]) // 255 - if not step: - lut.extend(list(range(256))) - else: - n = step // 2 - for i in range(256): - lut.append(n // step) - n = n + h[i + b] - return _lut(image, lut) - - -def expand( - image: Image.Image, - border: int | tuple[int, ...] = 0, - fill: str | int | tuple[int, ...] = 0, -) -> Image.Image: - """ - Add border to the image - - :param image: The image to expand. - :param border: Border width, in pixels. - :param fill: Pixel fill value (a color value). Default is 0 (black). - :return: An image. - """ - left, top, right, bottom = _border(border) - width = left + image.size[0] + right - height = top + image.size[1] + bottom - color = _color(fill, image.mode) - if image.palette: - mode = image.palette.mode - palette = ImagePalette.ImagePalette(mode, image.getpalette(mode)) - if isinstance(color, tuple) and (len(color) == 3 or len(color) == 4): - color = palette.getcolor(color) - else: - palette = None - out = Image.new(image.mode, (width, height), color) - if palette: - out.putpalette(palette.palette, mode) - out.paste(image, (left, top)) - return out - - -def fit( - image: Image.Image, - size: tuple[int, int], - method: int = Image.Resampling.BICUBIC, - bleed: float = 0.0, - centering: tuple[float, float] = (0.5, 0.5), -) -> Image.Image: - """ - Returns a resized and cropped version of the image, cropped to the - requested aspect ratio and size. - - This function was contributed by Kevin Cazabon. - - :param image: The image to resize and crop. - :param size: The requested output size in pixels, given as a - (width, height) tuple. - :param method: Resampling method to use. Default is - :py:attr:`~PIL.Image.Resampling.BICUBIC`. - See :ref:`concept-filters`. - :param bleed: Remove a border around the outside of the image from all - four edges. The value is a decimal percentage (use 0.01 for - one percent). The default value is 0 (no border). - Cannot be greater than or equal to 0.5. - :param centering: Control the cropping position. Use (0.5, 0.5) for - center cropping (e.g. if cropping the width, take 50% off - of the left side, and therefore 50% off the right side). - (0.0, 0.0) will crop from the top left corner (i.e. if - cropping the width, take all of the crop off of the right - side, and if cropping the height, take all of it off the - bottom). (1.0, 0.0) will crop from the bottom left - corner, etc. (i.e. if cropping the width, take all of the - crop off the left side, and if cropping the height take - none from the top, and therefore all off the bottom). - :return: An image. - """ - - # by Kevin Cazabon, Feb 17/2000 - # kevin@cazabon.com - # https://www.cazabon.com - - centering_x, centering_y = centering - - if not 0.0 <= centering_x <= 1.0: - centering_x = 0.5 - if not 0.0 <= centering_y <= 1.0: - centering_y = 0.5 - - if not 0.0 <= bleed < 0.5: - bleed = 0.0 - - # calculate the area to use for resizing and cropping, subtracting - # the 'bleed' around the edges - - # number of pixels to trim off on Top and Bottom, Left and Right - bleed_pixels = (bleed * image.size[0], bleed * image.size[1]) - - live_size = ( - image.size[0] - bleed_pixels[0] * 2, - image.size[1] - bleed_pixels[1] * 2, - ) - - # calculate the aspect ratio of the live_size - live_size_ratio = live_size[0] / live_size[1] - - # calculate the aspect ratio of the output image - output_ratio = size[0] / size[1] - - # figure out if the sides or top/bottom will be cropped off - if live_size_ratio == output_ratio: - # live_size is already the needed ratio - crop_width = live_size[0] - crop_height = live_size[1] - elif live_size_ratio >= output_ratio: - # live_size is wider than what's needed, crop the sides - crop_width = output_ratio * live_size[1] - crop_height = live_size[1] - else: - # live_size is taller than what's needed, crop the top and bottom - crop_width = live_size[0] - crop_height = live_size[0] / output_ratio - - # make the crop - crop_left = bleed_pixels[0] + (live_size[0] - crop_width) * centering_x - crop_top = bleed_pixels[1] + (live_size[1] - crop_height) * centering_y - - crop = (crop_left, crop_top, crop_left + crop_width, crop_top + crop_height) - - # resize the image and return it - return image.resize(size, method, box=crop) - - -def flip(image: Image.Image) -> Image.Image: - """ - Flip the image vertically (top to bottom). - - :param image: The image to flip. - :return: An image. - """ - return image.transpose(Image.Transpose.FLIP_TOP_BOTTOM) - - -def grayscale(image: Image.Image) -> Image.Image: - """ - Convert the image to grayscale. - - :param image: The image to convert. - :return: An image. - """ - return image.convert("L") - - -def invert(image: Image.Image) -> Image.Image: - """ - Invert (negate) the image. - - :param image: The image to invert. - :return: An image. - """ - lut = list(range(255, -1, -1)) - return image.point(lut) if image.mode == "1" else _lut(image, lut) - - -def mirror(image: Image.Image) -> Image.Image: - """ - Flip image horizontally (left to right). - - :param image: The image to mirror. - :return: An image. - """ - return image.transpose(Image.Transpose.FLIP_LEFT_RIGHT) - - -def posterize(image: Image.Image, bits: int) -> Image.Image: - """ - Reduce the number of bits for each color channel. - - :param image: The image to posterize. - :param bits: The number of bits to keep for each channel (1-8). - :return: An image. - """ - mask = ~(2 ** (8 - bits) - 1) - lut = [i & mask for i in range(256)] - return _lut(image, lut) - - -def solarize(image: Image.Image, threshold: int = 128) -> Image.Image: - """ - Invert all pixel values above a threshold. - - :param image: The image to solarize. - :param threshold: All pixels above this grayscale level are inverted. - :return: An image. - """ - lut = [] - for i in range(256): - if i < threshold: - lut.append(i) - else: - lut.append(255 - i) - return _lut(image, lut) - - -@overload -def exif_transpose(image: Image.Image, *, in_place: Literal[True]) -> None: ... - - -@overload -def exif_transpose( - image: Image.Image, *, in_place: Literal[False] = False -) -> Image.Image: ... - - -def exif_transpose(image: Image.Image, *, in_place: bool = False) -> Image.Image | None: - """ - If an image has an EXIF Orientation tag, other than 1, transpose the image - accordingly, and remove the orientation data. - - :param image: The image to transpose. - :param in_place: Boolean. Keyword-only argument. - If ``True``, the original image is modified in-place, and ``None`` is returned. - If ``False`` (default), a new :py:class:`~PIL.Image.Image` object is returned - with the transposition applied. If there is no transposition, a copy of the - image will be returned. - """ - image.load() - image_exif = image.getexif() - orientation = image_exif.get(ExifTags.Base.Orientation, 1) - method = { - 2: Image.Transpose.FLIP_LEFT_RIGHT, - 3: Image.Transpose.ROTATE_180, - 4: Image.Transpose.FLIP_TOP_BOTTOM, - 5: Image.Transpose.TRANSPOSE, - 6: Image.Transpose.ROTATE_270, - 7: Image.Transpose.TRANSVERSE, - 8: Image.Transpose.ROTATE_90, - }.get(orientation) - if method is not None: - if in_place: - image.im = image.im.transpose(method) - image._size = image.im.size - else: - transposed_image = image.transpose(method) - exif_image = image if in_place else transposed_image - - exif = exif_image.getexif() - if ExifTags.Base.Orientation in exif: - del exif[ExifTags.Base.Orientation] - if "exif" in exif_image.info: - exif_image.info["exif"] = exif.tobytes() - elif "Raw profile type exif" in exif_image.info: - exif_image.info["Raw profile type exif"] = exif.tobytes().hex() - for key in ("XML:com.adobe.xmp", "xmp"): - if key in exif_image.info: - for pattern in ( - r'tiff:Orientation="([0-9])"', - r"([0-9])", - ): - value = exif_image.info[key] - if isinstance(value, str): - value = re.sub(pattern, "", value) - elif isinstance(value, tuple): - value = tuple( - re.sub(pattern.encode(), b"", v) for v in value - ) - else: - value = re.sub(pattern.encode(), b"", value) - exif_image.info[key] = value - if not in_place: - return transposed_image - elif not in_place: - return image.copy() - return None diff --git a/.venv/lib/python3.12/site-packages/PIL/ImagePalette.py b/.venv/lib/python3.12/site-packages/PIL/ImagePalette.py deleted file mode 100644 index 2abbd46e..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImagePalette.py +++ /dev/null @@ -1,290 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# image palette object -# -# History: -# 1996-03-11 fl Rewritten. -# 1997-01-03 fl Up and running. -# 1997-08-23 fl Added load hack -# 2001-04-16 fl Fixed randint shadow bug in random() -# -# Copyright (c) 1997-2001 by Secret Labs AB -# Copyright (c) 1996-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import array -from collections.abc import Sequence -from typing import IO - -from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile - -TYPE_CHECKING = False -if TYPE_CHECKING: - from . import Image - - -class ImagePalette: - """ - Color palette for palette mapped images - - :param mode: The mode to use for the palette. See: - :ref:`concept-modes`. Defaults to "RGB" - :param palette: An optional palette. If given, it must be a bytearray, - an array or a list of ints between 0-255. The list must consist of - all channels for one color followed by the next color (e.g. RGBRGBRGB). - Defaults to an empty palette. - """ - - def __init__( - self, - mode: str = "RGB", - palette: Sequence[int] | bytes | bytearray | None = None, - ) -> None: - self.mode = mode - self.rawmode: str | None = None # if set, palette contains raw data - self.palette = palette or bytearray() - self.dirty: int | None = None - - @property - def palette(self) -> Sequence[int] | bytes | bytearray: - return self._palette - - @palette.setter - def palette(self, palette: Sequence[int] | bytes | bytearray) -> None: - self._colors: dict[tuple[int, ...], int] | None = None - self._palette = palette - - @property - def colors(self) -> dict[tuple[int, ...], int]: - if self._colors is None: - mode_len = len(self.mode) - self._colors = {} - for i in range(0, len(self.palette), mode_len): - color = tuple(self.palette[i : i + mode_len]) - if color in self._colors: - continue - self._colors[color] = i // mode_len - return self._colors - - @colors.setter - def colors(self, colors: dict[tuple[int, ...], int]) -> None: - self._colors = colors - - def copy(self) -> ImagePalette: - new = ImagePalette() - - new.mode = self.mode - new.rawmode = self.rawmode - if self.palette is not None: - new.palette = self.palette[:] - new.dirty = self.dirty - - return new - - def getdata(self) -> tuple[str, Sequence[int] | bytes | bytearray]: - """ - Get palette contents in format suitable for the low-level - ``im.putpalette`` primitive. - - .. warning:: This method is experimental. - """ - if self.rawmode: - return self.rawmode, self.palette - return self.mode, self.tobytes() - - def tobytes(self) -> bytes: - """Convert palette to bytes. - - .. warning:: This method is experimental. - """ - if self.rawmode: - msg = "palette contains raw palette data" - raise ValueError(msg) - if isinstance(self.palette, bytes): - return self.palette - arr = array.array("B", self.palette) - return arr.tobytes() - - # Declare tostring as an alias for tobytes - tostring = tobytes - - def _new_color_index( - self, image: Image.Image | None = None, e: Exception | None = None - ) -> int: - if not isinstance(self.palette, bytearray): - self._palette = bytearray(self.palette) - index = len(self.palette) // len(self.mode) - special_colors: tuple[int | tuple[int, ...] | None, ...] = () - if image: - special_colors = ( - image.info.get("background"), - image.info.get("transparency"), - ) - while index in special_colors: - index += 1 - if index >= 256: - if image: - # Search for an unused index - for i, count in reversed(list(enumerate(image.histogram()))): - if count == 0 and i not in special_colors: - index = i - break - if index >= 256: - msg = "cannot allocate more than 256 colors" - raise ValueError(msg) from e - return index - - def getcolor( - self, - color: tuple[int, ...], - image: Image.Image | None = None, - ) -> int: - """Given an rgb tuple, allocate palette entry. - - .. warning:: This method is experimental. - """ - if self.rawmode: - msg = "palette contains raw palette data" - raise ValueError(msg) - if isinstance(color, tuple): - if self.mode == "RGB": - if len(color) == 4: - if color[3] != 255: - msg = "cannot add non-opaque RGBA color to RGB palette" - raise ValueError(msg) - color = color[:3] - elif self.mode == "RGBA": - if len(color) == 3: - color += (255,) - try: - return self.colors[color] - except KeyError as e: - # allocate new color slot - index = self._new_color_index(image, e) - assert isinstance(self._palette, bytearray) - self.colors[color] = index - mode_len = len(self.mode) - if index * mode_len < len(self.palette): - self._palette = ( - self._palette[: index * mode_len] - + bytes(color) - + self._palette[index * mode_len + mode_len :] - ) - else: - self._palette += bytes(color) - self.dirty = 1 - return index - else: - msg = f"unknown color specifier: {repr(color)}" # type: ignore[unreachable] - raise ValueError(msg) - - def save(self, fp: str | IO[str]) -> None: - """Save palette to text file. - - .. warning:: This method is experimental. - """ - if self.rawmode: - msg = "palette contains raw palette data" - raise ValueError(msg) - open_fp = False - if isinstance(fp, str): - fp = open(fp, "w") - open_fp = True - try: - fp.write("# Palette\n") - fp.write(f"# Mode: {self.mode}\n") - palette_len = len(self.palette) - for i in range(256): - fp.write(f"{i}") - for j in range(i * len(self.mode), (i + 1) * len(self.mode)): - fp.write(f" {self.palette[j] if j < palette_len else 0}") - fp.write("\n") - finally: - if open_fp: - fp.close() - - -# -------------------------------------------------------------------- -# Internal - - -def raw(rawmode: str, data: Sequence[int] | bytes | bytearray) -> ImagePalette: - palette = ImagePalette() - palette.rawmode = rawmode - palette.palette = data - palette.dirty = 1 - return palette - - -# -------------------------------------------------------------------- -# Factories - - -def make_linear_lut(black: int, white: float) -> list[int]: - if black == 0: - return [int(white * i // 255) for i in range(256)] - - msg = "unavailable when black is non-zero" - raise NotImplementedError(msg) # FIXME - - -def make_gamma_lut(exp: float) -> list[int]: - return [int(((i / 255.0) ** exp) * 255.0 + 0.5) for i in range(256)] - - -def negative(mode: str = "RGB") -> ImagePalette: - palette = list(range(256 * len(mode))) - palette.reverse() - return ImagePalette(mode, [i // len(mode) for i in palette]) - - -def random(mode: str = "RGB") -> ImagePalette: - from random import randint - - palette = [randint(0, 255) for _ in range(256 * len(mode))] - return ImagePalette(mode, palette) - - -def sepia(white: str = "#fff0c0") -> ImagePalette: - bands = [make_linear_lut(0, band) for band in ImageColor.getrgb(white)] - return ImagePalette("RGB", [bands[i % 3][i // 3] for i in range(256 * 3)]) - - -def wedge(mode: str = "RGB") -> ImagePalette: - palette = list(range(256 * len(mode))) - return ImagePalette(mode, [i // len(mode) for i in palette]) - - -def load(filename: str) -> tuple[bytes, str]: - # FIXME: supports GIMP gradients only - - with open(filename, "rb") as fp: - paletteHandlers: list[ - type[ - GimpPaletteFile.GimpPaletteFile - | GimpGradientFile.GimpGradientFile - | PaletteFile.PaletteFile - ] - ] = [ - GimpPaletteFile.GimpPaletteFile, - GimpGradientFile.GimpGradientFile, - PaletteFile.PaletteFile, - ] - for paletteHandler in paletteHandlers: - try: - fp.seek(0) - lut = paletteHandler(fp).getpalette() - if lut: - break - except (SyntaxError, ValueError): - pass - else: - msg = "cannot load palette" - raise OSError(msg) - - return lut # data, rawmode diff --git a/.venv/lib/python3.12/site-packages/PIL/ImagePath.py b/.venv/lib/python3.12/site-packages/PIL/ImagePath.py deleted file mode 100644 index 77e8a609..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImagePath.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# path interface -# -# History: -# 1996-11-04 fl Created -# 2002-04-14 fl Added documentation stub class -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image - -Path = Image.core.path diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageQt.py b/.venv/lib/python3.12/site-packages/PIL/ImageQt.py deleted file mode 100644 index af4d0742..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageQt.py +++ /dev/null @@ -1,219 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a simple Qt image interface. -# -# history: -# 2006-06-03 fl: created -# 2006-06-04 fl: inherit from QImage instead of wrapping it -# 2006-06-05 fl: removed toimage helper; move string support to ImageQt -# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com) -# -# Copyright (c) 2006 by Secret Labs AB -# Copyright (c) 2006 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import sys -from io import BytesIO - -from . import Image -from ._util import is_path - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from typing import Any - - from . import ImageFile - - QBuffer: type - -qt_version: str | None -qt_versions = [ - ["6", "PyQt6"], - ["side6", "PySide6"], -] - -# If a version has already been imported, attempt it first -qt_versions.sort(key=lambda version: version[1] in sys.modules, reverse=True) -for version, qt_module in qt_versions: - try: - qRgba: Callable[[int, int, int, int], int] - if qt_module == "PyQt6": - from PyQt6.QtCore import QBuffer, QByteArray, QIODevice - from PyQt6.QtGui import QImage, QPixmap, qRgba - elif qt_module == "PySide6": - from PySide6.QtCore import ( # type: ignore[assignment] - QBuffer, - QByteArray, - QIODevice, - ) - from PySide6.QtGui import QImage, QPixmap, qRgba # type: ignore[assignment] - except (ImportError, RuntimeError): - continue - qt_is_installed = True - qt_version = version - break -else: - qt_is_installed = False - qt_version = None - - -def rgb(r: int, g: int, b: int, a: int = 255) -> int: - """(Internal) Turns an RGB color into a Qt compatible color integer.""" - # use qRgb to pack the colors, and then turn the resulting long - # into a negative integer with the same bitpattern. - return qRgba(r, g, b, a) & 0xFFFFFFFF - - -def fromqimage(im: QImage | QPixmap) -> ImageFile.ImageFile: - """ - :param im: QImage or PIL ImageQt object - """ - buffer = QBuffer() - qt_openmode: object - if qt_version == "6": - try: - qt_openmode = getattr(QIODevice, "OpenModeFlag") - except AttributeError: - qt_openmode = getattr(QIODevice, "OpenMode") - else: - qt_openmode = QIODevice - buffer.open(getattr(qt_openmode, "ReadWrite")) - # preserve alpha channel with png - # otherwise ppm is more friendly with Image.open - if im.hasAlphaChannel(): - im.save(buffer, "png") - else: - im.save(buffer, "ppm") - - b = BytesIO() - b.write(buffer.data()) - buffer.close() - b.seek(0) - - return Image.open(b) - - -def fromqpixmap(im: QPixmap) -> ImageFile.ImageFile: - return fromqimage(im) - - -def align8to32(bytes: bytes, width: int, mode: str) -> bytes: - """ - converts each scanline of data from 8 bit to 32 bit aligned - """ - - bits_per_pixel = {"1": 1, "L": 8, "P": 8, "I;16": 16}[mode] - - # calculate bytes per line and the extra padding if needed - bits_per_line = bits_per_pixel * width - full_bytes_per_line, remaining_bits_per_line = divmod(bits_per_line, 8) - bytes_per_line = full_bytes_per_line + (1 if remaining_bits_per_line else 0) - - extra_padding = -bytes_per_line % 4 - - # already 32 bit aligned by luck - if not extra_padding: - return bytes - - new_data = [ - bytes[i * bytes_per_line : (i + 1) * bytes_per_line] + b"\x00" * extra_padding - for i in range(len(bytes) // bytes_per_line) - ] - - return b"".join(new_data) - - -def _toqclass_helper(im: Image.Image | str | QByteArray) -> dict[str, Any]: - data = None - colortable = None - exclusive_fp = False - - # handle filename, if given instead of image name - if hasattr(im, "toUtf8"): - # FIXME - is this really the best way to do this? - im = str(im.toUtf8(), "utf-8") - if is_path(im): - im = Image.open(im) - exclusive_fp = True - assert isinstance(im, Image.Image) - - qt_format = getattr(QImage, "Format") if qt_version == "6" else QImage - if im.mode == "1": - format = getattr(qt_format, "Format_Mono") - elif im.mode == "L": - format = getattr(qt_format, "Format_Indexed8") - colortable = [rgb(i, i, i) for i in range(256)] - elif im.mode == "P": - format = getattr(qt_format, "Format_Indexed8") - palette = im.getpalette() - assert palette is not None - colortable = [rgb(*palette[i : i + 3]) for i in range(0, len(palette), 3)] - elif im.mode == "RGB": - # Populate the 4th channel with 255 - im = im.convert("RGBA") - - data = im.tobytes("raw", "BGRA") - format = getattr(qt_format, "Format_RGB32") - elif im.mode == "RGBA": - data = im.tobytes("raw", "BGRA") - format = getattr(qt_format, "Format_ARGB32") - elif im.mode == "I;16": - im = im.point(lambda i: i * 256) - - format = getattr(qt_format, "Format_Grayscale16") - else: - if exclusive_fp: - im.close() - msg = f"unsupported image mode {repr(im.mode)}" - raise ValueError(msg) - - size = im.size - __data = data or align8to32(im.tobytes(), size[0], im.mode) - if exclusive_fp: - im.close() - return {"data": __data, "size": size, "format": format, "colortable": colortable} - - -if qt_is_installed: - - class ImageQt(QImage): - def __init__(self, im: Image.Image | str | QByteArray) -> None: - """ - An PIL image wrapper for Qt. This is a subclass of PyQt's QImage - class. - - :param im: A PIL Image object, or a file name (given either as - Python string or a PyQt string object). - """ - im_data = _toqclass_helper(im) - # must keep a reference, or Qt will crash! - # All QImage constructors that take data operate on an existing - # buffer, so this buffer has to hang on for the life of the image. - # Fixes https://github.com/python-pillow/Pillow/issues/1370 - self.__data = im_data["data"] - super().__init__( - self.__data, - im_data["size"][0], - im_data["size"][1], - im_data["format"], - ) - if im_data["colortable"]: - self.setColorTable(im_data["colortable"]) - - -def toqimage(im: Image.Image | str | QByteArray) -> ImageQt: - return ImageQt(im) - - -def toqpixmap(im: Image.Image | str | QByteArray) -> QPixmap: - qimage = toqimage(im) - pixmap = getattr(QPixmap, "fromImage")(qimage) - if qt_version == "6": - pixmap.detach() - return pixmap diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageSequence.py b/.venv/lib/python3.12/site-packages/PIL/ImageSequence.py deleted file mode 100644 index 361be489..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageSequence.py +++ /dev/null @@ -1,88 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# sequence support classes -# -# history: -# 1997-02-20 fl Created -# -# Copyright (c) 1997 by Secret Labs AB. -# Copyright (c) 1997 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -## -from __future__ import annotations - -from . import Image - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - - -class Iterator: - """ - This class implements an iterator object that can be used to loop - over an image sequence. - - You can use the ``[]`` operator to access elements by index. This operator - will raise an :py:exc:`IndexError` if you try to access a nonexistent - frame. - - :param im: An image object. - """ - - def __init__(self, im: Image.Image) -> None: - if not hasattr(im, "seek"): - msg = "im must have seek method" - raise AttributeError(msg) - self.im = im - self.position = getattr(self.im, "_min_frame", 0) - - def __getitem__(self, ix: int) -> Image.Image: - try: - self.im.seek(ix) - return self.im - except EOFError as e: - msg = "end of sequence" - raise IndexError(msg) from e - - def __iter__(self) -> Iterator: - return self - - def __next__(self) -> Image.Image: - try: - self.im.seek(self.position) - self.position += 1 - return self.im - except EOFError as e: - msg = "end of sequence" - raise StopIteration(msg) from e - - -def all_frames( - im: Image.Image | list[Image.Image], - func: Callable[[Image.Image], Image.Image] | None = None, -) -> list[Image.Image]: - """ - Applies a given function to all frames in an image or a list of images. - The frames are returned as a list of separate images. - - :param im: An image, or a list of images. - :param func: The function to apply to all of the image frames. - :returns: A list of images. - """ - if not isinstance(im, list): - im = [im] - - ims = [] - for imSequence in im: - current = imSequence.tell() - - ims += [im_frame.copy() for im_frame in Iterator(imSequence)] - - imSequence.seek(current) - return [func(im) for im in ims] if func else ims diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageShow.py b/.venv/lib/python3.12/site-packages/PIL/ImageShow.py deleted file mode 100644 index 7705608e..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageShow.py +++ /dev/null @@ -1,362 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# im.show() drivers -# -# History: -# 2008-04-06 fl Created -# -# Copyright (c) Secret Labs AB 2008. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import abc -import os -import shutil -import subprocess -import sys -from shlex import quote -from typing import Any - -from . import Image - -_viewers = [] - - -def register(viewer: type[Viewer] | Viewer, order: int = 1) -> None: - """ - The :py:func:`register` function is used to register additional viewers:: - - from PIL import ImageShow - ImageShow.register(MyViewer()) # MyViewer will be used as a last resort - ImageShow.register(MySecondViewer(), 0) # MySecondViewer will be prioritised - ImageShow.register(ImageShow.XVViewer(), 0) # XVViewer will be prioritised - - :param viewer: The viewer to be registered. - :param order: - Zero or a negative integer to prepend this viewer to the list, - a positive integer to append it. - """ - if isinstance(viewer, type) and issubclass(viewer, Viewer): - viewer = viewer() - if order > 0: - _viewers.append(viewer) - else: - _viewers.insert(0, viewer) - - -def show(image: Image.Image, title: str | None = None, **options: Any) -> bool: - r""" - Display a given image. - - :param image: An image object. - :param title: Optional title. Not all viewers can display the title. - :param \**options: Additional viewer options. - :returns: ``True`` if a suitable viewer was found, ``False`` otherwise. - """ - for viewer in _viewers: - if viewer.show(image, title=title, **options): - return True - return False - - -class Viewer: - """Base class for viewers.""" - - # main api - - def show(self, image: Image.Image, **options: Any) -> int: - """ - The main function for displaying an image. - Converts the given image to the target format and displays it. - """ - - if not ( - image.mode in ("1", "RGBA") - or (self.format == "PNG" and image.mode in ("I;16", "LA")) - ): - base = Image.getmodebase(image.mode) - if image.mode != base: - image = image.convert(base) - - return self.show_image(image, **options) - - # hook methods - - format: str | None = None - """The format to convert the image into.""" - options: dict[str, Any] = {} - """Additional options used to convert the image.""" - - def get_format(self, image: Image.Image) -> str | None: - """Return format name, or ``None`` to save as PGM/PPM.""" - return self.format - - def get_command(self, file: str, **options: Any) -> str: - """ - Returns the command used to display the file. - Not implemented in the base class. - """ - msg = "unavailable in base viewer" - raise NotImplementedError(msg) - - def save_image(self, image: Image.Image) -> str: - """Save to temporary file and return filename.""" - return image._dump(format=self.get_format(image), **self.options) - - def show_image(self, image: Image.Image, **options: Any) -> int: - """Display the given image.""" - return self.show_file(self.save_image(image), **options) - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - os.system(self.get_command(path, **options)) # nosec - return 1 - - -# -------------------------------------------------------------------- - - -class WindowsViewer(Viewer): - """The default viewer on Windows is the default system application for PNG files.""" - - format = "PNG" - options = {"compress_level": 1, "save_all": True} - - def get_command(self, file: str, **options: Any) -> str: - return ( - f'start "Pillow" /WAIT "{file}" ' - "&& ping -n 4 127.0.0.1 >NUL " - f'&& del /f "{file}"' - ) - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - subprocess.Popen( - self.get_command(path, **options), - shell=True, - creationflags=getattr(subprocess, "CREATE_NO_WINDOW"), - ) # nosec - return 1 - - -if sys.platform == "win32": - register(WindowsViewer) - - -class MacViewer(Viewer): - """The default viewer on macOS using ``Preview.app``.""" - - format = "PNG" - options = {"compress_level": 1, "save_all": True} - - def get_command(self, file: str, **options: Any) -> str: - # on darwin open returns immediately resulting in the temp - # file removal while app is opening - command = "open -a Preview.app" - command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&" - return command - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - subprocess.call(["open", "-a", "Preview.app", path]) - - pyinstaller = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS") - executable = (not pyinstaller and sys.executable) or shutil.which("python3") - if executable: - subprocess.Popen( - [ - executable, - "-c", - "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", - path, - ] - ) - return 1 - - -if sys.platform == "darwin": - register(MacViewer) - - -class UnixViewer(abc.ABC, Viewer): - format = "PNG" - options = {"compress_level": 1, "save_all": True} - - @abc.abstractmethod - def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: - pass - - def get_command(self, file: str, **options: Any) -> str: - command = self.get_command_ex(file, **options)[0] - return f"{command} {quote(file)}" - - -class XDGViewer(UnixViewer): - """ - The freedesktop.org ``xdg-open`` command. - """ - - def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: - command = executable = "xdg-open" - return command, executable - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - subprocess.Popen(["xdg-open", path]) - return 1 - - -class DisplayViewer(UnixViewer): - """ - The ImageMagick ``display`` command. - This viewer supports the ``title`` parameter. - """ - - def get_command_ex( - self, file: str, title: str | None = None, **options: Any - ) -> tuple[str, str]: - command = executable = "display" - if title: - command += f" -title {quote(title)}" - return command, executable - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - args = ["display"] - title = options.get("title") - if title: - args += ["-title", title] - args.append(path) - - subprocess.Popen(args) - return 1 - - -class GmDisplayViewer(UnixViewer): - """The GraphicsMagick ``gm display`` command.""" - - def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: - executable = "gm" - command = "gm display" - return command, executable - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - subprocess.Popen(["gm", "display", path]) - return 1 - - -class EogViewer(UnixViewer): - """The GNOME Image Viewer ``eog`` command.""" - - def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: - executable = "eog" - command = "eog -n" - return command, executable - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - subprocess.Popen(["eog", "-n", path]) - return 1 - - -class XVViewer(UnixViewer): - """ - The X Viewer ``xv`` command. - This viewer supports the ``title`` parameter. - """ - - def get_command_ex( - self, file: str, title: str | None = None, **options: Any - ) -> tuple[str, str]: - # note: xv is pretty outdated. most modern systems have - # imagemagick's display command instead. - command = executable = "xv" - if title: - command += f" -name {quote(title)}" - return command, executable - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - args = ["xv"] - title = options.get("title") - if title: - args += ["-name", title] - args.append(path) - - subprocess.Popen(args) - return 1 - - -if sys.platform not in ("win32", "darwin"): # unixoids - if shutil.which("xdg-open"): - register(XDGViewer) - if shutil.which("display"): - register(DisplayViewer) - if shutil.which("gm"): - register(GmDisplayViewer) - if shutil.which("eog"): - register(EogViewer) - if shutil.which("xv"): - register(XVViewer) - - -class IPythonViewer(Viewer): - """The viewer for IPython frontends.""" - - def show_image(self, image: Image.Image, **options: Any) -> int: - ipython_display(image) - return 1 - - -try: - from IPython.display import display as ipython_display -except ImportError: - pass -else: - register(IPythonViewer) - - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Syntax: python3 ImageShow.py imagefile [title]") - sys.exit() - - with Image.open(sys.argv[1]) as im: - print(show(im, *sys.argv[2:])) diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageStat.py b/.venv/lib/python3.12/site-packages/PIL/ImageStat.py deleted file mode 100644 index 3a1044ba..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageStat.py +++ /dev/null @@ -1,167 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# global image statistics -# -# History: -# 1996-04-05 fl Created -# 1997-05-21 fl Added mask; added rms, var, stddev attributes -# 1997-08-05 fl Added median -# 1998-07-05 hk Fixed integer overflow error -# -# Notes: -# This class shows how to implement delayed evaluation of attributes. -# To get a certain value, simply access the corresponding attribute. -# The __getattr__ dispatcher takes care of the rest. -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996-97. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import math -from functools import cached_property - -from . import Image - - -class Stat: - def __init__( - self, image_or_list: Image.Image | list[int], mask: Image.Image | None = None - ) -> None: - """ - Calculate statistics for the given image. If a mask is included, - only the regions covered by that mask are included in the - statistics. You can also pass in a previously calculated histogram. - - :param image: A PIL image, or a precalculated histogram. - - .. note:: - - For a PIL image, calculations rely on the - :py:meth:`~PIL.Image.Image.histogram` method. The pixel counts are - grouped into 256 bins, even if the image has more than 8 bits per - channel. So ``I`` and ``F`` mode images have a maximum ``mean``, - ``median`` and ``rms`` of 255, and cannot have an ``extrema`` maximum - of more than 255. - - :param mask: An optional mask. - """ - if isinstance(image_or_list, Image.Image): - self.h = image_or_list.histogram(mask) - elif isinstance(image_or_list, list): - self.h = image_or_list - else: - msg = "first argument must be image or list" # type: ignore[unreachable] - raise TypeError(msg) - self.bands = list(range(len(self.h) // 256)) - - @cached_property - def extrema(self) -> list[tuple[int, int]]: - """ - Min/max values for each band in the image. - - .. note:: - This relies on the :py:meth:`~PIL.Image.Image.histogram` method, and - simply returns the low and high bins used. This is correct for - images with 8 bits per channel, but fails for other modes such as - ``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.Image.getextrema` to - return per-band extrema for the image. This is more correct and - efficient because, for non-8-bit modes, the histogram method uses - :py:meth:`~PIL.Image.Image.getextrema` to determine the bins used. - """ - - def minmax(histogram: list[int]) -> tuple[int, int]: - res_min, res_max = 255, 0 - for i in range(256): - if histogram[i]: - res_min = i - break - for i in range(255, -1, -1): - if histogram[i]: - res_max = i - break - return res_min, res_max - - return [minmax(self.h[i:]) for i in range(0, len(self.h), 256)] - - @cached_property - def count(self) -> list[int]: - """Total number of pixels for each band in the image.""" - return [sum(self.h[i : i + 256]) for i in range(0, len(self.h), 256)] - - @cached_property - def sum(self) -> list[float]: - """Sum of all pixels for each band in the image.""" - - v = [] - for i in range(0, len(self.h), 256): - layer_sum = 0.0 - for j in range(256): - layer_sum += j * self.h[i + j] - v.append(layer_sum) - return v - - @cached_property - def sum2(self) -> list[float]: - """Squared sum of all pixels for each band in the image.""" - - v = [] - for i in range(0, len(self.h), 256): - sum2 = 0.0 - for j in range(256): - sum2 += (j**2) * float(self.h[i + j]) - v.append(sum2) - return v - - @cached_property - def mean(self) -> list[float]: - """Average (arithmetic mean) pixel level for each band in the image.""" - return [self.sum[i] / self.count[i] if self.count[i] else 0 for i in self.bands] - - @cached_property - def median(self) -> list[int]: - """Median pixel level for each band in the image.""" - - v = [] - for i in self.bands: - s = 0 - half = self.count[i] // 2 - b = i * 256 - for j in range(256): - s = s + self.h[b + j] - if s > half: - break - v.append(j) - return v - - @cached_property - def rms(self) -> list[float]: - """RMS (root-mean-square) for each band in the image.""" - return [ - math.sqrt(self.sum2[i] / self.count[i]) if self.count[i] else 0 - for i in self.bands - ] - - @cached_property - def var(self) -> list[float]: - """Variance for each band in the image.""" - return [ - ( - (self.sum2[i] - (self.sum[i] ** 2.0) / self.count[i]) / self.count[i] - if self.count[i] - else 0 - ) - for i in self.bands - ] - - @cached_property - def stddev(self) -> list[float]: - """Standard deviation for each band in the image.""" - return [math.sqrt(self.var[i]) for i in self.bands] - - -Global = Stat # compatibility diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageText.py b/.venv/lib/python3.12/site-packages/PIL/ImageText.py deleted file mode 100644 index 008d20d3..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageText.py +++ /dev/null @@ -1,508 +0,0 @@ -from __future__ import annotations - -import math -import re -from typing import AnyStr, Generic, NamedTuple - -from . import ImageFont -from ._typing import _Ink - -Font = ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont - - -class _Line(NamedTuple): - x: float - y: float - anchor: str - text: str | bytes - - -class _Wrap(Generic[AnyStr]): - lines: list[AnyStr] = [] - position = 0 - offset = 0 - - def __init__( - self, - text: Text[AnyStr], - width: int, - height: int | None = None, - font: Font | None = None, - ) -> None: - self.text: Text[AnyStr] = text - self.width = width - self.height = height - self.font = font - - input_text = self.text.text - emptystring = "" if isinstance(input_text, str) else b"" - line = emptystring - - for word in re.findall( - r"\s*\S+" if isinstance(input_text, str) else rb"\s*\S+", input_text - ): - newlines = re.findall( - r"[^\S\n]*\n" if isinstance(input_text, str) else rb"[^\S\n]*\n", word - ) - if newlines: - if not self.add_line(line): - break - for i, line in enumerate(newlines): - if i != 0 and not self.add_line(emptystring): - break - self.position += len(line) - word = word[len(line) :] - line = emptystring - - new_line = line + word - if self.text._get_bbox(new_line, self.font)[2] <= width: - # This word fits on the line - line = new_line - continue - - # This word does not fit on the line - if line and not self.add_line(line): - break - - original_length = len(word) - word = word.lstrip() - self.offset = original_length - len(word) - - if self.text._get_bbox(word, self.font)[2] > width: - if font is None: - msg = "Word does not fit within line" - raise ValueError(msg) - break - line = word - else: - if line: - self.add_line(line) - self.remaining_text: AnyStr = input_text[self.position :] - - def add_line(self, line: AnyStr) -> bool: - lines = self.lines + [line] - if self.height is not None: - last_line_y = self.text._split(lines=lines)[-1].y - last_line_height = self.text._get_bbox(line, self.font)[3] - if last_line_y + last_line_height > self.height: - return False - - self.lines = lines - self.position += len(line) + self.offset - self.offset = 0 - return True - - -class Text(Generic[AnyStr]): - def __init__( - self, - text: AnyStr, - font: Font | None = None, - mode: str = "RGB", - spacing: float = 4, - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - ) -> None: - """ - :param text: String to be drawn. - :param font: Either an :py:class:`~PIL.ImageFont.ImageFont` instance, - :py:class:`~PIL.ImageFont.FreeTypeFont` instance, - :py:class:`~PIL.ImageFont.TransposedFont` instance or ``None``. If - ``None``, the default font from :py:meth:`.ImageFont.load_default` - will be used. - :param mode: The image mode this will be used with. - :param spacing: The number of pixels between lines. - :param direction: Direction of the text. It can be ``"rtl"`` (right to left), - ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). - Requires libraqm. - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional font features - that are not enabled by default, for example ``"dlig"`` or - ``"ss01"``, but can be also used to turn off default font - features, for example ``"-liga"`` to disable ligatures or - ``"-kern"`` to disable kerning. To get all supported - features, see `OpenType docs`_. - Requires libraqm. - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code`_. - Requires libraqm. - """ - self.text: AnyStr = text - self.font = font or ImageFont.load_default() - - self.mode = mode - self.spacing = spacing - self.direction = direction - self.features = features - self.language = language - - self.embedded_color = False - - self.stroke_width: float = 0 - self.stroke_fill: _Ink | None = None - - def embed_color(self) -> None: - """ - Use embedded color glyphs (COLR, CBDT, SBIX). - """ - if self.mode not in ("RGB", "RGBA"): - msg = "Embedded color supported only in RGB and RGBA modes" - raise ValueError(msg) - self.embedded_color = True - - def stroke(self, width: float = 0, fill: _Ink | None = None) -> None: - """ - :param width: The width of the text stroke. - :param fill: Color to use for the text stroke when drawing. If not given, will - default to the ``fill`` parameter from - :py:meth:`.ImageDraw.ImageDraw.text`. - """ - self.stroke_width = width - self.stroke_fill = fill - - def _get_fontmode(self) -> str: - if self.mode in ("1", "P", "I", "F"): - return "1" - elif self.embedded_color: - return "RGBA" - else: - return "L" - - def wrap( - self, - width: int, - height: int | None = None, - scaling: str | tuple[str, int] | None = None, - ) -> Text[AnyStr] | None: - """ - Wrap text to fit within a given width. - - :param width: The width to fit within. - :param height: An optional height limit. Any text that does not fit within this - will be returned as a new :py:class:`.Text` object. - :param scaling: An optional directive to scale the text, either "grow" as much - as possible within the given dimensions, or "shrink" until it - fits. It can also be a tuple of (direction, limit), with an - integer limit to stop scaling at. - - :returns: An :py:class:`.Text` object, or None. - """ - if isinstance(self.font, ImageFont.TransposedFont): - msg = "TransposedFont not supported" - raise ValueError(msg) - if self.direction not in (None, "ltr"): - msg = "Only ltr direction supported" - raise ValueError(msg) - - if scaling is None: - wrap = _Wrap(self, width, height) - else: - if not isinstance(self.font, ImageFont.FreeTypeFont): - msg = "'scaling' only supports FreeTypeFont" - raise ValueError(msg) - if height is None: - msg = "'scaling' requires 'height'" - raise ValueError(msg) - - if isinstance(scaling, str): - limit = 1 - else: - scaling, limit = scaling - - font = self.font - wrap = _Wrap(self, width, height, font) - if scaling == "shrink": - if not wrap.remaining_text: - return None - - size = math.ceil(font.size) - while wrap.remaining_text: - if size == max(limit, 1): - msg = "Text could not be scaled" - raise ValueError(msg) - size -= 1 - font = self.font.font_variant(size=size) - wrap = _Wrap(self, width, height, font) - self.font = font - else: - if wrap.remaining_text: - msg = "Text could not be scaled" - raise ValueError(msg) - - size = math.floor(font.size) - while not wrap.remaining_text: - if size == limit: - msg = "Text could not be scaled" - raise ValueError(msg) - size += 1 - font = self.font.font_variant(size=size) - last_wrap = wrap - wrap = _Wrap(self, width, height, font) - size -= 1 - if size != self.font.size: - self.font = self.font.font_variant(size=size) - wrap = last_wrap - - if wrap.remaining_text: - text = Text( - text=wrap.remaining_text, - font=self.font, - mode=self.mode, - spacing=self.spacing, - direction=self.direction, - features=self.features, - language=self.language, - ) - text.embedded_color = self.embedded_color - text.stroke_width = self.stroke_width - text.stroke_fill = self.stroke_fill - else: - text = None - - newline = "\n" if isinstance(self.text, str) else b"\n" - self.text = newline.join(wrap.lines) - return text - - def get_length(self) -> float: - """ - Returns length (in pixels with 1/64 precision) of text. - - This is the amount by which following text should be offset. - Text bounding box may extend past the length in some fonts, - e.g. when using italics or accents. - - The result is returned as a float; it is a whole number if using basic layout. - - Note that the sum of two lengths may not equal the length of a concatenated - string due to kerning. If you need to adjust for kerning, include the following - character and subtract its length. - - For example, instead of:: - - hello = ImageText.Text("Hello", font).get_length() - world = ImageText.Text("World", font).get_length() - helloworld = ImageText.Text("HelloWorld", font).get_length() - assert hello + world == helloworld - - use:: - - hello = ( - ImageText.Text("HelloW", font).get_length() - - ImageText.Text("W", font).get_length() - ) # adjusted for kerning - world = ImageText.Text("World", font).get_length() - helloworld = ImageText.Text("HelloWorld", font).get_length() - assert hello + world == helloworld - - or disable kerning with (requires libraqm):: - - hello = ImageText.Text("Hello", font, features=["-kern"]).get_length() - world = ImageText.Text("World", font, features=["-kern"]).get_length() - helloworld = ImageText.Text( - "HelloWorld", font, features=["-kern"] - ).get_length() - assert hello + world == helloworld - - :return: Either width for horizontal text, or height for vertical text. - """ - if isinstance(self.text, str): - multiline = "\n" in self.text - else: - multiline = b"\n" in self.text - if multiline: - msg = "can't measure length of multiline text" - raise ValueError(msg) - return self.font.getlength( - self.text, - self._get_fontmode(), - self.direction, - self.features, - self.language, - ) - - def _split( - self, - xy: tuple[float, float] = (0, 0), - anchor: str | None = None, - align: str = "left", - lines: list[str] | list[bytes] | None = None, - ) -> list[_Line]: - if anchor is None: - anchor = "lt" if self.direction == "ttb" else "la" - elif len(anchor) != 2: - msg = "anchor must be a 2 character string" - raise ValueError(msg) - - if lines is None: - lines = ( - self.text.split("\n") - if isinstance(self.text, str) - else self.text.split(b"\n") - ) - if len(lines) == 1: - return [_Line(xy[0], xy[1], anchor, lines[0])] - - if anchor[1] in "tb" and self.direction != "ttb": - msg = "anchor not supported for multiline text" - raise ValueError(msg) - - fontmode = self._get_fontmode() - line_spacing = ( - self.font.getbbox( - "A", - fontmode, - None, - self.features, - self.language, - self.stroke_width, - )[3] - + self.stroke_width - + self.spacing - ) - - top = xy[1] - parts = [] - if self.direction == "ttb": - left = xy[0] - for line in lines: - parts.append(_Line(left, top, anchor, line)) - left += line_spacing - else: - widths = [] - max_width: float = 0 - for line in lines: - line_width = self.font.getlength( - line, fontmode, self.direction, self.features, self.language - ) - widths.append(line_width) - max_width = max(max_width, line_width) - - if anchor[1] == "m": - top -= (len(lines) - 1) * line_spacing / 2.0 - elif anchor[1] == "d": - top -= (len(lines) - 1) * line_spacing - - idx = -1 - for line in lines: - left = xy[0] - idx += 1 - width_difference = max_width - widths[idx] - - # align by align parameter - if align in ("left", "justify"): - pass - elif align == "center": - left += width_difference / 2.0 - elif align == "right": - left += width_difference - else: - msg = 'align must be "left", "center", "right" or "justify"' - raise ValueError(msg) - - if ( - align == "justify" - and width_difference != 0 - and idx != len(lines) - 1 - ): - words = ( - line.split(" ") if isinstance(line, str) else line.split(b" ") - ) - if len(words) > 1: - # align left by anchor - if anchor[0] == "m": - left -= max_width / 2.0 - elif anchor[0] == "r": - left -= max_width - - word_widths = [ - self.font.getlength( - word, - fontmode, - self.direction, - self.features, - self.language, - ) - for word in words - ] - word_anchor = "l" + anchor[1] - width_difference = max_width - sum(word_widths) - i = 0 - for word in words: - parts.append(_Line(left, top, word_anchor, word)) - left += word_widths[i] + width_difference / (len(words) - 1) - i += 1 - top += line_spacing - continue - - # align left by anchor - if anchor[0] == "m": - left -= width_difference / 2.0 - elif anchor[0] == "r": - left -= width_difference - parts.append(_Line(left, top, anchor, line)) - top += line_spacing - - return parts - - def _get_bbox( - self, text: str | bytes, font: Font | None = None, anchor: str | None = None - ) -> tuple[float, float, float, float]: - return (font or self.font).getbbox( - text, - self._get_fontmode(), - self.direction, - self.features, - self.language, - self.stroke_width, - anchor, - ) - - def get_bbox( - self, - xy: tuple[float, float] = (0, 0), - anchor: str | None = None, - align: str = "left", - ) -> tuple[float, float, float, float]: - """ - Returns bounding box (in pixels) of text. - - Use :py:meth:`get_length` to get the offset of following text with 1/64 pixel - precision. The bounding box includes extra margins for some fonts, e.g. italics - or accents. - - :param xy: The anchor coordinates of the text. - :param anchor: The text anchor alignment. Determines the relative location of - the anchor to the text. The default alignment is top left, - specifically ``la`` for horizontal text and ``lt`` for - vertical text. See :ref:`text-anchors` for details. - :param align: For multiline text, ``"left"``, ``"center"``, ``"right"`` or - ``"justify"`` determines the relative alignment of lines. Use the - ``anchor`` parameter to specify the alignment to ``xy``. - - :return: ``(left, top, right, bottom)`` bounding box - """ - bbox: tuple[float, float, float, float] | None = None - for x, y, anchor, text in self._split(xy, anchor, align): - bbox_line = self._get_bbox(text, anchor=anchor) - bbox_line = ( - bbox_line[0] + x, - bbox_line[1] + y, - bbox_line[2] + x, - bbox_line[3] + y, - ) - if bbox is None: - bbox = bbox_line - else: - bbox = ( - min(bbox[0], bbox_line[0]), - min(bbox[1], bbox_line[1]), - max(bbox[2], bbox_line[2]), - max(bbox[3], bbox_line[3]), - ) - - assert bbox is not None - return bbox diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageTk.py b/.venv/lib/python3.12/site-packages/PIL/ImageTk.py deleted file mode 100644 index 3a4cb81e..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageTk.py +++ /dev/null @@ -1,266 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a Tk display interface -# -# History: -# 96-04-08 fl Created -# 96-09-06 fl Added getimage method -# 96-11-01 fl Rewritten, removed image attribute and crop method -# 97-05-09 fl Use PyImagingPaste method instead of image type -# 97-05-12 fl Minor tweaks to match the IFUNC95 interface -# 97-05-17 fl Support the "pilbitmap" booster patch -# 97-06-05 fl Added file= and data= argument to image constructors -# 98-03-09 fl Added width and height methods to Image classes -# 98-07-02 fl Use default mode for "P" images without palette attribute -# 98-07-02 fl Explicitly destroy Tkinter image objects -# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch) -# 99-07-26 fl Automatically hook into Tkinter (if possible) -# 99-08-15 fl Hook uses _imagingtk instead of _imaging -# -# Copyright (c) 1997-1999 by Secret Labs AB -# Copyright (c) 1996-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import tkinter -from io import BytesIO -from typing import Any - -from . import Image, ImageFile - -TYPE_CHECKING = False -if TYPE_CHECKING: - from ._typing import CapsuleType - -# -------------------------------------------------------------------- -# Check for Tkinter interface hooks - - -def _get_image_from_kw(kw: dict[str, Any]) -> ImageFile.ImageFile | None: - source = None - if "file" in kw: - source = kw.pop("file") - elif "data" in kw: - source = BytesIO(kw.pop("data")) - if not source: - return None - return Image.open(source) - - -def _pyimagingtkcall( - command: str, photo: PhotoImage | tkinter.PhotoImage, ptr: CapsuleType -) -> None: - tk = photo.tk - try: - tk.call(command, photo, repr(ptr)) - except tkinter.TclError: - # activate Tkinter hook - # may raise an error if it cannot attach to Tkinter - from . import _imagingtk - - _imagingtk.tkinit(tk.interpaddr()) - tk.call(command, photo, repr(ptr)) - - -# -------------------------------------------------------------------- -# PhotoImage - - -class PhotoImage: - """ - A Tkinter-compatible photo image. This can be used - everywhere Tkinter expects an image object. If the image is an RGBA - image, pixels having alpha 0 are treated as transparent. - - The constructor takes either a PIL image, or a mode and a size. - Alternatively, you can use the ``file`` or ``data`` options to initialize - the photo image object. - - :param image: Either a PIL image, or a mode string. If a mode string is - used, a size must also be given. - :param size: If the first argument is a mode string, this defines the size - of the image. - :keyword file: A filename to load the image from (using - ``Image.open(file)``). - :keyword data: An 8-bit string containing image data (as loaded from an - image file). - """ - - def __init__( - self, - image: Image.Image | str | None = None, - size: tuple[int, int] | None = None, - **kw: Any, - ) -> None: - # Tk compatibility: file or data - if image is None: - image = _get_image_from_kw(kw) - - if image is None: - msg = "Image is required" - raise ValueError(msg) - elif isinstance(image, str): - mode = image - image = None - - if size is None: - msg = "If first argument is mode, size is required" - raise ValueError(msg) - else: - # got an image instead of a mode - mode = image.mode - if mode == "P": - # palette mapped data - image.apply_transparency() - image.load() - mode = image.palette.mode if image.palette else "RGB" - size = image.size - kw["width"], kw["height"] = size - - if mode not in ["1", "L", "RGB", "RGBA"]: - mode = Image.getmodebase(mode) - - self.__mode = mode - self.__size = size - self.__photo = tkinter.PhotoImage(**kw) - self.tk = self.__photo.tk - if image: - self.paste(image) - - def __del__(self) -> None: - try: - name = self.__photo.name - except AttributeError: - return - self.__photo.name = None - try: - self.__photo.tk.call("image", "delete", name) - except Exception: - pass # ignore internal errors - - def __str__(self) -> str: - """ - Get the Tkinter photo image identifier. This method is automatically - called by Tkinter whenever a PhotoImage object is passed to a Tkinter - method. - - :return: A Tkinter photo image identifier (a string). - """ - return str(self.__photo) - - def width(self) -> int: - """ - Get the width of the image. - - :return: The width, in pixels. - """ - return self.__size[0] - - def height(self) -> int: - """ - Get the height of the image. - - :return: The height, in pixels. - """ - return self.__size[1] - - def paste(self, im: Image.Image) -> None: - """ - Paste a PIL image into the photo image. Note that this can - be very slow if the photo image is displayed. - - :param im: A PIL image. The size must match the target region. If the - mode does not match, the image is converted to the mode of - the bitmap image. - """ - # convert to blittable - ptr = im.getim() - image = im.im - if not image.isblock() or im.mode != self.__mode: - block = Image.core.new_block(self.__mode, im.size) - image.convert2(block, image) # convert directly between buffers - ptr = block.ptr - - _pyimagingtkcall("PyImagingPhoto", self.__photo, ptr) - - -# -------------------------------------------------------------------- -# BitmapImage - - -class BitmapImage: - """ - A Tkinter-compatible bitmap image. This can be used everywhere Tkinter - expects an image object. - - The given image must have mode "1". Pixels having value 0 are treated as - transparent. Options, if any, are passed on to Tkinter. The most commonly - used option is ``foreground``, which is used to specify the color for the - non-transparent parts. See the Tkinter documentation for information on - how to specify colours. - - :param image: A PIL image. - """ - - def __init__(self, image: Image.Image | None = None, **kw: Any) -> None: - # Tk compatibility: file or data - if image is None: - image = _get_image_from_kw(kw) - - if image is None: - msg = "Image is required" - raise ValueError(msg) - self.__mode = image.mode - self.__size = image.size - - self.__photo = tkinter.BitmapImage(data=image.tobitmap(), **kw) - - def __del__(self) -> None: - try: - name = self.__photo.name - except AttributeError: - return - self.__photo.name = None - try: - self.__photo.tk.call("image", "delete", name) - except Exception: - pass # ignore internal errors - - def width(self) -> int: - """ - Get the width of the image. - - :return: The width, in pixels. - """ - return self.__size[0] - - def height(self) -> int: - """ - Get the height of the image. - - :return: The height, in pixels. - """ - return self.__size[1] - - def __str__(self) -> str: - """ - Get the Tkinter bitmap image identifier. This method is automatically - called by Tkinter whenever a BitmapImage object is passed to a Tkinter - method. - - :return: A Tkinter bitmap image identifier (a string). - """ - return str(self.__photo) - - -def getimage(photo: PhotoImage) -> Image.Image: - """Copies the contents of a PhotoImage to a PIL image memory.""" - im = Image.new("RGBA", (photo.width(), photo.height())) - - _pyimagingtkcall("PyImagingPhotoGet", photo, im.getim()) - - return im diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageTransform.py b/.venv/lib/python3.12/site-packages/PIL/ImageTransform.py deleted file mode 100644 index fb144ff3..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageTransform.py +++ /dev/null @@ -1,136 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# transform wrappers -# -# History: -# 2002-04-08 fl Created -# -# Copyright (c) 2002 by Secret Labs AB -# Copyright (c) 2002 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from collections.abc import Sequence -from typing import Any - -from . import Image - - -class Transform(Image.ImageTransformHandler): - """Base class for other transforms defined in :py:mod:`~PIL.ImageTransform`.""" - - method: Image.Transform - - def __init__(self, data: Sequence[Any]) -> None: - self.data = data - - def getdata(self) -> tuple[Image.Transform, Sequence[int]]: - return self.method, self.data - - def transform( - self, - size: tuple[int, int], - image: Image.Image, - **options: Any, - ) -> Image.Image: - """Perform the transform. Called from :py:meth:`.Image.transform`.""" - # can be overridden - method, data = self.getdata() - return image.transform(size, method, data, **options) - - -class AffineTransform(Transform): - """ - Define an affine image transform. - - This function takes a 6-tuple (a, b, c, d, e, f) which contain the first - two rows from the inverse of an affine transform matrix. For each pixel - (x, y) in the output image, the new value is taken from a position (a x + - b y + c, d x + e y + f) in the input image, rounded to nearest pixel. - - This function can be used to scale, translate, rotate, and shear the - original image. - - See :py:meth:`.Image.transform` - - :param matrix: A 6-tuple (a, b, c, d, e, f) containing the first two rows - from the inverse of an affine transform matrix. - """ - - method = Image.Transform.AFFINE - - -class PerspectiveTransform(Transform): - """ - Define a perspective image transform. - - This function takes an 8-tuple (a, b, c, d, e, f, g, h). For each pixel - (x, y) in the output image, the new value is taken from a position - ((a x + b y + c) / (g x + h y + 1), (d x + e y + f) / (g x + h y + 1)) in - the input image, rounded to nearest pixel. - - This function can be used to scale, translate, rotate, and shear the - original image. - - See :py:meth:`.Image.transform` - - :param matrix: An 8-tuple (a, b, c, d, e, f, g, h). - """ - - method = Image.Transform.PERSPECTIVE - - -class ExtentTransform(Transform): - """ - Define a transform to extract a subregion from an image. - - Maps a rectangle (defined by two corners) from the image to a rectangle of - the given size. The resulting image will contain data sampled from between - the corners, such that (x0, y0) in the input image will end up at (0,0) in - the output image, and (x1, y1) at size. - - This method can be used to crop, stretch, shrink, or mirror an arbitrary - rectangle in the current image. It is slightly slower than crop, but about - as fast as a corresponding resize operation. - - See :py:meth:`.Image.transform` - - :param bbox: A 4-tuple (x0, y0, x1, y1) which specifies two points in the - input image's coordinate system. See :ref:`coordinate-system`. - """ - - method = Image.Transform.EXTENT - - -class QuadTransform(Transform): - """ - Define a quad image transform. - - Maps a quadrilateral (a region defined by four corners) from the image to a - rectangle of the given size. - - See :py:meth:`.Image.transform` - - :param xy: An 8-tuple (x0, y0, x1, y1, x2, y2, x3, y3) which contain the - upper left, lower left, lower right, and upper right corner of the - source quadrilateral. - """ - - method = Image.Transform.QUAD - - -class MeshTransform(Transform): - """ - Define a mesh image transform. A mesh transform consists of one or more - individual quad transforms. - - See :py:meth:`.Image.transform` - - :param data: A list of (bbox, quad) tuples. - """ - - method = Image.Transform.MESH diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageWin.py b/.venv/lib/python3.12/site-packages/PIL/ImageWin.py deleted file mode 100644 index 98c28f29..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImageWin.py +++ /dev/null @@ -1,247 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a Windows DIB display interface -# -# History: -# 1996-05-20 fl Created -# 1996-09-20 fl Fixed subregion exposure -# 1997-09-21 fl Added draw primitive (for tzPrint) -# 2003-05-21 fl Added experimental Window/ImageWindow classes -# 2003-09-05 fl Added fromstring/tostring methods -# -# Copyright (c) Secret Labs AB 1997-2003. -# Copyright (c) Fredrik Lundh 1996-2003. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image - - -class HDC: - """ - Wraps an HDC integer. The resulting object can be passed to the - :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` - methods. - """ - - def __init__(self, dc: int) -> None: - self.dc = dc - - def __int__(self) -> int: - return self.dc - - -class HWND: - """ - Wraps an HWND integer. The resulting object can be passed to the - :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` - methods, instead of a DC. - """ - - def __init__(self, wnd: int) -> None: - self.wnd = wnd - - def __int__(self) -> int: - return self.wnd - - -class Dib: - """ - A Windows bitmap with the given mode and size. The mode can be one of "1", - "L", "P", or "RGB". - - If the display requires a palette, this constructor creates a suitable - palette and associates it with the image. For an "L" image, 128 graylevels - are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together - with 20 graylevels. - - To make sure that palettes work properly under Windows, you must call the - ``palette`` method upon certain events from Windows. - - :param image: Either a PIL image, or a mode string. If a mode string is - used, a size must also be given. The mode can be one of "1", - "L", "P", or "RGB". - :param size: If the first argument is a mode string, this - defines the size of the image. - """ - - def __init__( - self, image: Image.Image | str, size: tuple[int, int] | None = None - ) -> None: - if isinstance(image, str): - mode = image - image = "" - if size is None: - msg = "If first argument is mode, size is required" - raise ValueError(msg) - else: - mode = image.mode - size = image.size - if mode not in ["1", "L", "P", "RGB"]: - mode = Image.getmodebase(mode) - self.image = Image.core.display(mode, size) - self.mode = mode - self.size = size - if image: - assert not isinstance(image, str) - self.paste(image) - - def expose(self, handle: int | HDC | HWND) -> None: - """ - Copy the bitmap contents to a device context. - - :param handle: Device context (HDC), cast to a Python integer, or an - HDC or HWND instance. In PythonWin, you can use - ``CDC.GetHandleAttrib()`` to get a suitable handle. - """ - handle_int = int(handle) - if isinstance(handle, HWND): - dc = self.image.getdc(handle_int) - try: - self.image.expose(dc) - finally: - self.image.releasedc(handle_int, dc) - else: - self.image.expose(handle_int) - - def draw( - self, - handle: int | HDC | HWND, - dst: tuple[int, int, int, int], - src: tuple[int, int, int, int] | None = None, - ) -> None: - """ - Same as expose, but allows you to specify where to draw the image, and - what part of it to draw. - - The destination and source areas are given as 4-tuple rectangles. If - the source is omitted, the entire image is copied. If the source and - the destination have different sizes, the image is resized as - necessary. - """ - if src is None: - src = (0, 0) + self.size - handle_int = int(handle) - if isinstance(handle, HWND): - dc = self.image.getdc(handle_int) - try: - self.image.draw(dc, dst, src) - finally: - self.image.releasedc(handle_int, dc) - else: - self.image.draw(handle_int, dst, src) - - def query_palette(self, handle: int | HDC | HWND) -> int: - """ - Installs the palette associated with the image in the given device - context. - - This method should be called upon **QUERYNEWPALETTE** and - **PALETTECHANGED** events from Windows. If this method returns a - non-zero value, one or more display palette entries were changed, and - the image should be redrawn. - - :param handle: Device context (HDC), cast to a Python integer, or an - HDC or HWND instance. - :return: The number of entries that were changed (if one or more entries, - this indicates that the image should be redrawn). - """ - handle_int = int(handle) - if isinstance(handle, HWND): - handle = self.image.getdc(handle_int) - try: - result = self.image.query_palette(handle) - finally: - self.image.releasedc(handle, handle) - else: - result = self.image.query_palette(handle_int) - return result - - def paste( - self, im: Image.Image, box: tuple[int, int, int, int] | None = None - ) -> None: - """ - Paste a PIL image into the bitmap image. - - :param im: A PIL image. The size must match the target region. - If the mode does not match, the image is converted to the - mode of the bitmap image. - :param box: A 4-tuple defining the left, upper, right, and - lower pixel coordinate. See :ref:`coordinate-system`. If - None is given instead of a tuple, all of the image is - assumed. - """ - im.load() - if self.mode != im.mode: - im = im.convert(self.mode) - if box: - self.image.paste(im.im, box) - else: - self.image.paste(im.im) - - def frombytes(self, buffer: bytes) -> None: - """ - Load display memory contents from byte data. - - :param buffer: A buffer containing display data (usually - data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`) - """ - self.image.frombytes(buffer) - - def tobytes(self) -> bytes: - """ - Copy display memory contents to bytes object. - - :return: A bytes object containing display data. - """ - return self.image.tobytes() - - -class Window: - """Create a Window with the given title size.""" - - def __init__( - self, title: str = "PIL", width: int | None = None, height: int | None = None - ) -> None: - self.hwnd = Image.core.createwindow( - title, self.__dispatcher, width or 0, height or 0 - ) - - def __dispatcher(self, action: str, *args: int) -> None: - getattr(self, f"ui_handle_{action}")(*args) - - def ui_handle_clear(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None: - pass - - def ui_handle_damage(self, x0: int, y0: int, x1: int, y1: int) -> None: - pass - - def ui_handle_destroy(self) -> None: - pass - - def ui_handle_repair(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None: - pass - - def ui_handle_resize(self, width: int, height: int) -> None: - pass - - def mainloop(self) -> None: - Image.core.eventloop() - - -class ImageWindow(Window): - """Create an image window which displays the given image.""" - - def __init__(self, image: Image.Image | Dib, title: str = "PIL") -> None: - if not isinstance(image, Dib): - image = Dib(image) - self.image = image - width, height = image.size - super().__init__(title, width=width, height=height) - - def ui_handle_repair(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None: - self.image.draw(dc, (x0, y0, x1, y1)) diff --git a/.venv/lib/python3.12/site-packages/PIL/ImtImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/ImtImagePlugin.py deleted file mode 100644 index c4eccee3..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/ImtImagePlugin.py +++ /dev/null @@ -1,103 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# IM Tools support for PIL -# -# history: -# 1996-05-27 fl Created (read 8-bit images only) -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.2) -# -# Copyright (c) Secret Labs AB 1997-2001. -# Copyright (c) Fredrik Lundh 1996-2001. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re - -from . import Image, ImageFile - -# -# -------------------------------------------------------------------- - -field = re.compile(rb"([a-z]*) ([^ \r\n]*)") - - -## -# Image plugin for IM Tools images. - - -class ImtImageFile(ImageFile.ImageFile): - format = "IMT" - format_description = "IM Tools" - - def _open(self) -> None: - # Quick rejection: if there's not a LF among the first - # 100 bytes, this is (probably) not a text header. - - assert self.fp is not None - - buffer = self.fp.read(100) - if b"\n" not in buffer: - msg = "not an IM file" - raise SyntaxError(msg) - - xsize = ysize = 0 - - while True: - if buffer: - s = buffer[:1] - buffer = buffer[1:] - else: - s = self.fp.read(1) - if not s: - break - - if s == b"\x0c": - # image data begins - self.tile = [ - ImageFile._Tile( - "raw", - (0, 0) + self.size, - self.fp.tell() - len(buffer), - self.mode, - ) - ] - - break - - else: - # read key/value pair - if b"\n" not in buffer: - buffer += self.fp.read(100) - lines = buffer.split(b"\n") - s += lines.pop(0) - buffer = b"\n".join(lines) - if len(s) == 1 or len(s) > 100: - break - if s[0] == ord(b"*"): - continue # comment - - m = field.match(s) - if not m: - break - k, v = m.group(1, 2) - if k == b"width": - xsize = int(v) - self._size = xsize, ysize - elif k == b"height": - ysize = int(v) - self._size = xsize, ysize - elif k == b"pixel" and v == b"n8": - self._mode = "L" - - -# -# -------------------------------------------------------------------- - -Image.register_open(ImtImageFile.format, ImtImageFile) - -# -# no extension registered (".im" is simply too common) diff --git a/.venv/lib/python3.12/site-packages/PIL/IptcImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/IptcImagePlugin.py deleted file mode 100644 index 9c8be8b4..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/IptcImagePlugin.py +++ /dev/null @@ -1,226 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# IPTC/NAA file handling -# -# history: -# 1995-10-01 fl Created -# 1998-03-09 fl Cleaned up and added to PIL -# 2002-06-18 fl Added getiptcinfo helper -# -# Copyright (c) Secret Labs AB 1997-2002. -# Copyright (c) Fredrik Lundh 1995. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from io import BytesIO -from typing import cast - -from . import Image, ImageFile -from ._binary import i16be as i16 -from ._binary import i32be as i32 - -COMPRESSION = {1: "raw", 5: "jpeg"} - - -# -# Helpers - - -def _i(c: bytes) -> int: - return i32((b"\0\0\0\0" + c)[-4:]) - - -## -# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields -# from TIFF and JPEG files, use the getiptcinfo function. - - -class IptcImageFile(ImageFile.ImageFile): - format = "IPTC" - format_description = "IPTC/NAA" - - def getint(self, key: tuple[int, int]) -> int: - return _i(self.info[key]) - - def field(self) -> tuple[tuple[int, int] | None, int]: - # - # get a IPTC field header - assert self.fp is not None - s = self.fp.read(5) - if not s.strip(b"\x00"): - return None, 0 - - tag = s[1], s[2] - - # syntax - if s[0] != 0x1C or tag[0] not in [1, 2, 3, 4, 5, 6, 7, 8, 9, 240]: - msg = "invalid IPTC/NAA file" - raise SyntaxError(msg) - - # field size - size = s[3] - if size > 132: - msg = "illegal field length in IPTC/NAA file" - raise OSError(msg) - elif size == 128: - size = 0 - elif size > 128: - size = _i(self.fp.read(size - 128)) - else: - size = i16(s, 3) - - return tag, size - - def _open(self) -> None: - # load descriptive fields - assert self.fp is not None - while True: - offset = self.fp.tell() - tag, size = self.field() - if not tag or tag == (8, 10): - break - if size: - tagdata = self.fp.read(size) - else: - tagdata = None - if tag in self.info: - if isinstance(self.info[tag], list): - self.info[tag].append(tagdata) - else: - self.info[tag] = [self.info[tag], tagdata] - else: - self.info[tag] = tagdata - - # mode - layers = self.info[(3, 60)][0] - component = self.info[(3, 60)][1] - if layers == 1 and not component: - self._mode = "L" - band = None - else: - if layers == 3 and component: - self._mode = "RGB" - elif layers == 4 and component: - self._mode = "CMYK" - if (3, 65) in self.info: - band = self.info[(3, 65)][0] - 1 - else: - band = 0 - - # size - self._size = self.getint((3, 20)), self.getint((3, 30)) - - # compression - try: - compression = COMPRESSION[self.getint((3, 120))] - except KeyError as e: - msg = "Unknown IPTC image compression" - raise OSError(msg) from e - - # tile - if tag == (8, 10): - self.tile = [ - ImageFile._Tile("iptc", (0, 0) + self.size, offset, (compression, band)) - ] - - def load(self) -> Image.core.PixelAccess | None: - if self.tile: - args = self.tile[0].args - assert isinstance(args, tuple) - compression, band = args - - assert self.fp is not None - self.fp.seek(self.tile[0].offset) - - # Copy image data to temporary file - o = BytesIO() - if compression == "raw": - # To simplify access to the extracted file, - # prepend a PPM header - o.write(b"P5\n%d %d\n255\n" % self.size) - while True: - type, size = self.field() - if type != (8, 10): - break - while size > 0: - s = self.fp.read(min(size, 8192)) - if not s: - break - o.write(s) - size -= len(s) - - with Image.open(o) as _im: - if band is not None: - bands = [Image.new("L", _im.size)] * Image.getmodebands(self.mode) - bands[band] = _im - im = Image.merge(self.mode, bands) - else: - im = _im - im.load() - self.im = im.im - self.tile = [] - return ImageFile.ImageFile.load(self) - - -Image.register_open(IptcImageFile.format, IptcImageFile) - -Image.register_extension(IptcImageFile.format, ".iim") - - -def getiptcinfo( - im: ImageFile.ImageFile, -) -> dict[tuple[int, int], bytes | list[bytes]] | None: - """ - Get IPTC information from TIFF, JPEG, or IPTC file. - - :param im: An image containing IPTC data. - :returns: A dictionary containing IPTC information, or None if - no IPTC information block was found. - """ - from . import JpegImagePlugin, TiffImagePlugin - - data = None - - if isinstance(im, IptcImageFile): - # return info dictionary right away - return {k: v for k, v in im.info.items() if isinstance(k, tuple)} - - elif isinstance(im, JpegImagePlugin.JpegImageFile): - # extract the IPTC/NAA resource - photoshop = im.info.get("photoshop") - if photoshop: - data = photoshop.get(0x0404) - - elif isinstance(im, TiffImagePlugin.TiffImageFile): - # get raw data from the IPTC/NAA tag (PhotoShop tags the data - # as 4-byte integers, so we cannot use the get method...) - try: - data = im.tag_v2._tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] - except KeyError: - pass - - if data is None: - return None # no properties - - # create an IptcImagePlugin object without initializing it - class FakeImage: - pass - - fake_im = FakeImage() - fake_im.__class__ = IptcImageFile # type: ignore[assignment] - iptc_im = cast(IptcImageFile, fake_im) - - # parse the IPTC information chunk - iptc_im.info = {} - iptc_im.fp = BytesIO(data) - - try: - iptc_im._open() - except (IndexError, KeyError): - pass # expected failure - - return {k: v for k, v in iptc_im.info.items() if isinstance(k, tuple)} diff --git a/.venv/lib/python3.12/site-packages/PIL/Jpeg2KImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/Jpeg2KImagePlugin.py deleted file mode 100644 index cb377353..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/Jpeg2KImagePlugin.py +++ /dev/null @@ -1,460 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# JPEG2000 file handling -# -# History: -# 2014-03-12 ajh Created -# 2021-06-30 rogermb Extract dpi information from the 'resc' header box -# -# Copyright (c) 2014 Coriolis Systems Limited -# Copyright (c) 2014 Alastair Houghton -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import struct -from typing import cast - -from . import Image, ImageFile, ImagePalette, _binary - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from typing import IO - - -class BoxReader: - """ - A small helper class to read fields stored in JPEG2000 header boxes - and to easily step into and read sub-boxes. - """ - - def __init__(self, fp: IO[bytes], length: int = -1) -> None: - self.fp = fp - self.has_length = length >= 0 - self.length = length - self.remaining_in_box = -1 - - def _can_read(self, num_bytes: int) -> bool: - if self.has_length and self.fp.tell() + num_bytes > self.length: - # Outside box: ensure we don't read past the known file length - return False - if self.remaining_in_box >= 0: - # Inside box contents: ensure read does not go past box boundaries - return num_bytes <= self.remaining_in_box - else: - return True # No length known, just read - - def _read_bytes(self, num_bytes: int) -> bytes: - if not self._can_read(num_bytes): - msg = "Not enough data in header" - raise SyntaxError(msg) - - data = self.fp.read(num_bytes) - if len(data) < num_bytes: - msg = f"Expected to read {num_bytes} bytes but only got {len(data)}." - raise OSError(msg) - - if self.remaining_in_box > 0: - self.remaining_in_box -= num_bytes - return data - - def read_fields(self, field_format: str) -> tuple[int | bytes, ...]: - size = struct.calcsize(field_format) - data = self._read_bytes(size) - return struct.unpack(field_format, data) - - def read_boxes(self) -> BoxReader: - size = self.remaining_in_box - data = self._read_bytes(size) - return BoxReader(io.BytesIO(data), size) - - def has_next_box(self) -> bool: - if self.has_length: - return self.fp.tell() + self.remaining_in_box < self.length - else: - return True - - def next_box_type(self) -> bytes: - # Skip the rest of the box if it has not been read - if self.remaining_in_box > 0: - self.fp.seek(self.remaining_in_box, os.SEEK_CUR) - self.remaining_in_box = -1 - - # Read the length and type of the next box - lbox, tbox = cast(tuple[int, bytes], self.read_fields(">I4s")) - if lbox == 1: - lbox = cast(int, self.read_fields(">Q")[0]) - hlen = 16 - else: - hlen = 8 - - if lbox < hlen or not self._can_read(lbox - hlen): - msg = "Invalid header length" - raise SyntaxError(msg) - - self.remaining_in_box = lbox - hlen - return tbox - - -def _parse_codestream(fp: IO[bytes]) -> tuple[tuple[int, int], str]: - """Parse the JPEG 2000 codestream to extract the size and component - count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" - - hdr = fp.read(2) - lsiz = _binary.i16be(hdr) - siz = hdr + fp.read(lsiz - 2) - lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, _, _, _, _, csiz = struct.unpack_from( - ">HHIIIIIIIIH", siz - ) - - size = (xsiz - xosiz, ysiz - yosiz) - if csiz == 1: - ssiz = struct.unpack_from(">B", siz, 38) - if (ssiz[0] & 0x7F) + 1 > 8: - mode = "I;16" - else: - mode = "L" - elif csiz == 2: - mode = "LA" - elif csiz == 3: - mode = "RGB" - elif csiz == 4: - mode = "RGBA" - else: - msg = "unable to determine J2K image mode" - raise SyntaxError(msg) - - return size, mode - - -def _res_to_dpi(num: int, denom: int, exp: int) -> float | None: - """Convert JPEG2000's (numerator, denominator, exponent-base-10) resolution, - calculated as (num / denom) * 10^exp and stored in dots per meter, - to floating-point dots per inch.""" - if denom == 0: - return None - return (254 * num * (10**exp)) / (10000 * denom) - - -def _parse_jp2_header( - fp: IO[bytes], -) -> tuple[ - tuple[int, int], - str, - str | None, - tuple[float, float] | None, - ImagePalette.ImagePalette | None, -]: - """Parse the JP2 header box to extract size, component count, - color space information, and optionally DPI information, - returning a (size, mode, mimetype, dpi) tuple.""" - - # Find the JP2 header box - reader = BoxReader(fp) - header = None - mimetype = None - while reader.has_next_box(): - tbox = reader.next_box_type() - - if tbox == b"jp2h": - header = reader.read_boxes() - break - elif tbox == b"ftyp": - if reader.read_fields(">4s")[0] == b"jpx ": - mimetype = "image/jpx" - assert header is not None - - size = None - mode = None - bpc = None - nc = None - dpi = None # 2-tuple of DPI info, or None - palette = None - colr = None - - while header.has_next_box(): - tbox = header.next_box_type() - - if tbox == b"ihdr": - height, width, nc, bpc = header.read_fields(">IIHB") - assert isinstance(height, int) - assert isinstance(width, int) - assert isinstance(bpc, int) - size = (width, height) - if nc == 1 and (bpc & 0x7F) > 8: - mode = "I;16" - elif nc == 1: - mode = "L" - elif nc == 2: - mode = "LA" - elif nc == 3: - mode = "RGB" - elif nc == 4: - mode = "RGBA" - elif tbox == b"colr": - meth, _, _, enumcs = header.read_fields(">BBBI") - if meth == 1: - if enumcs in (0, 15): - colr = "1" - elif enumcs == 12: - colr = "CMYK" - if nc == 4: - mode = "CMYK" - elif enumcs == 17: - colr = "L" - elif tbox == b"pclr" and mode in ("L", "LA") and colr not in ("1", "L"): - ne, npc = header.read_fields(">HB") - assert isinstance(ne, int) - assert isinstance(npc, int) - max_bitdepth = 0 - for bitdepth in header.read_fields(">" + ("B" * npc)): - assert isinstance(bitdepth, int) - if bitdepth > max_bitdepth: - max_bitdepth = bitdepth - if max_bitdepth <= 8: - if npc == 4: - palette_mode = "CMYK" if colr == "CMYK" else "RGBA" - else: - palette_mode = "RGB" - palette = ImagePalette.ImagePalette(palette_mode) - for i in range(ne): - color: list[int] = [] - for value in header.read_fields(">" + ("B" * npc)): - assert isinstance(value, int) - color.append(value) - palette.getcolor(tuple(color)) - mode = "P" if mode == "L" else "PA" - elif tbox == b"res ": - res = header.read_boxes() - while res.has_next_box(): - tres = res.next_box_type() - if tres == b"resc": - vrcn, vrcd, hrcn, hrcd, vrce, hrce = res.read_fields(">HHHHBB") - assert isinstance(vrcn, int) - assert isinstance(vrcd, int) - assert isinstance(hrcn, int) - assert isinstance(hrcd, int) - assert isinstance(vrce, int) - assert isinstance(hrce, int) - hres = _res_to_dpi(hrcn, hrcd, hrce) - vres = _res_to_dpi(vrcn, vrcd, vrce) - if hres is not None and vres is not None: - dpi = (hres, vres) - break - - if size is None or mode is None: - msg = "Malformed JP2 header" - raise SyntaxError(msg) - - return size, mode, mimetype, dpi, palette - - -## -# Image plugin for JPEG2000 images. - - -class Jpeg2KImageFile(ImageFile.ImageFile): - format = "JPEG2000" - format_description = "JPEG 2000 (ISO 15444)" - - def _open(self) -> None: - assert self.fp is not None - sig = self.fp.read(4) - if sig == b"\xff\x4f\xff\x51": - self.codec = "j2k" - self._size, self._mode = _parse_codestream(self.fp) - self._parse_comment() - else: - sig = sig + self.fp.read(8) - - if sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a": - self.codec = "jp2" - header = _parse_jp2_header(self.fp) - self._size, self._mode, self.custom_mimetype, dpi, self.palette = header - if dpi is not None: - self.info["dpi"] = dpi - if self.fp.read(12).endswith(b"jp2c\xff\x4f\xff\x51"): - hdr = self.fp.read(2) - length = _binary.i16be(hdr) - self.fp.seek(length - 2, os.SEEK_CUR) - self._parse_comment() - else: - msg = "not a JPEG 2000 file" - raise SyntaxError(msg) - - self._reduce = 0 - self.layers = 0 - - fd = -1 - length = -1 - - try: - fd = self.fp.fileno() - length = os.fstat(fd).st_size - except Exception: - fd = -1 - try: - pos = self.fp.tell() - self.fp.seek(0, io.SEEK_END) - length = self.fp.tell() - self.fp.seek(pos) - except Exception: - length = -1 - - self.tile = [ - ImageFile._Tile( - "jpeg2k", - (0, 0) + self.size, - 0, - (self.codec, self._reduce, self.layers, fd, length), - ) - ] - - def _parse_comment(self) -> None: - assert self.fp is not None - while True: - marker = self.fp.read(2) - if not marker: - break - typ = marker[1] - if typ in (0x90, 0xD9): - # Start of tile or end of codestream - break - hdr = self.fp.read(2) - length = _binary.i16be(hdr) - if typ == 0x64: - # Comment - self.info["comment"] = self.fp.read(length - 2)[2:] - break - else: - self.fp.seek(length - 2, os.SEEK_CUR) - - @property # type: ignore[override] - def reduce( - self, - ) -> ( - Callable[[int | tuple[int, int], tuple[int, int, int, int] | None], Image.Image] - | int - ): - # https://github.com/python-pillow/Pillow/issues/4343 found that the - # new Image 'reduce' method was shadowed by this plugin's 'reduce' - # property. This attempts to allow for both scenarios - return self._reduce or super().reduce - - @reduce.setter - def reduce(self, value: int) -> None: - self._reduce = value - - def load(self) -> Image.core.PixelAccess | None: - if self.tile and self._reduce: - power = 1 << self._reduce - adjust = power >> 1 - self._size = ( - int((self.size[0] + adjust) / power), - int((self.size[1] + adjust) / power), - ) - - # Update the reduce and layers settings - t = self.tile[0] - assert isinstance(t[3], tuple) - t3 = (t[3][0], self._reduce, self.layers, t[3][3], t[3][4]) - self.tile = [ImageFile._Tile(t[0], (0, 0) + self.size, t[2], t3)] - - return ImageFile.ImageFile.load(self) - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith( - (b"\xff\x4f\xff\x51", b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a") - ) - - -# ------------------------------------------------------------ -# Save support - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - # Get the keyword arguments - info = im.encoderinfo - - if isinstance(filename, str): - filename = filename.encode() - if filename.endswith(b".j2k") or info.get("no_jp2", False): - kind = "j2k" - else: - kind = "jp2" - - offset = info.get("offset", None) - tile_offset = info.get("tile_offset", None) - tile_size = info.get("tile_size", None) - quality_mode = info.get("quality_mode", "rates") - quality_layers = info.get("quality_layers", None) - if quality_layers is not None and not ( - isinstance(quality_layers, (list, tuple)) - and all( - isinstance(quality_layer, (int, float)) for quality_layer in quality_layers - ) - ): - msg = "quality_layers must be a sequence of numbers" - raise ValueError(msg) - - num_resolutions = info.get("num_resolutions", 0) - cblk_size = info.get("codeblock_size", None) - precinct_size = info.get("precinct_size", None) - irreversible = info.get("irreversible", False) - progression = info.get("progression", "LRCP") - cinema_mode = info.get("cinema_mode", "no") - mct = info.get("mct", 0) - signed = info.get("signed", False) - comment = info.get("comment") - if isinstance(comment, str): - comment = comment.encode() - plt = info.get("plt", False) - - fd = -1 - if hasattr(fp, "fileno"): - try: - fd = fp.fileno() - except Exception: - fd = -1 - - im.encoderconfig = ( - offset, - tile_offset, - tile_size, - quality_mode, - quality_layers, - num_resolutions, - cblk_size, - precinct_size, - irreversible, - progression, - cinema_mode, - mct, - signed, - fd, - comment, - plt, - ) - - ImageFile._save(im, fp, [ImageFile._Tile("jpeg2k", (0, 0) + im.size, 0, kind)]) - - -# ------------------------------------------------------------ -# Registry stuff - - -Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept) -Image.register_save(Jpeg2KImageFile.format, _save) - -Image.register_extensions( - Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"] -) - -Image.register_mime(Jpeg2KImageFile.format, "image/jp2") diff --git a/.venv/lib/python3.12/site-packages/PIL/JpegImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/JpegImagePlugin.py deleted file mode 100644 index 46320eb3..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/JpegImagePlugin.py +++ /dev/null @@ -1,889 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# JPEG (JFIF) file handling -# -# See "Digital Compression and Coding of Continuous-Tone Still Images, -# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1) -# -# History: -# 1995-09-09 fl Created -# 1995-09-13 fl Added full parser -# 1996-03-25 fl Added hack to use the IJG command line utilities -# 1996-05-05 fl Workaround Photoshop 2.5 CMYK polarity bug -# 1996-05-28 fl Added draft support, JFIF version (0.1) -# 1996-12-30 fl Added encoder options, added progression property (0.2) -# 1997-08-27 fl Save mode 1 images as BW (0.3) -# 1998-07-12 fl Added YCbCr to draft and save methods (0.4) -# 1998-10-19 fl Don't hang on files using 16-bit DQT's (0.4.1) -# 2001-04-16 fl Extract DPI settings from JFIF files (0.4.2) -# 2002-07-01 fl Skip pad bytes before markers; identify Exif files (0.4.3) -# 2003-04-25 fl Added experimental EXIF decoder (0.5) -# 2003-06-06 fl Added experimental EXIF GPSinfo decoder -# 2003-09-13 fl Extract COM markers -# 2009-09-06 fl Added icc_profile support (from Florian Hoech) -# 2009-03-06 fl Changed CMYK handling; always use Adobe polarity (0.6) -# 2009-03-08 fl Added subsampling support (from Justin Huff). -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-1996 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import array -import io -import math -import os -import struct -import subprocess -import sys -import tempfile -import warnings - -from . import Image, ImageFile -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._binary import o8 -from ._binary import o16be as o16 -from .JpegPresets import presets - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import IO, Any - - from .MpoImagePlugin import MpoImageFile - -# -# Parser - - -def Skip(self: JpegImageFile, marker: int) -> None: - assert self.fp is not None - n = i16(self.fp.read(2)) - 2 - ImageFile._safe_read(self.fp, n) - - -def APP(self: JpegImageFile, marker: int) -> None: - # - # Application marker. Store these in the APP dictionary. - # Also look for well-known application markers. - - assert self.fp is not None - n = i16(self.fp.read(2)) - 2 - s = ImageFile._safe_read(self.fp, n) - - app = f"APP{marker & 15}" - - self.app[app] = s # compatibility - self.applist.append((app, s)) - - if marker == 0xFFE0 and s.startswith(b"JFIF"): - # extract JFIF information - self.info["jfif"] = version = i16(s, 5) # version - self.info["jfif_version"] = divmod(version, 256) - # extract JFIF properties - try: - jfif_unit = s[7] - jfif_density = i16(s, 8), i16(s, 10) - except Exception: - pass - else: - if jfif_unit == 1: - self.info["dpi"] = jfif_density - elif jfif_unit == 2: # cm - # 1 dpcm = 2.54 dpi - self.info["dpi"] = tuple(d * 2.54 for d in jfif_density) - self.info["jfif_unit"] = jfif_unit - self.info["jfif_density"] = jfif_density - elif marker == 0xFFE1 and s.startswith(b"Exif\0\0"): - # extract EXIF information - if "exif" in self.info: - self.info["exif"] += s[6:] - else: - self.info["exif"] = s - self._exif_offset = self.fp.tell() - n + 6 - elif marker == 0xFFE1 and s.startswith(b"http://ns.adobe.com/xap/1.0/\x00"): - self.info["xmp"] = s.split(b"\x00", 1)[1] - elif marker == 0xFFE2 and s.startswith(b"FPXR\0"): - # extract FlashPix information (incomplete) - self.info["flashpix"] = s # FIXME: value will change - elif marker == 0xFFE2 and s.startswith(b"ICC_PROFILE\0"): - # Since an ICC profile can be larger than the maximum size of - # a JPEG marker (64K), we need provisions to split it into - # multiple markers. The format defined by the ICC specifies - # one or more APP2 markers containing the following data: - # Identifying string ASCII "ICC_PROFILE\0" (12 bytes) - # Marker sequence number 1, 2, etc (1 byte) - # Number of markers Total of APP2's used (1 byte) - # Profile data (remainder of APP2 data) - # Decoders should use the marker sequence numbers to - # reassemble the profile, rather than assuming that the APP2 - # markers appear in the correct sequence. - self.icclist.append(s) - elif marker == 0xFFED and s.startswith(b"Photoshop 3.0\x00"): - # parse the image resource block - offset = 14 - photoshop = self.info.setdefault("photoshop", {}) - try: - while s[offset : offset + 4] == b"8BIM": - offset += 4 - # resource code - code = i16(s, offset) - offset += 2 - # resource name (usually empty) - name_len = s[offset] - # name = s[offset+1:offset+1+name_len] - offset += 1 + name_len - offset += offset & 1 # align - # resource data block - size = i32(s, offset) - offset += 4 - data = s[offset : offset + size] - if code == 0x03ED: # ResolutionInfo - photoshop[code] = { - "XResolution": i32(data, 0) / 65536, - "DisplayedUnitsX": i16(data, 4), - "YResolution": i32(data, 8) / 65536, - "DisplayedUnitsY": i16(data, 12), - } - else: - photoshop[code] = data - offset += size - offset += offset & 1 # align - except struct.error: - pass # insufficient data - - elif marker == 0xFFEE and s.startswith(b"Adobe"): - self.info["adobe"] = i16(s, 5) - # extract Adobe custom properties - try: - adobe_transform = s[11] - except IndexError: - pass - else: - self.info["adobe_transform"] = adobe_transform - elif marker == 0xFFE2 and s.startswith(b"MPF\0"): - # extract MPO information - self.info["mp"] = s[4:] - # offset is current location minus buffer size - # plus constant header size - self.info["mpoffset"] = self.fp.tell() - n + 4 - - -def COM(self: JpegImageFile, marker: int) -> None: - # - # Comment marker. Store these in the APP dictionary. - assert self.fp is not None - n = i16(self.fp.read(2)) - 2 - s = ImageFile._safe_read(self.fp, n) - - self.info["comment"] = s - self.app["COM"] = s # compatibility - self.applist.append(("COM", s)) - - -def SOF(self: JpegImageFile, marker: int) -> None: - # - # Start of frame marker. Defines the size and mode of the - # image. JPEG is colour blind, so we use some simple - # heuristics to map the number of layers to an appropriate - # mode. Note that this could be made a bit brighter, by - # looking for JFIF and Adobe APP markers. - - assert self.fp is not None - n = i16(self.fp.read(2)) - 2 - s = ImageFile._safe_read(self.fp, n) - self._size = i16(s, 3), i16(s, 1) - if self._im is not None and self.size != self.im.size: - self._im = None - - self.bits = s[0] - if self.bits != 8: - msg = f"cannot handle {self.bits}-bit layers" - raise SyntaxError(msg) - - self.layers = s[5] - if self.layers == 1: - self._mode = "L" - elif self.layers == 3: - self._mode = "RGB" - elif self.layers == 4: - self._mode = "CMYK" - else: - msg = f"cannot handle {self.layers}-layer images" - raise SyntaxError(msg) - - if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]: - self.info["progressive"] = self.info["progression"] = 1 - - if self.icclist: - # fixup icc profile - self.icclist.sort() # sort by sequence number - if self.icclist[0][13] == len(self.icclist): - profile = [p[14:] for p in self.icclist] - icc_profile = b"".join(profile) - else: - icc_profile = None # wrong number of fragments - self.info["icc_profile"] = icc_profile - self.icclist = [] - - for i in range(6, len(s), 3): - t = s[i : i + 3] - # 4-tuples: id, vsamp, hsamp, qtable - self.layer.append((t[0], t[1] // 16, t[1] & 15, t[2])) - - -def DQT(self: JpegImageFile, marker: int) -> None: - # - # Define quantization table. Note that there might be more - # than one table in each marker. - - # FIXME: The quantization tables can be used to estimate the - # compression quality. - - assert self.fp is not None - n = i16(self.fp.read(2)) - 2 - s = ImageFile._safe_read(self.fp, n) - while len(s): - v = s[0] - precision = 1 if (v // 16 == 0) else 2 # in bytes - qt_length = 1 + precision * 64 - if len(s) < qt_length: - msg = "bad quantization table marker" - raise SyntaxError(msg) - data = array.array("B" if precision == 1 else "H", s[1:qt_length]) - if sys.byteorder == "little" and precision > 1: - data.byteswap() # the values are always big-endian - self.quantization[v & 15] = [data[i] for i in zigzag_index] - s = s[qt_length:] - - -# -# JPEG marker table - -MARKER = { - 0xFFC0: ("SOF0", "Baseline DCT", SOF), - 0xFFC1: ("SOF1", "Extended Sequential DCT", SOF), - 0xFFC2: ("SOF2", "Progressive DCT", SOF), - 0xFFC3: ("SOF3", "Spatial lossless", SOF), - 0xFFC4: ("DHT", "Define Huffman table", Skip), - 0xFFC5: ("SOF5", "Differential sequential DCT", SOF), - 0xFFC6: ("SOF6", "Differential progressive DCT", SOF), - 0xFFC7: ("SOF7", "Differential spatial", SOF), - 0xFFC8: ("JPG", "Extension", None), - 0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF), - 0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF), - 0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF), - 0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip), - 0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF), - 0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF), - 0xFFCF: ("SOF15", "Differential spatial (AC)", SOF), - 0xFFD0: ("RST0", "Restart 0", None), - 0xFFD1: ("RST1", "Restart 1", None), - 0xFFD2: ("RST2", "Restart 2", None), - 0xFFD3: ("RST3", "Restart 3", None), - 0xFFD4: ("RST4", "Restart 4", None), - 0xFFD5: ("RST5", "Restart 5", None), - 0xFFD6: ("RST6", "Restart 6", None), - 0xFFD7: ("RST7", "Restart 7", None), - 0xFFD8: ("SOI", "Start of image", None), - 0xFFD9: ("EOI", "End of image", None), - 0xFFDA: ("SOS", "Start of scan", Skip), - 0xFFDB: ("DQT", "Define quantization table", DQT), - 0xFFDC: ("DNL", "Define number of lines", Skip), - 0xFFDD: ("DRI", "Define restart interval", Skip), - 0xFFDE: ("DHP", "Define hierarchical progression", SOF), - 0xFFDF: ("EXP", "Expand reference component", Skip), - 0xFFE0: ("APP0", "Application segment 0", APP), - 0xFFE1: ("APP1", "Application segment 1", APP), - 0xFFE2: ("APP2", "Application segment 2", APP), - 0xFFE3: ("APP3", "Application segment 3", APP), - 0xFFE4: ("APP4", "Application segment 4", APP), - 0xFFE5: ("APP5", "Application segment 5", APP), - 0xFFE6: ("APP6", "Application segment 6", APP), - 0xFFE7: ("APP7", "Application segment 7", APP), - 0xFFE8: ("APP8", "Application segment 8", APP), - 0xFFE9: ("APP9", "Application segment 9", APP), - 0xFFEA: ("APP10", "Application segment 10", APP), - 0xFFEB: ("APP11", "Application segment 11", APP), - 0xFFEC: ("APP12", "Application segment 12", APP), - 0xFFED: ("APP13", "Application segment 13", APP), - 0xFFEE: ("APP14", "Application segment 14", APP), - 0xFFEF: ("APP15", "Application segment 15", APP), - 0xFFF0: ("JPG0", "Extension 0", None), - 0xFFF1: ("JPG1", "Extension 1", None), - 0xFFF2: ("JPG2", "Extension 2", None), - 0xFFF3: ("JPG3", "Extension 3", None), - 0xFFF4: ("JPG4", "Extension 4", None), - 0xFFF5: ("JPG5", "Extension 5", None), - 0xFFF6: ("JPG6", "Extension 6", None), - 0xFFF7: ("JPG7", "Extension 7", None), - 0xFFF8: ("JPG8", "Extension 8", None), - 0xFFF9: ("JPG9", "Extension 9", None), - 0xFFFA: ("JPG10", "Extension 10", None), - 0xFFFB: ("JPG11", "Extension 11", None), - 0xFFFC: ("JPG12", "Extension 12", None), - 0xFFFD: ("JPG13", "Extension 13", None), - 0xFFFE: ("COM", "Comment", COM), -} - - -def _accept(prefix: bytes) -> bool: - # Magic number was taken from https://en.wikipedia.org/wiki/JPEG - return prefix.startswith(b"\xff\xd8\xff") - - -## -# Image plugin for JPEG and JFIF images. - - -class JpegImageFile(ImageFile.ImageFile): - format = "JPEG" - format_description = "JPEG (ISO 10918)" - - def _open(self) -> None: - assert self.fp is not None - s = self.fp.read(3) - - if not _accept(s): - msg = "not a JPEG file" - raise SyntaxError(msg) - s = b"\xff" - - # Create attributes - self.bits = self.layers = 0 - self._exif_offset = 0 - - # JPEG specifics (internal) - self.layer: list[tuple[int, int, int, int]] = [] - self._huffman_dc: dict[Any, Any] = {} - self._huffman_ac: dict[Any, Any] = {} - self.quantization: dict[int, list[int]] = {} - self.app: dict[str, bytes] = {} # compatibility - self.applist: list[tuple[str, bytes]] = [] - self.icclist: list[bytes] = [] - - while True: - i = s[0] - if i == 0xFF: - s = s + self.fp.read(1) - i = i16(s) - else: - # Skip non-0xFF junk - s = self.fp.read(1) - continue - - if i in MARKER: - name, description, handler = MARKER[i] - if handler is not None: - handler(self, i) - if i == 0xFFDA: # start of scan - rawmode = self.mode - if self.mode == "CMYK": - rawmode = "CMYK;I" # assume adobe conventions - self.tile = [ - ImageFile._Tile("jpeg", (0, 0) + self.size, 0, (rawmode, "")) - ] - # self.__offset = self.fp.tell() - break - s = self.fp.read(1) - elif i in {0, 0xFFFF}: - # padded marker or junk; move on - s = b"\xff" - elif i == 0xFF00: # Skip extraneous data (escaped 0xFF) - s = self.fp.read(1) - else: - msg = "no marker found" - raise SyntaxError(msg) - - self._read_dpi_from_exif() - - def __getstate__(self) -> list[Any]: - return super().__getstate__() + [self.layers, self.layer] - - def __setstate__(self, state: list[Any]) -> None: - self.layers, self.layer = state[6:] - super().__setstate__(state) - - def load_read(self, read_bytes: int) -> bytes: - """ - internal: read more image data - For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker - so libjpeg can finish decoding - """ - assert self.fp is not None - s = self.fp.read(read_bytes) - - if not s and ImageFile.LOAD_TRUNCATED_IMAGES and not hasattr(self, "_ended"): - # Premature EOF. - # Pretend file is finished adding EOI marker - self._ended = True - return b"\xff\xd9" - - return s - - def draft( - self, mode: str | None, size: tuple[int, int] | None - ) -> tuple[str, tuple[int, int, float, float]] | None: - if len(self.tile) != 1: - return None - - # Protect from second call - if self.decoderconfig: - return None - - d, e, o, a = self.tile[0] - scale = 1 - original_size = self.size - - assert isinstance(a, tuple) - if a[0] == "RGB" and mode in ["L", "YCbCr"]: - self._mode = mode - a = mode, "" - - if size: - scale = min(self.size[0] // size[0], self.size[1] // size[1]) - for s in [8, 4, 2, 1]: - if scale >= s: - break - assert e is not None - e = ( - e[0], - e[1], - (e[2] - e[0] + s - 1) // s + e[0], - (e[3] - e[1] + s - 1) // s + e[1], - ) - self._size = ((self.size[0] + s - 1) // s, (self.size[1] + s - 1) // s) - scale = s - - self.tile = [ImageFile._Tile(d, e, o, a)] - self.decoderconfig = (scale, 0) - - box = (0, 0, original_size[0] / scale, original_size[1] / scale) - return self.mode, box - - def load_djpeg(self) -> None: - # ALTERNATIVE: handle JPEGs via the IJG command line utilities - - f, path = tempfile.mkstemp() - os.close(f) - if os.path.exists(self.filename): - subprocess.check_call(["djpeg", "-outfile", path, self.filename]) - else: - try: - os.unlink(path) - except OSError: - pass - - msg = "Invalid Filename" - raise ValueError(msg) - - try: - with Image.open(path) as _im: - _im.load() - self.im = _im.im - finally: - try: - os.unlink(path) - except OSError: - pass - - self._mode = self.im.mode - self._size = self.im.size - - self.tile = [] - - def _getexif(self) -> dict[int, Any] | None: - return _getexif(self) - - def _read_dpi_from_exif(self) -> None: - # If DPI isn't in JPEG header, fetch from EXIF - if "dpi" in self.info or "exif" not in self.info: - return - try: - exif = self.getexif() - resolution_unit = exif[0x0128] - x_resolution = exif[0x011A] - try: - dpi = float(x_resolution[0]) / x_resolution[1] - except TypeError: - dpi = x_resolution - if math.isnan(dpi): - msg = "DPI is not a number" - raise ValueError(msg) - if resolution_unit == 3: # cm - # 1 dpcm = 2.54 dpi - dpi *= 2.54 - self.info["dpi"] = dpi, dpi - except ( - struct.error, # truncated EXIF - KeyError, # dpi not included - SyntaxError, # invalid/unreadable EXIF - TypeError, # dpi is an invalid float - ValueError, # dpi is an invalid float - ZeroDivisionError, # invalid dpi rational value - ): - self.info["dpi"] = 72, 72 - - def _getmp(self) -> dict[int, Any] | None: - return _getmp(self) - - -def _getexif(self: JpegImageFile) -> dict[int, Any] | None: - if "exif" not in self.info: - return None - return self.getexif()._get_merged_dict() - - -def _getmp(self: JpegImageFile) -> dict[int, Any] | None: - # Extract MP information. This method was inspired by the "highly - # experimental" _getexif version that's been in use for years now, - # itself based on the ImageFileDirectory class in the TIFF plugin. - - # The MP record essentially consists of a TIFF file embedded in a JPEG - # application marker. - try: - data = self.info["mp"] - except KeyError: - return None - file_contents = io.BytesIO(data) - head = file_contents.read(8) - endianness = ">" if head.startswith(b"\x4d\x4d\x00\x2a") else "<" - # process dictionary - from . import TiffImagePlugin - - try: - info = TiffImagePlugin.ImageFileDirectory_v2(head) - file_contents.seek(info.next) - info.load(file_contents) - mp = dict(info) - except Exception as e: - msg = "malformed MP Index (unreadable directory)" - raise SyntaxError(msg) from e - # it's an error not to have a number of images - try: - quant = mp[0xB001] - except KeyError as e: - msg = "malformed MP Index (no number of images)" - raise SyntaxError(msg) from e - # get MP entries - mpentries = [] - try: - rawmpentries = mp[0xB002] - for entrynum in range(quant): - unpackedentry = struct.unpack_from( - f"{endianness}LLLHH", rawmpentries, entrynum * 16 - ) - labels = ("Attribute", "Size", "DataOffset", "EntryNo1", "EntryNo2") - mpentry = dict(zip(labels, unpackedentry)) - mpentryattr = { - "DependentParentImageFlag": bool(mpentry["Attribute"] & (1 << 31)), - "DependentChildImageFlag": bool(mpentry["Attribute"] & (1 << 30)), - "RepresentativeImageFlag": bool(mpentry["Attribute"] & (1 << 29)), - "Reserved": (mpentry["Attribute"] & (3 << 27)) >> 27, - "ImageDataFormat": (mpentry["Attribute"] & (7 << 24)) >> 24, - "MPType": mpentry["Attribute"] & 0x00FFFFFF, - } - if mpentryattr["ImageDataFormat"] == 0: - mpentryattr["ImageDataFormat"] = "JPEG" - else: - msg = "unsupported picture format in MPO" - raise SyntaxError(msg) - mptypemap = { - 0x000000: "Undefined", - 0x010001: "Large Thumbnail (VGA Equivalent)", - 0x010002: "Large Thumbnail (Full HD Equivalent)", - 0x020001: "Multi-Frame Image (Panorama)", - 0x020002: "Multi-Frame Image: (Disparity)", - 0x020003: "Multi-Frame Image: (Multi-Angle)", - 0x030000: "Baseline MP Primary Image", - } - mpentryattr["MPType"] = mptypemap.get(mpentryattr["MPType"], "Unknown") - mpentry["Attribute"] = mpentryattr - mpentries.append(mpentry) - mp[0xB002] = mpentries - except KeyError as e: - msg = "malformed MP Index (bad MP Entry)" - raise SyntaxError(msg) from e - # Next we should try and parse the individual image unique ID list; - # we don't because I've never seen this actually used in a real MPO - # file and so can't test it. - return mp - - -# -------------------------------------------------------------------- -# stuff to save JPEG files - -RAWMODE = { - "1": "L", - "L": "L", - "RGB": "RGB", - "RGBX": "RGB", - "CMYK": "CMYK;I", # assume adobe conventions - "YCbCr": "YCbCr", -} - -# fmt: off -zigzag_index = ( - 0, 1, 5, 6, 14, 15, 27, 28, - 2, 4, 7, 13, 16, 26, 29, 42, - 3, 8, 12, 17, 25, 30, 41, 43, - 9, 11, 18, 24, 31, 40, 44, 53, - 10, 19, 23, 32, 39, 45, 52, 54, - 20, 22, 33, 38, 46, 51, 55, 60, - 21, 34, 37, 47, 50, 56, 59, 61, - 35, 36, 48, 49, 57, 58, 62, 63, -) - -samplings = { - (1, 1, 1, 1, 1, 1): 0, - (2, 1, 1, 1, 1, 1): 1, - (2, 2, 1, 1, 1, 1): 2, -} -# fmt: on - - -def get_sampling(im: Image.Image) -> int: - # There's no subsampling when images have only 1 layer - # (grayscale images) or when they are CMYK (4 layers), - # so set subsampling to the default value. - # - # NOTE: currently Pillow can't encode JPEG to YCCK format. - # If YCCK support is added in the future, subsampling code will have - # to be updated (here and in JpegEncode.c) to deal with 4 layers. - if not isinstance(im, JpegImageFile) or im.layers in (1, 4): - return -1 - sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] - return samplings.get(sampling, -1) - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - try: - rawmode = RAWMODE[im.mode] - except KeyError as e: - msg = f"cannot write mode {im.mode} as JPEG" - raise OSError(msg) from e - - info = im.encoderinfo - - dpi = [round(x) for x in info.get("dpi", (0, 0))] - - quality = info.get("quality", -1) - subsampling = info.get("subsampling", -1) - qtables = info.get("qtables") - - if quality == "keep": - quality = -1 - subsampling = "keep" - qtables = "keep" - elif quality in presets: - preset = presets[quality] - quality = -1 - subsampling = preset.get("subsampling", -1) - qtables = preset.get("quantization") - elif not isinstance(quality, int): - msg = "Invalid quality setting" - raise ValueError(msg) - else: - if subsampling in presets: - subsampling = presets[subsampling].get("subsampling", -1) - if isinstance(qtables, str) and qtables in presets: - qtables = presets[qtables].get("quantization") - - if subsampling == "4:4:4": - subsampling = 0 - elif subsampling == "4:2:2": - subsampling = 1 - elif subsampling == "4:2:0": - subsampling = 2 - elif subsampling == "4:1:1": - # For compatibility. Before Pillow 4.3, 4:1:1 actually meant 4:2:0. - # Set 4:2:0 if someone is still using that value. - subsampling = 2 - elif subsampling == "keep": - if im.format != "JPEG": - msg = "Cannot use 'keep' when original image is not a JPEG" - raise ValueError(msg) - subsampling = get_sampling(im) - - def validate_qtables( - qtables: ( - str | tuple[list[int], ...] | list[list[int]] | dict[int, list[int]] | None - ), - ) -> list[list[int]] | None: - if qtables is None: - return qtables - if isinstance(qtables, str): - try: - lines = [ - int(num) - for line in qtables.splitlines() - for num in line.split("#", 1)[0].split() - ] - except ValueError as e: - msg = "Invalid quantization table" - raise ValueError(msg) from e - else: - qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)] - if isinstance(qtables, (tuple, list, dict)): - if isinstance(qtables, dict): - qtables = [ - qtables[key] for key in range(len(qtables)) if key in qtables - ] - elif isinstance(qtables, tuple): - qtables = list(qtables) - if not (0 < len(qtables) < 5): - msg = "None or too many quantization tables" - raise ValueError(msg) - try: - for idx, table in enumerate(qtables): - if len(table) != 64: - msg = "Invalid quantization table" - raise TypeError(msg) - qtables[idx] = list(array.array("H", table)) - except TypeError as e: - msg = "Invalid quantization table" - raise ValueError(msg) from e - return qtables - - if qtables == "keep": - if im.format != "JPEG": - msg = "Cannot use 'keep' when original image is not a JPEG" - raise ValueError(msg) - qtables = getattr(im, "quantization", None) - qtables = validate_qtables(qtables) - - extra = info.get("extra", b"") - - MAX_BYTES_IN_MARKER = 65533 - if xmp := info.get("xmp"): - overhead_len = 29 # b"http://ns.adobe.com/xap/1.0/\x00" - max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len - if len(xmp) > max_data_bytes_in_marker: - msg = "XMP data is too long" - raise ValueError(msg) - size = o16(2 + overhead_len + len(xmp)) - extra += b"\xff\xe1" + size + b"http://ns.adobe.com/xap/1.0/\x00" + xmp - - if icc_profile := info.get("icc_profile"): - overhead_len = 14 # b"ICC_PROFILE\0" + o8(i) + o8(len(markers)) - max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len - markers = [] - while icc_profile: - markers.append(icc_profile[:max_data_bytes_in_marker]) - icc_profile = icc_profile[max_data_bytes_in_marker:] - i = 1 - for marker in markers: - size = o16(2 + overhead_len + len(marker)) - extra += ( - b"\xff\xe2" - + size - + b"ICC_PROFILE\0" - + o8(i) - + o8(len(markers)) - + marker - ) - i += 1 - - comment = info.get("comment", im.info.get("comment")) - - # "progressive" is the official name, but older documentation - # says "progression" - # FIXME: issue a warning if the wrong form is used (post-1.1.7) - progressive = info.get("progressive", False) or info.get("progression", False) - - optimize = info.get("optimize", False) - - exif = info.get("exif", b"") - if isinstance(exif, Image.Exif): - exif = exif.tobytes() - if len(exif) > MAX_BYTES_IN_MARKER: - msg = "EXIF data is too long" - raise ValueError(msg) - - # get keyword arguments - im.encoderconfig = ( - quality, - progressive, - info.get("smooth", 0), - optimize, - info.get("keep_rgb", False), - info.get("streamtype", 0), - dpi, - subsampling, - info.get("restart_marker_blocks", 0), - info.get("restart_marker_rows", 0), - qtables, - comment, - extra, - exif, - ) - - # if we optimize, libjpeg needs a buffer big enough to hold the whole image - # in a shot. Guessing on the size, at im.size bytes. (raw pixel size is - # channels*size, this is a value that's been used in a django patch. - # https://github.com/matthewwithanm/django-imagekit/issues/50 - if optimize or progressive: - # CMYK can be bigger - if im.mode == "CMYK": - bufsize = 4 * im.size[0] * im.size[1] - # keep sets quality to -1, but the actual value may be high. - elif quality >= 95 or quality == -1: - bufsize = 2 * im.size[0] * im.size[1] - else: - bufsize = im.size[0] * im.size[1] - if exif: - bufsize += len(exif) + 5 - if extra: - bufsize += len(extra) + 1 - else: - # The EXIF info needs to be written as one block, + APP1, + one spare byte. - # Ensure that our buffer is big enough. Same with the icc_profile block. - bufsize = max(len(exif) + 5, len(extra) + 1) - - ImageFile._save( - im, fp, [ImageFile._Tile("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize - ) - - -## -# Factory for making JPEG and MPO instances -def jpeg_factory( - fp: IO[bytes], filename: str | bytes | None = None -) -> JpegImageFile | MpoImageFile: - im = JpegImageFile(fp, filename) - try: - mpheader = im._getmp() - if mpheader is not None and mpheader[45057] > 1: - for segment, content in im.applist: - if segment == "APP1" and b' hdrgm:Version="' in content: - # Ultra HDR images are not yet supported - return im - # It's actually an MPO - from .MpoImagePlugin import MpoImageFile - - # Don't reload everything, just convert it. - im = MpoImageFile.adopt(im, mpheader) - except (TypeError, IndexError): - # It is really a JPEG - pass - except SyntaxError: - warnings.warn( - "Image appears to be a malformed MPO file, it will be " - "interpreted as a base JPEG file" - ) - return im - - -# --------------------------------------------------------------------- -# Registry stuff - -Image.register_open(JpegImageFile.format, jpeg_factory, _accept) -Image.register_save(JpegImageFile.format, _save) - -Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"]) - -Image.register_mime(JpegImageFile.format, "image/jpeg") diff --git a/.venv/lib/python3.12/site-packages/PIL/JpegPresets.py b/.venv/lib/python3.12/site-packages/PIL/JpegPresets.py deleted file mode 100644 index d0e64a35..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/JpegPresets.py +++ /dev/null @@ -1,242 +0,0 @@ -""" -JPEG quality settings equivalent to the Photoshop settings. -Can be used when saving JPEG files. - -The following presets are available by default: -``web_low``, ``web_medium``, ``web_high``, ``web_very_high``, ``web_maximum``, -``low``, ``medium``, ``high``, ``maximum``. -More presets can be added to the :py:data:`presets` dict if needed. - -To apply the preset, specify:: - - quality="preset_name" - -To apply only the quantization table:: - - qtables="preset_name" - -To apply only the subsampling setting:: - - subsampling="preset_name" - -Example:: - - im.save("image_name.jpg", quality="web_high") - -Subsampling ------------ - -Subsampling is the practice of encoding images by implementing less resolution -for chroma information than for luma information. -(ref.: https://en.wikipedia.org/wiki/Chroma_subsampling) - -Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and -4:2:0. - -You can get the subsampling of a JPEG with the -:func:`.JpegImagePlugin.get_sampling` function. - -In JPEG compressed data a JPEG marker is used instead of an EXIF tag. -(ref.: https://exiv2.org/tags.html) - - -Quantization tables -------------------- - -They are values use by the DCT (Discrete cosine transform) to remove -*unnecessary* information from the image (the lossy part of the compression). -(ref.: https://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, -https://en.wikipedia.org/wiki/JPEG#Quantization) - -You can get the quantization tables of a JPEG with:: - - im.quantization - -This will return a dict with a number of lists. You can pass this dict -directly as the qtables argument when saving a JPEG. - -The quantization table format in presets is a list with sublists. These formats -are interchangeable. - -Libjpeg ref.: -https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html - -""" - -from __future__ import annotations - -# fmt: off -presets = { - 'web_low': {'subsampling': 2, # "4:2:0" - 'quantization': [ - [20, 16, 25, 39, 50, 46, 62, 68, - 16, 18, 23, 38, 38, 53, 65, 68, - 25, 23, 31, 38, 53, 65, 68, 68, - 39, 38, 38, 53, 65, 68, 68, 68, - 50, 38, 53, 65, 68, 68, 68, 68, - 46, 53, 65, 68, 68, 68, 68, 68, - 62, 65, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68], - [21, 25, 32, 38, 54, 68, 68, 68, - 25, 28, 24, 38, 54, 68, 68, 68, - 32, 24, 32, 43, 66, 68, 68, 68, - 38, 38, 43, 53, 68, 68, 68, 68, - 54, 54, 66, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68] - ]}, - 'web_medium': {'subsampling': 2, # "4:2:0" - 'quantization': [ - [16, 11, 11, 16, 23, 27, 31, 30, - 11, 12, 12, 15, 20, 23, 23, 30, - 11, 12, 13, 16, 23, 26, 35, 47, - 16, 15, 16, 23, 26, 37, 47, 64, - 23, 20, 23, 26, 39, 51, 64, 64, - 27, 23, 26, 37, 51, 64, 64, 64, - 31, 23, 35, 47, 64, 64, 64, 64, - 30, 30, 47, 64, 64, 64, 64, 64], - [17, 15, 17, 21, 20, 26, 38, 48, - 15, 19, 18, 17, 20, 26, 35, 43, - 17, 18, 20, 22, 26, 30, 46, 53, - 21, 17, 22, 28, 30, 39, 53, 64, - 20, 20, 26, 30, 39, 48, 64, 64, - 26, 26, 30, 39, 48, 63, 64, 64, - 38, 35, 46, 53, 64, 64, 64, 64, - 48, 43, 53, 64, 64, 64, 64, 64] - ]}, - 'web_high': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [6, 4, 4, 6, 9, 11, 12, 16, - 4, 5, 5, 6, 8, 10, 12, 12, - 4, 5, 5, 6, 10, 12, 14, 19, - 6, 6, 6, 11, 12, 15, 19, 28, - 9, 8, 10, 12, 16, 20, 27, 31, - 11, 10, 12, 15, 20, 27, 31, 31, - 12, 12, 14, 19, 27, 31, 31, 31, - 16, 12, 19, 28, 31, 31, 31, 31], - [7, 7, 13, 24, 26, 31, 31, 31, - 7, 12, 16, 21, 31, 31, 31, 31, - 13, 16, 17, 31, 31, 31, 31, 31, - 24, 21, 31, 31, 31, 31, 31, 31, - 26, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31] - ]}, - 'web_very_high': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 4, 5, 7, 9, - 2, 2, 2, 4, 5, 7, 9, 12, - 3, 3, 4, 5, 8, 10, 12, 12, - 4, 4, 5, 7, 10, 12, 12, 12, - 5, 5, 7, 9, 12, 12, 12, 12, - 6, 6, 9, 12, 12, 12, 12, 12], - [3, 3, 5, 9, 13, 15, 15, 15, - 3, 4, 6, 11, 14, 12, 12, 12, - 5, 6, 9, 14, 12, 12, 12, 12, - 9, 11, 14, 12, 12, 12, 12, 12, - 13, 14, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12] - ]}, - 'web_maximum': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 2, - 1, 1, 1, 1, 1, 1, 2, 2, - 1, 1, 1, 1, 1, 2, 2, 3, - 1, 1, 1, 1, 2, 2, 3, 3, - 1, 1, 1, 2, 2, 3, 3, 3, - 1, 1, 2, 2, 3, 3, 3, 3], - [1, 1, 1, 2, 2, 3, 3, 3, - 1, 1, 1, 2, 3, 3, 3, 3, - 1, 1, 1, 3, 3, 3, 3, 3, - 2, 2, 3, 3, 3, 3, 3, 3, - 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3] - ]}, - 'low': {'subsampling': 2, # "4:2:0" - 'quantization': [ - [18, 14, 14, 21, 30, 35, 34, 17, - 14, 16, 16, 19, 26, 23, 12, 12, - 14, 16, 17, 21, 23, 12, 12, 12, - 21, 19, 21, 23, 12, 12, 12, 12, - 30, 26, 23, 12, 12, 12, 12, 12, - 35, 23, 12, 12, 12, 12, 12, 12, - 34, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12], - [20, 19, 22, 27, 20, 20, 17, 17, - 19, 25, 23, 14, 14, 12, 12, 12, - 22, 23, 14, 14, 12, 12, 12, 12, - 27, 14, 14, 12, 12, 12, 12, 12, - 20, 14, 12, 12, 12, 12, 12, 12, - 20, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12] - ]}, - 'medium': {'subsampling': 2, # "4:2:0" - 'quantization': [ - [12, 8, 8, 12, 17, 21, 24, 17, - 8, 9, 9, 11, 15, 19, 12, 12, - 8, 9, 10, 12, 19, 12, 12, 12, - 12, 11, 12, 21, 12, 12, 12, 12, - 17, 15, 19, 12, 12, 12, 12, 12, - 21, 19, 12, 12, 12, 12, 12, 12, - 24, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12], - [13, 11, 13, 16, 20, 20, 17, 17, - 11, 14, 14, 14, 14, 12, 12, 12, - 13, 14, 14, 14, 12, 12, 12, 12, - 16, 14, 14, 12, 12, 12, 12, 12, - 20, 14, 12, 12, 12, 12, 12, 12, - 20, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12] - ]}, - 'high': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [6, 4, 4, 6, 9, 11, 12, 16, - 4, 5, 5, 6, 8, 10, 12, 12, - 4, 5, 5, 6, 10, 12, 12, 12, - 6, 6, 6, 11, 12, 12, 12, 12, - 9, 8, 10, 12, 12, 12, 12, 12, - 11, 10, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, - 16, 12, 12, 12, 12, 12, 12, 12], - [7, 7, 13, 24, 20, 20, 17, 17, - 7, 12, 16, 14, 14, 12, 12, 12, - 13, 16, 14, 14, 12, 12, 12, 12, - 24, 14, 14, 12, 12, 12, 12, 12, - 20, 14, 12, 12, 12, 12, 12, 12, - 20, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12] - ]}, - 'maximum': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 4, 5, 7, 9, - 2, 2, 2, 4, 5, 7, 9, 12, - 3, 3, 4, 5, 8, 10, 12, 12, - 4, 4, 5, 7, 10, 12, 12, 12, - 5, 5, 7, 9, 12, 12, 12, 12, - 6, 6, 9, 12, 12, 12, 12, 12], - [3, 3, 5, 9, 13, 15, 15, 15, - 3, 4, 6, 10, 14, 12, 12, 12, - 5, 6, 9, 14, 12, 12, 12, 12, - 9, 10, 14, 12, 12, 12, 12, 12, - 13, 14, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12] - ]}, -} -# fmt: on diff --git a/.venv/lib/python3.12/site-packages/PIL/McIdasImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/McIdasImagePlugin.py deleted file mode 100644 index 9a47933b..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/McIdasImagePlugin.py +++ /dev/null @@ -1,78 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Basic McIdas support for PIL -# -# History: -# 1997-05-05 fl Created (8-bit images only) -# 2009-03-08 fl Added 16/32-bit support. -# -# Thanks to Richard Jones and Craig Swank for specs and samples. -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import struct - -from . import Image, ImageFile - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"\x00\x00\x00\x00\x00\x00\x00\x04") - - -## -# Image plugin for McIdas area images. - - -class McIdasImageFile(ImageFile.ImageFile): - format = "MCIDAS" - format_description = "McIdas area file" - - def _open(self) -> None: - # parse area file directory - assert self.fp is not None - - s = self.fp.read(256) - if not _accept(s) or len(s) != 256: - msg = "not an McIdas area file" - raise SyntaxError(msg) - - self.area_descriptor_raw = s - self.area_descriptor = w = [0, *struct.unpack("!64i", s)] - - # get mode - if w[11] == 1: - mode = rawmode = "L" - elif w[11] == 2: - mode = rawmode = "I;16B" - elif w[11] == 4: - # FIXME: add memory map support - mode = "I" - rawmode = "I;32B" - else: - msg = "unsupported McIdas format" - raise SyntaxError(msg) - - self._mode = mode - self._size = w[10], w[9] - - offset = w[34] + w[15] - stride = w[15] + w[10] * w[11] * w[14] - - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, offset, (rawmode, stride, 1)) - ] - - -# -------------------------------------------------------------------- -# registry - -Image.register_open(McIdasImageFile.format, McIdasImageFile, _accept) - -# no default extension diff --git a/.venv/lib/python3.12/site-packages/PIL/MicImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/MicImagePlugin.py deleted file mode 100644 index 99a07bae..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/MicImagePlugin.py +++ /dev/null @@ -1,103 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Microsoft Image Composer support for PIL -# -# Notes: -# uses TiffImagePlugin.py to read the actual image streams -# -# History: -# 97-01-20 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import olefile - -from . import Image, TiffImagePlugin - -# -# -------------------------------------------------------------------- - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(olefile.MAGIC) - - -## -# Image plugin for Microsoft's Image Composer file format. - - -class MicImageFile(TiffImagePlugin.TiffImageFile): - format = "MIC" - format_description = "Microsoft Image Composer" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - # read the OLE directory and see if this is a likely - # to be a Microsoft Image Composer file - - try: - self.ole = olefile.OleFileIO(self.fp) - except OSError as e: - msg = "not an MIC file; invalid OLE file" - raise SyntaxError(msg) from e - - # find ACI subfiles with Image members (maybe not the - # best way to identify MIC files, but what the... ;-) - - self.images = [ - path - for path in self.ole.listdir() - if path[1:] and path[0].endswith(".ACI") and path[1] == "Image" - ] - - # if we didn't find any images, this is probably not - # an MIC file. - if not self.images: - msg = "not an MIC file; no image entries" - raise SyntaxError(msg) - - self.frame = -1 - self._n_frames = len(self.images) - self.is_animated = self._n_frames > 1 - - assert self.fp is not None - self.__fp = self.fp - self.seek(0) - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - filename = self.images[frame] - self.fp = self.ole.openstream(filename) - - TiffImagePlugin.TiffImageFile._open(self) - - self.frame = frame - - def tell(self) -> int: - return self.frame - - def close(self) -> None: - self.__fp.close() - self.ole.close() - super().close() - - def __exit__(self, *args: object) -> None: - self.__fp.close() - self.ole.close() - super().__exit__() - - -# -# -------------------------------------------------------------------- - -Image.register_open(MicImageFile.format, MicImageFile, _accept) - -Image.register_extension(MicImageFile.format, ".mic") diff --git a/.venv/lib/python3.12/site-packages/PIL/MpegImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/MpegImagePlugin.py deleted file mode 100644 index 47ebe9d6..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/MpegImagePlugin.py +++ /dev/null @@ -1,84 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# MPEG file handling -# -# History: -# 95-09-09 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1995. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile -from ._binary import i8 -from ._typing import SupportsRead - -# -# Bitstream parser - - -class BitStream: - def __init__(self, fp: SupportsRead[bytes]) -> None: - self.fp = fp - self.bits = 0 - self.bitbuffer = 0 - - def next(self) -> int: - return i8(self.fp.read(1)) - - def peek(self, bits: int) -> int: - while self.bits < bits: - self.bitbuffer = (self.bitbuffer << 8) + self.next() - self.bits += 8 - return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1 - - def skip(self, bits: int) -> None: - while self.bits < bits: - self.bitbuffer = (self.bitbuffer << 8) + i8(self.fp.read(1)) - self.bits += 8 - self.bits = self.bits - bits - - def read(self, bits: int) -> int: - v = self.peek(bits) - self.bits = self.bits - bits - return v - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"\x00\x00\x01\xb3") - - -## -# Image plugin for MPEG streams. This plugin can identify a stream, -# but it cannot read it. - - -class MpegImageFile(ImageFile.ImageFile): - format = "MPEG" - format_description = "MPEG" - - def _open(self) -> None: - assert self.fp is not None - - s = BitStream(self.fp) - if s.read(32) != 0x1B3: - msg = "not an MPEG file" - raise SyntaxError(msg) - - self._mode = "RGB" - self._size = s.read(12), s.read(12) - - -# -------------------------------------------------------------------- -# Registry stuff - -Image.register_open(MpegImageFile.format, MpegImageFile, _accept) - -Image.register_extensions(MpegImageFile.format, [".mpg", ".mpeg"]) - -Image.register_mime(MpegImageFile.format, "video/mpeg") diff --git a/.venv/lib/python3.12/site-packages/PIL/MpoImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/MpoImagePlugin.py deleted file mode 100644 index bee0a56f..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/MpoImagePlugin.py +++ /dev/null @@ -1,203 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# MPO file handling -# -# See "Multi-Picture Format" (CIPA DC-007-Translation 2009, Standard of the -# Camera & Imaging Products Association) -# -# The multi-picture object combines multiple JPEG images (with a modified EXIF -# data format) into a single file. While it can theoretically be used much like -# a GIF animation, it is commonly used to represent 3D photographs and is (as -# of this writing) the most commonly used format by 3D cameras. -# -# History: -# 2014-03-13 Feneric Created -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -import struct -from typing import IO, Any, cast - -from . import ( - Image, - ImageFile, - ImageSequence, - JpegImagePlugin, - TiffImagePlugin, -) -from ._binary import o32le -from ._util import DeferredError - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - JpegImagePlugin._save(im, fp, filename) - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - append_images = im.encoderinfo.get("append_images", []) - if not append_images and not getattr(im, "is_animated", False): - _save(im, fp, filename) - return - - mpf_offset = 28 - offsets: list[int] = [] - im_sequences = [im, *append_images] - total = sum(getattr(seq, "n_frames", 1) for seq in im_sequences) - for im_sequence in im_sequences: - for im_frame in ImageSequence.Iterator(im_sequence): - if not offsets: - # APP2 marker - ifd_length = 66 + 16 * total - im_frame.encoderinfo["extra"] = ( - b"\xff\xe2" - + struct.pack(">H", 6 + ifd_length) - + b"MPF\0" - + b" " * ifd_length - ) - if exif := im_frame.encoderinfo.get("exif"): - if isinstance(exif, Image.Exif): - exif = exif.tobytes() - im_frame.encoderinfo["exif"] = exif - mpf_offset += 4 + len(exif) - - JpegImagePlugin._save(im_frame, fp, filename) - offsets.append(fp.tell()) - else: - encoderinfo = im_frame._attach_default_encoderinfo(im) - im_frame.save(fp, "JPEG") - im_frame.encoderinfo = encoderinfo - offsets.append(fp.tell() - offsets[-1]) - - ifd = TiffImagePlugin.ImageFileDirectory_v2() - ifd[0xB000] = b"0100" - ifd[0xB001] = len(offsets) - - mpentries = b"" - data_offset = 0 - for i, size in enumerate(offsets): - if i == 0: - mptype = 0x030000 # Baseline MP Primary Image - else: - mptype = 0x000000 # Undefined - mpentries += struct.pack(" None: - assert self.fp is not None - self.fp.seek(0) # prep the fp in order to pass the JPEG test - JpegImagePlugin.JpegImageFile._open(self) - self._after_jpeg_open() - - def _after_jpeg_open(self, mpheader: dict[int, Any] | None = None) -> None: - self.mpinfo = mpheader if mpheader is not None else self._getmp() - if self.mpinfo is None: - msg = "Image appears to be a malformed MPO file" - raise ValueError(msg) - self.n_frames = self.mpinfo[0xB001] - self.__mpoffsets = [ - mpent["DataOffset"] + self.info["mpoffset"] for mpent in self.mpinfo[0xB002] - ] - self.__mpoffsets[0] = 0 - # Note that the following assertion will only be invalid if something - # gets broken within JpegImagePlugin. - assert self.n_frames == len(self.__mpoffsets) - del self.info["mpoffset"] # no longer needed - self.is_animated = self.n_frames > 1 - assert self.fp is not None - self._fp = self.fp # FIXME: hack - self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame - self.__frame = 0 - self.offset = 0 - # for now we can only handle reading and individual frame extraction - self.readonly = 1 - - def load_seek(self, pos: int) -> None: - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self._fp.seek(pos) - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self.fp = self._fp - self.offset = self.__mpoffsets[frame] - - original_exif = self.info.get("exif") - if "exif" in self.info: - del self.info["exif"] - - self.fp.seek(self.offset + 2) # skip SOI marker - if not self.fp.read(2): - msg = "No data found for frame" - raise ValueError(msg) - self.fp.seek(self.offset) - JpegImagePlugin.JpegImageFile._open(self) - if self.info.get("exif") != original_exif: - self._reload_exif() - - self.tile = [ - ImageFile._Tile("jpeg", (0, 0) + self.size, self.offset, self.tile[0][-1]) - ] - self.__frame = frame - - def tell(self) -> int: - return self.__frame - - @staticmethod - def adopt( - jpeg_instance: JpegImagePlugin.JpegImageFile, - mpheader: dict[int, Any] | None = None, - ) -> MpoImageFile: - """ - Transform the instance of JpegImageFile into - an instance of MpoImageFile. - After the call, the JpegImageFile is extended - to be an MpoImageFile. - - This is essentially useful when opening a JPEG - file that reveals itself as an MPO, to avoid - double call to _open. - """ - jpeg_instance.__class__ = MpoImageFile - mpo_instance = cast(MpoImageFile, jpeg_instance) - mpo_instance._after_jpeg_open(mpheader) - return mpo_instance - - -# --------------------------------------------------------------------- -# Registry stuff - -# Note that since MPO shares a factory with JPEG, we do not need to do a -# separate registration for it here. -# Image.register_open(MpoImageFile.format, -# JpegImagePlugin.jpeg_factory, _accept) -Image.register_save(MpoImageFile.format, _save) -Image.register_save_all(MpoImageFile.format, _save_all) - -Image.register_extension(MpoImageFile.format, ".mpo") - -Image.register_mime(MpoImageFile.format, "image/mpo") diff --git a/.venv/lib/python3.12/site-packages/PIL/MspImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/MspImagePlugin.py deleted file mode 100644 index fa0f52fe..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/MspImagePlugin.py +++ /dev/null @@ -1,200 +0,0 @@ -# -# The Python Imaging Library. -# -# MSP file handling -# -# This is the format used by the Paint program in Windows 1 and 2. -# -# History: -# 95-09-05 fl Created -# 97-01-03 fl Read/write MSP images -# 17-02-21 es Fixed RLE interpretation -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1995-97. -# Copyright (c) Eric Soroos 2017. -# -# See the README file for information on usage and redistribution. -# -# More info on this format: https://archive.org/details/gg243631 -# Page 313: -# Figure 205. Windows Paint Version 1: "DanM" Format -# Figure 206. Windows Paint Version 2: "LinS" Format. Used in Windows V2.03 -# -# See also: https://www.fileformat.info/format/mspaint/egff.htm -from __future__ import annotations - -import io -import struct -from typing import IO - -from . import Image, ImageFile -from ._binary import i16le as i16 -from ._binary import o16le as o16 - -# -# read MSP files - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith((b"DanM", b"LinS")) - - -## -# Image plugin for Windows MSP images. This plugin supports both -# uncompressed (Windows 1.0). - - -class MspImageFile(ImageFile.ImageFile): - format = "MSP" - format_description = "Windows Paint" - - def _open(self) -> None: - # Header - assert self.fp is not None - - s = self.fp.read(32) - if not _accept(s): - msg = "not an MSP file" - raise SyntaxError(msg) - - # Header checksum - checksum = 0 - for i in range(0, 32, 2): - checksum = checksum ^ i16(s, i) - if checksum != 0: - msg = "bad MSP checksum" - raise SyntaxError(msg) - - self._mode = "1" - self._size = i16(s, 4), i16(s, 6) - - if s.startswith(b"DanM"): - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 32, "1")] - else: - self.tile = [ImageFile._Tile("MSP", (0, 0) + self.size, 32)] - - -class MspDecoder(ImageFile.PyDecoder): - # The algo for the MSP decoder is from - # https://www.fileformat.info/format/mspaint/egff.htm - # cc-by-attribution -- That page references is taken from the - # Encyclopedia of Graphics File Formats and is licensed by - # O'Reilly under the Creative Common/Attribution license - # - # For RLE encoded files, the 32byte header is followed by a scan - # line map, encoded as one 16bit word of encoded byte length per - # line. - # - # NOTE: the encoded length of the line can be 0. This was not - # handled in the previous version of this encoder, and there's no - # mention of how to handle it in the documentation. From the few - # examples I've seen, I've assumed that it is a fill of the - # background color, in this case, white. - # - # - # Pseudocode of the decoder: - # Read a BYTE value as the RunType - # If the RunType value is zero - # Read next byte as the RunCount - # Read the next byte as the RunValue - # Write the RunValue byte RunCount times - # If the RunType value is non-zero - # Use this value as the RunCount - # Read and write the next RunCount bytes literally - # - # e.g.: - # 0x00 03 ff 05 00 01 02 03 04 - # would yield the bytes: - # 0xff ff ff 00 01 02 03 04 - # - # which are then interpreted as a bit packed mode '1' image - - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - - img = io.BytesIO() - blank_line = bytearray((0xFF,) * ((self.state.xsize + 7) // 8)) - try: - self.fd.seek(32) - rowmap = struct.unpack_from( - f"<{self.state.ysize}H", self.fd.read(self.state.ysize * 2) - ) - except struct.error as e: - msg = "Truncated MSP file in row map" - raise OSError(msg) from e - - for x, rowlen in enumerate(rowmap): - try: - if rowlen == 0: - img.write(blank_line) - continue - row = self.fd.read(rowlen) - if len(row) != rowlen: - msg = f"Truncated MSP file, expected {rowlen} bytes on row {x}" - raise OSError(msg) - idx = 0 - while idx < rowlen: - runtype = row[idx] - idx += 1 - if runtype == 0: - runcount, runval = struct.unpack_from("Bc", row, idx) - img.write(runval * runcount) - idx += 2 - else: - runcount = runtype - img.write(row[idx : idx + runcount]) - idx += runcount - - except struct.error as e: - msg = f"Corrupted MSP file in row {x}" - raise OSError(msg) from e - - self.set_as_raw(img.getvalue(), "1") - - return -1, 0 - - -Image.register_decoder("MSP", MspDecoder) - - -# -# write MSP files (uncompressed only) - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode != "1": - msg = f"cannot write mode {im.mode} as MSP" - raise OSError(msg) - - # create MSP header - header = [0] * 16 - - header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1 - header[2], header[3] = im.size - header[4], header[5] = 1, 1 - header[6], header[7] = 1, 1 - header[8], header[9] = im.size - - checksum = 0 - for h in header: - checksum = checksum ^ h - header[12] = checksum # FIXME: is this the right field? - - # header - for h in header: - fp.write(o16(h)) - - # image body - ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 32, "1")]) - - -# -# registry - -Image.register_open(MspImageFile.format, MspImageFile, _accept) -Image.register_save(MspImageFile.format, _save) - -Image.register_extension(MspImageFile.format, ".msp") diff --git a/.venv/lib/python3.12/site-packages/PIL/PSDraw.py b/.venv/lib/python3.12/site-packages/PIL/PSDraw.py deleted file mode 100644 index e6b74a91..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PSDraw.py +++ /dev/null @@ -1,238 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# Simple PostScript graphics interface -# -# History: -# 1996-04-20 fl Created -# 1999-01-10 fl Added gsave/grestore to image method -# 2005-05-04 fl Fixed floating point issue in image (from Eric Etheridge) -# -# Copyright (c) 1997-2005 by Secret Labs AB. All rights reserved. -# Copyright (c) 1996 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import sys -from typing import IO - -from . import EpsImagePlugin - -TYPE_CHECKING = False - - -## -# Simple PostScript graphics interface. - - -class PSDraw: - """ - Sets up printing to the given file. If ``fp`` is omitted, - ``sys.stdout.buffer`` is assumed. - """ - - def __init__(self, fp: IO[bytes] | None = None) -> None: - if not fp: - fp = sys.stdout.buffer - self.fp = fp - - def begin_document(self, id: str | None = None) -> None: - """Set up printing of a document. (Write PostScript DSC header.)""" - # FIXME: incomplete - self.fp.write( - b"%!PS-Adobe-3.0\n" - b"save\n" - b"/showpage { } def\n" - b"%%EndComments\n" - b"%%BeginDocument\n" - ) - # self.fp.write(ERROR_PS) # debugging! - self.fp.write(EDROFF_PS) - self.fp.write(VDI_PS) - self.fp.write(b"%%EndProlog\n") - self.isofont: dict[bytes, int] = {} - - def end_document(self) -> None: - """Ends printing. (Write PostScript DSC footer.)""" - self.fp.write(b"%%EndDocument\nrestore showpage\n%%End\n") - if hasattr(self.fp, "flush"): - self.fp.flush() - - def setfont(self, font: str, size: int) -> None: - """ - Selects which font to use. - - :param font: A PostScript font name - :param size: Size in points. - """ - font_bytes = bytes(font, "UTF-8") - if font_bytes not in self.isofont: - # reencode font - self.fp.write( - b"/PSDraw-%s ISOLatin1Encoding /%s E\n" % (font_bytes, font_bytes) - ) - self.isofont[font_bytes] = 1 - # rough - self.fp.write(b"/F0 %d /PSDraw-%s F\n" % (size, font_bytes)) - - def line(self, xy0: tuple[int, int], xy1: tuple[int, int]) -> None: - """ - Draws a line between the two points. Coordinates are given in - PostScript point coordinates (72 points per inch, (0, 0) is the lower - left corner of the page). - """ - self.fp.write(b"%d %d %d %d Vl\n" % (*xy0, *xy1)) - - def rectangle(self, box: tuple[int, int, int, int]) -> None: - """ - Draws a rectangle. - - :param box: A tuple of four integers, specifying left, bottom, width and - height. - """ - self.fp.write(b"%d %d M 0 %d %d Vr\n" % box) - - def text(self, xy: tuple[int, int], text: str) -> None: - """ - Draws text at the given position. You must use - :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. - """ - # The font is loaded as ISOLatin1Encoding, so use latin-1 here. - text_bytes = bytes(text, "latin-1") - text_bytes = b"\\(".join(text_bytes.split(b"(")) - text_bytes = b"\\)".join(text_bytes.split(b")")) - self.fp.write(b"%d %d M (%s) S\n" % (xy + (text_bytes,))) - - if TYPE_CHECKING: - from . import Image - - def image( - self, box: tuple[int, int, int, int], im: Image.Image, dpi: int | None = None - ) -> None: - """Draw a PIL image, centered in the given box.""" - # default resolution depends on mode - if not dpi: - if im.mode == "1": - dpi = 200 # fax - else: - dpi = 100 # grayscale - # image size (on paper) - x = im.size[0] * 72 / dpi - y = im.size[1] * 72 / dpi - # max allowed size - xmax = float(box[2] - box[0]) - ymax = float(box[3] - box[1]) - if x > xmax: - y = y * xmax / x - x = xmax - if y > ymax: - x = x * ymax / y - y = ymax - dx = (xmax - x) / 2 + box[0] - dy = (ymax - y) / 2 + box[1] - self.fp.write(b"gsave\n%f %f translate\n" % (dx, dy)) - if (x, y) != im.size: - # EpsImagePlugin._save prints the image at (0,0,xsize,ysize) - sx = x / im.size[0] - sy = y / im.size[1] - self.fp.write(b"%f %f scale\n" % (sx, sy)) - EpsImagePlugin._save(im, self.fp, "", 0) - self.fp.write(b"\ngrestore\n") - - -# -------------------------------------------------------------------- -# PostScript driver - -# -# EDROFF.PS -- PostScript driver for Edroff 2 -# -# History: -# 94-01-25 fl: created (edroff 2.04) -# -# Copyright (c) Fredrik Lundh 1994. -# - - -EDROFF_PS = b"""\ -/S { show } bind def -/P { moveto show } bind def -/M { moveto } bind def -/X { 0 rmoveto } bind def -/Y { 0 exch rmoveto } bind def -/E { findfont - dup maxlength dict begin - { - 1 index /FID ne { def } { pop pop } ifelse - } forall - /Encoding exch def - dup /FontName exch def - currentdict end definefont pop -} bind def -/F { findfont exch scalefont dup setfont - [ exch /setfont cvx ] cvx bind def -} bind def -""" - -# -# VDI.PS -- PostScript driver for VDI meta commands -# -# History: -# 94-01-25 fl: created (edroff 2.04) -# -# Copyright (c) Fredrik Lundh 1994. -# - -VDI_PS = b"""\ -/Vm { moveto } bind def -/Va { newpath arcn stroke } bind def -/Vl { moveto lineto stroke } bind def -/Vc { newpath 0 360 arc closepath } bind def -/Vr { exch dup 0 rlineto - exch dup 0 exch rlineto - exch neg 0 rlineto - 0 exch neg rlineto - setgray fill } bind def -/Tm matrix def -/Ve { Tm currentmatrix pop - translate scale newpath 0 0 .5 0 360 arc closepath - Tm setmatrix -} bind def -/Vf { currentgray exch setgray fill setgray } bind def -""" - -# -# ERROR.PS -- Error handler -# -# History: -# 89-11-21 fl: created (pslist 1.10) -# - -ERROR_PS = b"""\ -/landscape false def -/errorBUF 200 string def -/errorNL { currentpoint 10 sub exch pop 72 exch moveto } def -errordict begin /handleerror { - initmatrix /Courier findfont 10 scalefont setfont - newpath 72 720 moveto $error begin /newerror false def - (PostScript Error) show errorNL errorNL - (Error: ) show - /errorname load errorBUF cvs show errorNL errorNL - (Command: ) show - /command load dup type /stringtype ne { errorBUF cvs } if show - errorNL errorNL - (VMstatus: ) show - vmstatus errorBUF cvs show ( bytes available, ) show - errorBUF cvs show ( bytes used at level ) show - errorBUF cvs show errorNL errorNL - (Operand stargck: ) show errorNL /ostargck load { - dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL - } forall errorNL - (Execution stargck: ) show errorNL /estargck load { - dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL - } forall - end showpage -} def end -""" diff --git a/.venv/lib/python3.12/site-packages/PIL/PaletteFile.py b/.venv/lib/python3.12/site-packages/PIL/PaletteFile.py deleted file mode 100644 index 2a26e5d4..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PaletteFile.py +++ /dev/null @@ -1,54 +0,0 @@ -# -# Python Imaging Library -# $Id$ -# -# stuff to read simple, teragon-style palette files -# -# History: -# 97-08-23 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from typing import IO - -from ._binary import o8 - - -class PaletteFile: - """File handler for Teragon-style palette files.""" - - rawmode = "RGB" - - def __init__(self, fp: IO[bytes]) -> None: - palette = [o8(i) * 3 for i in range(256)] - - while True: - s = fp.readline() - - if not s: - break - if s.startswith(b"#"): - continue - if len(s) > 100: - msg = "bad palette file" - raise SyntaxError(msg) - - v = [int(x) for x in s.split()] - try: - [i, r, g, b] = v - except ValueError: - [i, r] = v - g = b = r - - if 0 <= i <= 255: - palette[i] = o8(r) + o8(g) + o8(b) - - self.palette = b"".join(palette) - - def getpalette(self) -> tuple[bytes, str]: - return self.palette, self.rawmode diff --git a/.venv/lib/python3.12/site-packages/PIL/PalmImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/PalmImagePlugin.py deleted file mode 100644 index 232adf3d..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PalmImagePlugin.py +++ /dev/null @@ -1,217 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# - -## -# Image plugin for Palm pixmap images (output only). -## -from __future__ import annotations - -from typing import IO - -from . import Image, ImageFile -from ._binary import o8 -from ._binary import o16be as o16b - -# fmt: off -_Palm8BitColormapValues = ( - (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255), - (255, 51, 255), (255, 0, 255), (255, 255, 204), (255, 204, 204), - (255, 153, 204), (255, 102, 204), (255, 51, 204), (255, 0, 204), - (255, 255, 153), (255, 204, 153), (255, 153, 153), (255, 102, 153), - (255, 51, 153), (255, 0, 153), (204, 255, 255), (204, 204, 255), - (204, 153, 255), (204, 102, 255), (204, 51, 255), (204, 0, 255), - (204, 255, 204), (204, 204, 204), (204, 153, 204), (204, 102, 204), - (204, 51, 204), (204, 0, 204), (204, 255, 153), (204, 204, 153), - (204, 153, 153), (204, 102, 153), (204, 51, 153), (204, 0, 153), - (153, 255, 255), (153, 204, 255), (153, 153, 255), (153, 102, 255), - (153, 51, 255), (153, 0, 255), (153, 255, 204), (153, 204, 204), - (153, 153, 204), (153, 102, 204), (153, 51, 204), (153, 0, 204), - (153, 255, 153), (153, 204, 153), (153, 153, 153), (153, 102, 153), - (153, 51, 153), (153, 0, 153), (102, 255, 255), (102, 204, 255), - (102, 153, 255), (102, 102, 255), (102, 51, 255), (102, 0, 255), - (102, 255, 204), (102, 204, 204), (102, 153, 204), (102, 102, 204), - (102, 51, 204), (102, 0, 204), (102, 255, 153), (102, 204, 153), - (102, 153, 153), (102, 102, 153), (102, 51, 153), (102, 0, 153), - (51, 255, 255), (51, 204, 255), (51, 153, 255), (51, 102, 255), - (51, 51, 255), (51, 0, 255), (51, 255, 204), (51, 204, 204), - (51, 153, 204), (51, 102, 204), (51, 51, 204), (51, 0, 204), - (51, 255, 153), (51, 204, 153), (51, 153, 153), (51, 102, 153), - (51, 51, 153), (51, 0, 153), (0, 255, 255), (0, 204, 255), - (0, 153, 255), (0, 102, 255), (0, 51, 255), (0, 0, 255), - (0, 255, 204), (0, 204, 204), (0, 153, 204), (0, 102, 204), - (0, 51, 204), (0, 0, 204), (0, 255, 153), (0, 204, 153), - (0, 153, 153), (0, 102, 153), (0, 51, 153), (0, 0, 153), - (255, 255, 102), (255, 204, 102), (255, 153, 102), (255, 102, 102), - (255, 51, 102), (255, 0, 102), (255, 255, 51), (255, 204, 51), - (255, 153, 51), (255, 102, 51), (255, 51, 51), (255, 0, 51), - (255, 255, 0), (255, 204, 0), (255, 153, 0), (255, 102, 0), - (255, 51, 0), (255, 0, 0), (204, 255, 102), (204, 204, 102), - (204, 153, 102), (204, 102, 102), (204, 51, 102), (204, 0, 102), - (204, 255, 51), (204, 204, 51), (204, 153, 51), (204, 102, 51), - (204, 51, 51), (204, 0, 51), (204, 255, 0), (204, 204, 0), - (204, 153, 0), (204, 102, 0), (204, 51, 0), (204, 0, 0), - (153, 255, 102), (153, 204, 102), (153, 153, 102), (153, 102, 102), - (153, 51, 102), (153, 0, 102), (153, 255, 51), (153, 204, 51), - (153, 153, 51), (153, 102, 51), (153, 51, 51), (153, 0, 51), - (153, 255, 0), (153, 204, 0), (153, 153, 0), (153, 102, 0), - (153, 51, 0), (153, 0, 0), (102, 255, 102), (102, 204, 102), - (102, 153, 102), (102, 102, 102), (102, 51, 102), (102, 0, 102), - (102, 255, 51), (102, 204, 51), (102, 153, 51), (102, 102, 51), - (102, 51, 51), (102, 0, 51), (102, 255, 0), (102, 204, 0), - (102, 153, 0), (102, 102, 0), (102, 51, 0), (102, 0, 0), - (51, 255, 102), (51, 204, 102), (51, 153, 102), (51, 102, 102), - (51, 51, 102), (51, 0, 102), (51, 255, 51), (51, 204, 51), - (51, 153, 51), (51, 102, 51), (51, 51, 51), (51, 0, 51), - (51, 255, 0), (51, 204, 0), (51, 153, 0), (51, 102, 0), - (51, 51, 0), (51, 0, 0), (0, 255, 102), (0, 204, 102), - (0, 153, 102), (0, 102, 102), (0, 51, 102), (0, 0, 102), - (0, 255, 51), (0, 204, 51), (0, 153, 51), (0, 102, 51), - (0, 51, 51), (0, 0, 51), (0, 255, 0), (0, 204, 0), - (0, 153, 0), (0, 102, 0), (0, 51, 0), (17, 17, 17), - (34, 34, 34), (68, 68, 68), (85, 85, 85), (119, 119, 119), - (136, 136, 136), (170, 170, 170), (187, 187, 187), (221, 221, 221), - (238, 238, 238), (192, 192, 192), (128, 0, 0), (128, 0, 128), - (0, 128, 0), (0, 128, 128), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)) -# fmt: on - - -# so build a prototype image to be used for palette resampling -def build_prototype_image() -> Image.Image: - image = Image.new("L", (1, len(_Palm8BitColormapValues))) - image.putdata(list(range(len(_Palm8BitColormapValues)))) - palettedata: tuple[int, ...] = () - for colormapValue in _Palm8BitColormapValues: - palettedata += colormapValue - palettedata += (0, 0, 0) * (256 - len(_Palm8BitColormapValues)) - image.putpalette(palettedata) - return image - - -Palm8BitColormapImage = build_prototype_image() - -# OK, we now have in Palm8BitColormapImage, -# a "P"-mode image with the right palette -# -# -------------------------------------------------------------------- - -_FLAGS = {"custom-colormap": 0x4000, "is-compressed": 0x8000, "has-transparent": 0x2000} - -_COMPRESSION_TYPES = {"none": 0xFF, "rle": 0x01, "scanline": 0x00} - - -# -# -------------------------------------------------------------------- - -## -# (Internal) Image save plugin for the Palm format. - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode == "P": - rawmode = "P" - bpp = 8 - version = 1 - - elif im.mode == "L": - if im.encoderinfo.get("bpp") in (1, 2, 4): - # this is 8-bit grayscale, so we shift it to get the high-order bits, - # and invert it because - # Palm does grayscale from white (0) to black (1) - bpp = im.encoderinfo["bpp"] - maxval = (1 << bpp) - 1 - shift = 8 - bpp - im = im.point(lambda x: maxval - (x >> shift)) - elif im.info.get("bpp") in (1, 2, 4): - # here we assume that even though the inherent mode is 8-bit grayscale, - # only the lower bpp bits are significant. - # We invert them to match the Palm. - bpp = im.info["bpp"] - maxval = (1 << bpp) - 1 - im = im.point(lambda x: maxval - (x & maxval)) - else: - msg = f"cannot write mode {im.mode} as Palm" - raise OSError(msg) - - # we ignore the palette here - im._mode = "P" - rawmode = f"P;{bpp}" - version = 1 - - elif im.mode == "1": - # monochrome -- write it inverted, as is the Palm standard - rawmode = "1;I" - bpp = 1 - version = 0 - - else: - msg = f"cannot write mode {im.mode} as Palm" - raise OSError(msg) - - # - # make sure image data is available - im.load() - - # write header - - cols = im.size[0] - rows = im.size[1] - - rowbytes = int((cols + (16 // bpp - 1)) / (16 // bpp)) * 2 - transparent_index = 0 - compression_type = _COMPRESSION_TYPES["none"] - - flags = 0 - if im.mode == "P": - flags |= _FLAGS["custom-colormap"] - colormap = im.im.getpalette() - colors = len(colormap) // 3 - colormapsize = 4 * colors + 2 - else: - colormapsize = 0 - - if "offset" in im.info: - offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4 - else: - offset = 0 - - fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags)) - fp.write(o8(bpp)) - fp.write(o8(version)) - fp.write(o16b(offset)) - fp.write(o8(transparent_index)) - fp.write(o8(compression_type)) - fp.write(o16b(0)) # reserved by Palm - - # now write colormap if necessary - - if colormapsize: - fp.write(o16b(colors)) - for i in range(colors): - fp.write(o8(i)) - fp.write(colormap[3 * i : 3 * i + 3]) - - # now convert data to raw form - ImageFile._save( - im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, rowbytes, 1))] - ) - - if hasattr(fp, "flush"): - fp.flush() - - -# -# -------------------------------------------------------------------- - -Image.register_save("PALM", _save) - -Image.register_extension("PALM", ".palm") - -Image.register_mime("PALM", "image/palm") diff --git a/.venv/lib/python3.12/site-packages/PIL/PcdImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/PcdImagePlugin.py deleted file mode 100644 index 296f3775..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PcdImagePlugin.py +++ /dev/null @@ -1,68 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PCD file handling -# -# History: -# 96-05-10 fl Created -# 96-05-27 fl Added draft mode (128x192, 256x384) -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile - -## -# Image plugin for PhotoCD images. This plugin only reads the 768x512 -# image from the file; higher resolutions are encoded in a proprietary -# encoding. - - -class PcdImageFile(ImageFile.ImageFile): - format = "PCD" - format_description = "Kodak PhotoCD" - - def _open(self) -> None: - # rough - assert self.fp is not None - - self.fp.seek(2048) - s = self.fp.read(1539) - - if not s.startswith(b"PCD_"): - msg = "not a PCD file" - raise SyntaxError(msg) - - orientation = s[1538] & 3 - self.tile_post_rotate = None - if orientation == 1: - self.tile_post_rotate = 90 - elif orientation == 3: - self.tile_post_rotate = 270 - - self._mode = "RGB" - self._size = (512, 768) if orientation in (1, 3) else (768, 512) - self.tile = [ImageFile._Tile("pcd", (0, 0, 768, 512), 96 * 2048)] - - def load_prepare(self) -> None: - if self._im is None and self.tile_post_rotate: - self.im = Image.core.new(self.mode, (768, 512)) - ImageFile.ImageFile.load_prepare(self) - - def load_end(self) -> None: - if self.tile_post_rotate: - # Handle rotated PCDs - self.im = self.rotate(self.tile_post_rotate, expand=True).im - - -# -# registry - -Image.register_open(PcdImageFile.format, PcdImageFile) - -Image.register_extension(PcdImageFile.format, ".pcd") diff --git a/.venv/lib/python3.12/site-packages/PIL/PcfFontFile.py b/.venv/lib/python3.12/site-packages/PIL/PcfFontFile.py deleted file mode 100644 index b923293b..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PcfFontFile.py +++ /dev/null @@ -1,258 +0,0 @@ -# -# THIS IS WORK IN PROGRESS -# -# The Python Imaging Library -# $Id$ -# -# portable compiled font file parser -# -# history: -# 1997-08-19 fl created -# 2003-09-13 fl fixed loading of unicode fonts -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1997-2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io - -from . import FontFile, Image -from ._binary import i8 -from ._binary import i16be as b16 -from ._binary import i16le as l16 -from ._binary import i32be as b32 -from ._binary import i32le as l32 - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from typing import BinaryIO - -# -------------------------------------------------------------------- -# declarations - -PCF_MAGIC = 0x70636601 # "\x01fcp" - -PCF_PROPERTIES = 1 << 0 -PCF_ACCELERATORS = 1 << 1 -PCF_METRICS = 1 << 2 -PCF_BITMAPS = 1 << 3 -PCF_INK_METRICS = 1 << 4 -PCF_BDF_ENCODINGS = 1 << 5 -PCF_SWIDTHS = 1 << 6 -PCF_GLYPH_NAMES = 1 << 7 -PCF_BDF_ACCELERATORS = 1 << 8 - -BYTES_PER_ROW: list[Callable[[int], int]] = [ - lambda bits: ((bits + 7) >> 3), - lambda bits: ((bits + 15) >> 3) & ~1, - lambda bits: ((bits + 31) >> 3) & ~3, - lambda bits: ((bits + 63) >> 3) & ~7, -] - - -def sz(s: bytes, o: int) -> bytes: - return s[o : s.index(b"\0", o)] - - -class PcfFontFile(FontFile.FontFile): - """Font file plugin for the X11 PCF format.""" - - name = "name" - - def __init__(self, fp: BinaryIO, charset_encoding: str = "iso8859-1"): - self.charset_encoding = charset_encoding - - magic = l32(fp.read(4)) - if magic != PCF_MAGIC: - msg = "not a PCF file" - raise SyntaxError(msg) - - super().__init__() - - count = l32(fp.read(4)) - self.toc = {} - for i in range(count): - type = l32(fp.read(4)) - self.toc[type] = l32(fp.read(4)), l32(fp.read(4)), l32(fp.read(4)) - - self.fp = fp - - self.info = self._load_properties() - - metrics = self._load_metrics() - bitmaps = self._load_bitmaps(metrics) - encoding = self._load_encoding() - - # - # create glyph structure - - for ch, ix in enumerate(encoding): - if ix is not None: - ( - xsize, - ysize, - left, - right, - width, - ascent, - descent, - attributes, - ) = metrics[ix] - self.glyph[ch] = ( - (width, 0), - (left, descent - ysize, xsize + left, descent), - (0, 0, xsize, ysize), - bitmaps[ix], - ) - - def _getformat( - self, tag: int - ) -> tuple[BinaryIO, int, Callable[[bytes], int], Callable[[bytes], int]]: - format, size, offset = self.toc[tag] - - fp = self.fp - fp.seek(offset) - - format = l32(fp.read(4)) - - if format & 4: - i16, i32 = b16, b32 - else: - i16, i32 = l16, l32 - - return fp, format, i16, i32 - - def _load_properties(self) -> dict[bytes, bytes | int]: - # - # font properties - - properties = {} - - fp, format, i16, i32 = self._getformat(PCF_PROPERTIES) - - nprops = i32(fp.read(4)) - - # read property description - p = [(i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4))) for _ in range(nprops)] - - if nprops & 3: - fp.seek(4 - (nprops & 3), io.SEEK_CUR) # pad - - data = fp.read(i32(fp.read(4))) - - for k, s, v in p: - property_value: bytes | int = sz(data, v) if s else v - properties[sz(data, k)] = property_value - - return properties - - def _load_metrics(self) -> list[tuple[int, int, int, int, int, int, int, int]]: - # - # font metrics - - metrics: list[tuple[int, int, int, int, int, int, int, int]] = [] - - fp, format, i16, i32 = self._getformat(PCF_METRICS) - - append = metrics.append - - if (format & 0xFF00) == 0x100: - # "compressed" metrics - for i in range(i16(fp.read(2))): - left = i8(fp.read(1)) - 128 - right = i8(fp.read(1)) - 128 - width = i8(fp.read(1)) - 128 - ascent = i8(fp.read(1)) - 128 - descent = i8(fp.read(1)) - 128 - xsize = right - left - ysize = ascent + descent - append((xsize, ysize, left, right, width, ascent, descent, 0)) - - else: - # "jumbo" metrics - for i in range(i32(fp.read(4))): - left = i16(fp.read(2)) - right = i16(fp.read(2)) - width = i16(fp.read(2)) - ascent = i16(fp.read(2)) - descent = i16(fp.read(2)) - attributes = i16(fp.read(2)) - xsize = right - left - ysize = ascent + descent - append((xsize, ysize, left, right, width, ascent, descent, attributes)) - - return metrics - - def _load_bitmaps( - self, metrics: list[tuple[int, int, int, int, int, int, int, int]] - ) -> list[Image.Image]: - # - # bitmap data - - fp, format, i16, i32 = self._getformat(PCF_BITMAPS) - - nbitmaps = i32(fp.read(4)) - - if nbitmaps != len(metrics): - msg = "Wrong number of bitmaps" - raise OSError(msg) - - offsets = [i32(fp.read(4)) for _ in range(nbitmaps)] - - bitmap_sizes = [i32(fp.read(4)) for _ in range(4)] - - # byteorder = format & 4 # non-zero => MSB - bitorder = format & 8 # non-zero => MSB - padindex = format & 3 - - bitmapsize = bitmap_sizes[padindex] - offsets.append(bitmapsize) - - data = fp.read(bitmapsize) - - pad = BYTES_PER_ROW[padindex] - mode = "1;R" - if bitorder: - mode = "1" - - bitmaps = [] - for i in range(nbitmaps): - xsize, ysize = metrics[i][:2] - b, e = offsets[i : i + 2] - bitmaps.append( - Image.frombytes("1", (xsize, ysize), data[b:e], "raw", mode, pad(xsize)) - ) - - return bitmaps - - def _load_encoding(self) -> list[int | None]: - fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS) - - first_col, last_col = i16(fp.read(2)), i16(fp.read(2)) - first_row, last_row = i16(fp.read(2)), i16(fp.read(2)) - - i16(fp.read(2)) # default - - nencoding = (last_col - first_col + 1) * (last_row - first_row + 1) - - # map character code to bitmap index - encoding: list[int | None] = [None] * min(256, nencoding) - - encoding_offsets = [i16(fp.read(2)) for _ in range(nencoding)] - - for i in range(first_col, len(encoding)): - try: - encoding_offset = encoding_offsets[ - ord(bytearray([i]).decode(self.charset_encoding)) - ] - if encoding_offset != 0xFFFF: - encoding[i] = encoding_offset - except UnicodeDecodeError: # noqa: PERF203 - # character is not supported in selected encoding - pass - - return encoding diff --git a/.venv/lib/python3.12/site-packages/PIL/PcxImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/PcxImagePlugin.py deleted file mode 100644 index 3e34e3c6..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PcxImagePlugin.py +++ /dev/null @@ -1,232 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PCX file handling -# -# This format was originally used by ZSoft's popular PaintBrush -# program for the IBM PC. It is also supported by many MS-DOS and -# Windows applications, including the Windows PaintBrush program in -# Windows 3. -# -# history: -# 1995-09-01 fl Created -# 1996-05-20 fl Fixed RGB support -# 1997-01-03 fl Fixed 2-bit and 4-bit support -# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1) -# 1999-02-07 fl Added write support -# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust -# 2002-07-30 fl Seek from to current position, not beginning of file -# 2003-06-03 fl Extract DPI settings (info["dpi"]) -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import logging -from typing import IO - -from . import Image, ImageFile, ImagePalette -from ._binary import i16le as i16 -from ._binary import o8 -from ._binary import o16le as o16 - -logger = logging.getLogger(__name__) - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 2 and prefix[0] == 10 and prefix[1] in [0, 2, 3, 5] - - -## -# Image plugin for Paintbrush images. - - -class PcxImageFile(ImageFile.ImageFile): - format = "PCX" - format_description = "Paintbrush" - - def _open(self) -> None: - # header - assert self.fp is not None - - s = self.fp.read(68) - if not _accept(s): - msg = "not a PCX file" - raise SyntaxError(msg) - - # image - bbox = i16(s, 4), i16(s, 6), i16(s, 8) + 1, i16(s, 10) + 1 - if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: - msg = "bad PCX image size" - raise SyntaxError(msg) - logger.debug("BBox: %s %s %s %s", *bbox) - - offset = self.fp.tell() + 60 - - # format - version = s[1] - bits = s[3] - planes = s[65] - provided_stride = i16(s, 66) - logger.debug( - "PCX version %s, bits %s, planes %s, stride %s", - version, - bits, - planes, - provided_stride, - ) - - self.info["dpi"] = i16(s, 12), i16(s, 14) - - if bits == 1 and planes == 1: - mode = rawmode = "1" - - elif bits == 1 and planes in (2, 4): - mode = "P" - rawmode = f"P;{planes}L" - self.palette = ImagePalette.raw("RGB", s[16:64]) - - elif version == 5 and bits == 8 and planes == 1: - mode = rawmode = "L" - # FIXME: hey, this doesn't work with the incremental loader !!! - self.fp.seek(-769, io.SEEK_END) - s = self.fp.read(769) - if len(s) == 769 and s[0] == 12: - # check if the palette is linear grayscale - for i in range(256): - if s[i * 3 + 1 : i * 3 + 4] != o8(i) * 3: - mode = rawmode = "P" - break - if mode == "P": - self.palette = ImagePalette.raw("RGB", s[1:]) - - elif version == 5 and bits == 8 and planes == 3: - mode = "RGB" - rawmode = "RGB;L" - - else: - msg = "unknown PCX mode" - raise OSError(msg) - - self._mode = mode - self._size = bbox[2] - bbox[0], bbox[3] - bbox[1] - - # Don't trust the passed in stride. - # Calculate the approximate position for ourselves. - # CVE-2020-35653 - stride = (self._size[0] * bits + 7) // 8 - - # While the specification states that this must be even, - # not all images follow this - if provided_stride != stride: - stride += stride % 2 - - bbox = (0, 0) + self.size - logger.debug("size: %sx%s", *self.size) - - self.tile = [ImageFile._Tile("pcx", bbox, offset, (rawmode, planes * stride))] - - -# -------------------------------------------------------------------- -# save PCX files - - -SAVE = { - # mode: (version, bits, planes, raw mode) - "1": (2, 1, 1, "1"), - "L": (5, 8, 1, "L"), - "P": (5, 8, 1, "P"), - "RGB": (5, 8, 3, "RGB;L"), -} - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.width == 0 or im.height == 0: - msg = "Cannot write empty image as PCX" - raise ValueError(msg) - - try: - version, bits, planes, rawmode = SAVE[im.mode] - except KeyError as e: - msg = f"Cannot save {im.mode} images as PCX" - raise ValueError(msg) from e - - # bytes per plane - stride = (im.size[0] * bits + 7) // 8 - # stride should be even - stride += stride % 2 - # Stride needs to be kept in sync with the PcxEncode.c version. - # Ideally it should be passed in in the state, but the bytes value - # gets overwritten. - - logger.debug( - "PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d", - im.size[0], - bits, - stride, - ) - - # under windows, we could determine the current screen size with - # "Image.core.display_mode()[1]", but I think that's overkill... - - screen = im.size - - dpi = 100, 100 - - # PCX header - fp.write( - o8(10) - + o8(version) - + o8(1) - + o8(bits) - + o16(0) - + o16(0) - + o16(im.size[0] - 1) - + o16(im.size[1] - 1) - + o16(dpi[0]) - + o16(dpi[1]) - + b"\0" * 24 - + b"\xff" * 24 - + b"\0" - + o8(planes) - + o16(stride) - + o16(1) - + o16(screen[0]) - + o16(screen[1]) - + b"\0" * 54 - ) - - assert fp.tell() == 128 - - ImageFile._save( - im, fp, [ImageFile._Tile("pcx", (0, 0) + im.size, 0, (rawmode, bits * planes))] - ) - - if im.mode == "P": - # colour palette - fp.write(o8(12)) - palette = im.im.getpalette("RGB", "RGB") - palette += b"\x00" * (768 - len(palette)) - fp.write(palette) # 768 bytes - elif im.mode == "L": - # grayscale palette - fp.write(o8(12)) - for i in range(256): - fp.write(o8(i) * 3) - - -# -------------------------------------------------------------------- -# registry - - -Image.register_open(PcxImageFile.format, PcxImageFile, _accept) -Image.register_save(PcxImageFile.format, _save) - -Image.register_extension(PcxImageFile.format, ".pcx") - -Image.register_mime(PcxImageFile.format, "image/x-pcx") diff --git a/.venv/lib/python3.12/site-packages/PIL/PdfImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/PdfImagePlugin.py deleted file mode 100644 index 5594c7e0..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PdfImagePlugin.py +++ /dev/null @@ -1,311 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PDF (Acrobat) file handling -# -# History: -# 1996-07-16 fl Created -# 1997-01-18 fl Fixed header -# 2004-02-21 fl Fixes for 1/L/CMYK images, etc. -# 2004-02-24 fl Fixes for 1 and P images. -# -# Copyright (c) 1997-2004 by Secret Labs AB. All rights reserved. -# Copyright (c) 1996-1997 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -## -# Image plugin for PDF images (output only). -## -from __future__ import annotations - -import io -import math -import os -import time -from typing import IO, Any - -from . import Image, ImageFile, ImageSequence, PdfParser, features - -# -# -------------------------------------------------------------------- - -# object ids: -# 1. catalogue -# 2. pages -# 3. image -# 4. page -# 5. page contents - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, save_all=True) - - -## -# (Internal) Image save plugin for the PDF format. - - -def _write_image( - im: Image.Image, - filename: str | bytes, - existing_pdf: PdfParser.PdfParser, - image_refs: list[PdfParser.IndirectReference], -) -> tuple[PdfParser.IndirectReference, str]: - # FIXME: Should replace ASCIIHexDecode with RunLengthDecode - # (packbits) or LZWDecode (tiff/lzw compression). Note that - # PDF 1.2 also supports Flatedecode (zip compression). - - params = None - decode = None - - # - # Get image characteristics - - width, height = im.size - - dict_obj: dict[str, Any] = {"BitsPerComponent": 8} - if im.mode == "1": - if features.check("libtiff"): - decode_filter = "CCITTFaxDecode" - dict_obj["BitsPerComponent"] = 1 - params = PdfParser.PdfArray( - [ - PdfParser.PdfDict( - { - "K": -1, - "BlackIs1": True, - "Columns": width, - "Rows": height, - } - ) - ] - ) - else: - decode_filter = "DCTDecode" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray") - procset = "ImageB" # grayscale - elif im.mode == "L": - decode_filter = "DCTDecode" - # params = f"<< /Predictor 15 /Columns {width-2} >>" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray") - procset = "ImageB" # grayscale - elif im.mode == "LA": - decode_filter = "JPXDecode" - # params = f"<< /Predictor 15 /Columns {width-2} >>" - procset = "ImageB" # grayscale - dict_obj["SMaskInData"] = 1 - elif im.mode == "P": - decode_filter = "ASCIIHexDecode" - palette = im.getpalette() - assert palette is not None - dict_obj["ColorSpace"] = [ - PdfParser.PdfName("Indexed"), - PdfParser.PdfName("DeviceRGB"), - len(palette) // 3 - 1, - PdfParser.PdfBinary(palette), - ] - procset = "ImageI" # indexed color - - if "transparency" in im.info: - smask = im.convert("LA").getchannel("A") - smask.encoderinfo = {} - - image_ref = _write_image(smask, filename, existing_pdf, image_refs)[0] - dict_obj["SMask"] = image_ref - elif im.mode == "RGB": - decode_filter = "DCTDecode" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceRGB") - procset = "ImageC" # color images - elif im.mode == "RGBA": - decode_filter = "JPXDecode" - procset = "ImageC" # color images - dict_obj["SMaskInData"] = 1 - elif im.mode == "CMYK": - decode_filter = "DCTDecode" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceCMYK") - procset = "ImageC" # color images - decode = [1, 0, 1, 0, 1, 0, 1, 0] - else: - msg = f"cannot save mode {im.mode}" - raise ValueError(msg) - - # - # image - - op = io.BytesIO() - - if decode_filter == "ASCIIHexDecode": - ImageFile._save(im, op, [ImageFile._Tile("hex", (0, 0) + im.size, 0, im.mode)]) - elif decode_filter == "CCITTFaxDecode": - im.save( - op, - "TIFF", - compression="group4", - # use a single strip - strip_size=math.ceil(width / 8) * height, - ) - elif decode_filter == "DCTDecode": - Image.SAVE["JPEG"](im, op, filename) - elif decode_filter == "JPXDecode": - del dict_obj["BitsPerComponent"] - Image.SAVE["JPEG2000"](im, op, filename) - else: - msg = f"unsupported PDF filter ({decode_filter})" - raise ValueError(msg) - - stream = op.getvalue() - filter: PdfParser.PdfArray | PdfParser.PdfName - if decode_filter == "CCITTFaxDecode": - stream = stream[8:] - filter = PdfParser.PdfArray([PdfParser.PdfName(decode_filter)]) - else: - filter = PdfParser.PdfName(decode_filter) - - image_ref = image_refs.pop(0) - existing_pdf.write_obj( - image_ref, - stream=stream, - Type=PdfParser.PdfName("XObject"), - Subtype=PdfParser.PdfName("Image"), - Width=width, # * 72.0 / x_resolution, - Height=height, # * 72.0 / y_resolution, - Filter=filter, - Decode=decode, - DecodeParms=params, - **dict_obj, - ) - - return image_ref, procset - - -def _save( - im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False -) -> None: - is_appending = im.encoderinfo.get("append", False) - filename_str = filename.decode() if isinstance(filename, bytes) else filename - if is_appending: - existing_pdf = PdfParser.PdfParser(f=fp, filename=filename_str, mode="r+b") - else: - existing_pdf = PdfParser.PdfParser(f=fp, filename=filename_str, mode="w+b") - - dpi = im.encoderinfo.get("dpi") - if dpi: - x_resolution = dpi[0] - y_resolution = dpi[1] - else: - x_resolution = y_resolution = im.encoderinfo.get("resolution", 72.0) - - info = { - "title": ( - None if is_appending else os.path.splitext(os.path.basename(filename))[0] - ), - "author": None, - "subject": None, - "keywords": None, - "creator": None, - "producer": None, - "creationDate": None if is_appending else time.gmtime(), - "modDate": None if is_appending else time.gmtime(), - } - for k, default in info.items(): - v = im.encoderinfo.get(k) if k in im.encoderinfo else default - if v: - existing_pdf.info[k[0].upper() + k[1:]] = v - - # - # make sure image data is available - im.load() - - existing_pdf.start_writing() - existing_pdf.write_header() - existing_pdf.write_comment("created by Pillow PDF driver") - - # - # pages - ims = [im] - if save_all: - append_images = im.encoderinfo.get("append_images", []) - for append_im in append_images: - append_im.encoderinfo = im.encoderinfo.copy() - ims.append(append_im) - number_of_pages = 0 - image_refs = [] - page_refs = [] - contents_refs = [] - for im in ims: - im_number_of_pages = 1 - if save_all: - im_number_of_pages = getattr(im, "n_frames", 1) - number_of_pages += im_number_of_pages - for i in range(im_number_of_pages): - image_refs.append(existing_pdf.next_object_id(0)) - if im.mode == "P" and "transparency" in im.info: - image_refs.append(existing_pdf.next_object_id(0)) - - page_refs.append(existing_pdf.next_object_id(0)) - contents_refs.append(existing_pdf.next_object_id(0)) - existing_pdf.pages.append(page_refs[-1]) - - # - # catalog and list of pages - existing_pdf.write_catalog() - - page_number = 0 - for im_sequence in ims: - im_pages: ImageSequence.Iterator | list[Image.Image] = ( - ImageSequence.Iterator(im_sequence) if save_all else [im_sequence] - ) - for im in im_pages: - image_ref, procset = _write_image(im, filename, existing_pdf, image_refs) - - # - # page - - existing_pdf.write_page( - page_refs[page_number], - Resources=PdfParser.PdfDict( - ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)], - XObject=PdfParser.PdfDict(image=image_ref), - ), - MediaBox=[ - 0, - 0, - im.width * 72.0 / x_resolution, - im.height * 72.0 / y_resolution, - ], - Contents=contents_refs[page_number], - ) - - # - # page contents - - page_contents = b"q %f 0 0 %f 0 0 cm /image Do Q\n" % ( - im.width * 72.0 / x_resolution, - im.height * 72.0 / y_resolution, - ) - - existing_pdf.write_obj(contents_refs[page_number], stream=page_contents) - - page_number += 1 - - # - # trailer - existing_pdf.write_xref_and_trailer() - if hasattr(fp, "flush"): - fp.flush() - existing_pdf.close() - - -# -# -------------------------------------------------------------------- - - -Image.register_save("PDF", _save) -Image.register_save_all("PDF", _save_all) - -Image.register_extension("PDF", ".pdf") - -Image.register_mime("PDF", "application/pdf") diff --git a/.venv/lib/python3.12/site-packages/PIL/PdfParser.py b/.venv/lib/python3.12/site-packages/PIL/PdfParser.py deleted file mode 100644 index f7f3a464..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PdfParser.py +++ /dev/null @@ -1,1081 +0,0 @@ -from __future__ import annotations - -import calendar -import codecs -import collections -import mmap -import os -import re -import time -import zlib -from typing import Any, NamedTuple - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import IO - - _DictBase = collections.UserDict[str | bytes, Any] -else: - _DictBase = collections.UserDict - - -# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set -# on page 656 -def encode_text(s: str) -> bytes: - return codecs.BOM_UTF16_BE + s.encode("utf_16_be") - - -PDFDocEncoding = { - 0x16: "\u0017", - 0x18: "\u02d8", - 0x19: "\u02c7", - 0x1A: "\u02c6", - 0x1B: "\u02d9", - 0x1C: "\u02dd", - 0x1D: "\u02db", - 0x1E: "\u02da", - 0x1F: "\u02dc", - 0x80: "\u2022", - 0x81: "\u2020", - 0x82: "\u2021", - 0x83: "\u2026", - 0x84: "\u2014", - 0x85: "\u2013", - 0x86: "\u0192", - 0x87: "\u2044", - 0x88: "\u2039", - 0x89: "\u203a", - 0x8A: "\u2212", - 0x8B: "\u2030", - 0x8C: "\u201e", - 0x8D: "\u201c", - 0x8E: "\u201d", - 0x8F: "\u2018", - 0x90: "\u2019", - 0x91: "\u201a", - 0x92: "\u2122", - 0x93: "\ufb01", - 0x94: "\ufb02", - 0x95: "\u0141", - 0x96: "\u0152", - 0x97: "\u0160", - 0x98: "\u0178", - 0x99: "\u017d", - 0x9A: "\u0131", - 0x9B: "\u0142", - 0x9C: "\u0153", - 0x9D: "\u0161", - 0x9E: "\u017e", - 0xA0: "\u20ac", -} - - -def decode_text(b: bytes) -> str: - if b[: len(codecs.BOM_UTF16_BE)] == codecs.BOM_UTF16_BE: - return b[len(codecs.BOM_UTF16_BE) :].decode("utf_16_be") - else: - return "".join(PDFDocEncoding.get(byte, chr(byte)) for byte in b) - - -class PdfFormatError(RuntimeError): - """An error that probably indicates a syntactic or semantic error in the - PDF file structure""" - - pass - - -def check_format_condition(condition: bool, error_message: str) -> None: - if not condition: - raise PdfFormatError(error_message) - - -class IndirectReferenceTuple(NamedTuple): - object_id: int - generation: int - - -class IndirectReference(IndirectReferenceTuple): - def __str__(self) -> str: - return f"{self.object_id} {self.generation} R" - - def __bytes__(self) -> bytes: - return self.__str__().encode("us-ascii") - - def __eq__(self, other: object) -> bool: - if self.__class__ is not other.__class__: - return False - assert isinstance(other, IndirectReference) - return other.object_id == self.object_id and other.generation == self.generation - - def __ne__(self, other: object) -> bool: - return not (self == other) - - def __hash__(self) -> int: - return hash((self.object_id, self.generation)) - - -class IndirectObjectDef(IndirectReference): - def __str__(self) -> str: - return f"{self.object_id} {self.generation} obj" - - -class XrefTable: - def __init__(self) -> None: - self.existing_entries: dict[int, tuple[int, int]] = ( - {} - ) # object ID => (offset, generation) - self.new_entries: dict[int, tuple[int, int]] = ( - {} - ) # object ID => (offset, generation) - self.deleted_entries = {0: 65536} # object ID => generation - self.reading_finished = False - - def __setitem__(self, key: int, value: tuple[int, int]) -> None: - if self.reading_finished: - self.new_entries[key] = value - else: - self.existing_entries[key] = value - if key in self.deleted_entries: - del self.deleted_entries[key] - - def __getitem__(self, key: int) -> tuple[int, int]: - try: - return self.new_entries[key] - except KeyError: - return self.existing_entries[key] - - def __delitem__(self, key: int) -> None: - if key in self.new_entries: - generation = self.new_entries[key][1] + 1 - del self.new_entries[key] - self.deleted_entries[key] = generation - elif key in self.existing_entries: - generation = self.existing_entries[key][1] + 1 - self.deleted_entries[key] = generation - elif key in self.deleted_entries: - generation = self.deleted_entries[key] - else: - msg = f"object ID {key} cannot be deleted because it doesn't exist" - raise IndexError(msg) - - def __contains__(self, key: int) -> bool: - return key in self.existing_entries or key in self.new_entries - - def __len__(self) -> int: - return len( - set(self.existing_entries.keys()) - | set(self.new_entries.keys()) - | set(self.deleted_entries.keys()) - ) - - def keys(self) -> set[int]: - return ( - set(self.existing_entries.keys()) - set(self.deleted_entries.keys()) - ) | set(self.new_entries.keys()) - - def write(self, f: IO[bytes]) -> int: - keys = sorted(set(self.new_entries.keys()) | set(self.deleted_entries.keys())) - deleted_keys = sorted(set(self.deleted_entries.keys())) - startxref = f.tell() - f.write(b"xref\n") - while keys: - # find a contiguous sequence of object IDs - prev: int | None = None - for index, key in enumerate(keys): - if prev is None or prev + 1 == key: - prev = key - else: - contiguous_keys = keys[:index] - keys = keys[index:] - break - else: - contiguous_keys = keys - keys = [] - f.write(b"%d %d\n" % (contiguous_keys[0], len(contiguous_keys))) - for object_id in contiguous_keys: - if object_id in self.new_entries: - f.write(b"%010d %05d n \n" % self.new_entries[object_id]) - else: - this_deleted_object_id = deleted_keys.pop(0) - check_format_condition( - object_id == this_deleted_object_id, - f"expected the next deleted object ID to be {object_id}, " - f"instead found {this_deleted_object_id}", - ) - try: - next_in_linked_list = deleted_keys[0] - except IndexError: - next_in_linked_list = 0 - f.write( - b"%010d %05d f \n" - % (next_in_linked_list, self.deleted_entries[object_id]) - ) - return startxref - - -class PdfName: - name: bytes - - def __init__(self, name: PdfName | bytes | str) -> None: - if isinstance(name, PdfName): - self.name = name.name - elif isinstance(name, bytes): - self.name = name - else: - self.name = name.encode("us-ascii") - - def name_as_str(self) -> str: - return self.name.decode("us-ascii") - - def __eq__(self, other: object) -> bool: - return ( - isinstance(other, PdfName) and other.name == self.name - ) or other == self.name - - def __hash__(self) -> int: - return hash(self.name) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({repr(self.name)})" - - @classmethod - def from_pdf_stream(cls, data: bytes) -> PdfName: - return cls(PdfParser.interpret_name(data)) - - allowed_chars = set(range(33, 127)) - {ord(c) for c in "#%/()<>[]{}"} - - def __bytes__(self) -> bytes: - result = bytearray(b"/") - for b in self.name: - if b in self.allowed_chars: - result.append(b) - else: - result.extend(b"#%02X" % b) - return bytes(result) - - -class PdfArray(list[Any]): - def __bytes__(self) -> bytes: - return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]" - - -class PdfDict(_DictBase): - def __setattr__(self, key: str, value: Any) -> None: - if key == "data": - collections.UserDict.__setattr__(self, key, value) - else: - self[key.encode("us-ascii")] = value - - def __getattr__(self, key: str) -> str | time.struct_time: - try: - value = self[key.encode("us-ascii")] - except KeyError as e: - raise AttributeError(key) from e - if isinstance(value, bytes): - value = decode_text(value) - if key.endswith("Date"): - if value.startswith("D:"): - value = value[2:] - - relationship = "Z" - if len(value) > 17: - relationship = value[14] - offset = int(value[15:17]) * 60 - if len(value) > 20: - offset += int(value[18:20]) - - format = "%Y%m%d%H%M%S"[: len(value) - 2] - value = time.strptime(value[: len(format) + 2], format) - if relationship in ["+", "-"]: - offset *= 60 - if relationship == "+": - offset *= -1 - value = time.gmtime(calendar.timegm(value) + offset) - return value - - def __bytes__(self) -> bytes: - out = bytearray(b"<<") - for key, value in self.items(): - if value is None: - continue - value = pdf_repr(value) - out.extend(b"\n") - out.extend(bytes(PdfName(key))) - out.extend(b" ") - out.extend(value) - out.extend(b"\n>>") - return bytes(out) - - -class PdfBinary: - def __init__(self, data: list[int] | bytes) -> None: - self.data = data - - def __bytes__(self) -> bytes: - return b"<%s>" % b"".join(b"%02X" % b for b in self.data) - - -class PdfStream: - def __init__(self, dictionary: PdfDict, buf: bytes) -> None: - self.dictionary = dictionary - self.buf = buf - - def decode(self) -> bytes: - try: - filter = self.dictionary[b"Filter"] - except KeyError: - return self.buf - if filter == b"FlateDecode": - try: - expected_length = self.dictionary[b"DL"] - except KeyError: - expected_length = self.dictionary[b"Length"] - return zlib.decompress(self.buf, bufsize=int(expected_length)) - else: - msg = f"stream filter {repr(filter)} unknown/unsupported" - raise NotImplementedError(msg) - - -def pdf_repr(x: Any) -> bytes: - if x is True: - return b"true" - elif x is False: - return b"false" - elif x is None: - return b"null" - elif isinstance(x, (PdfName, PdfDict, PdfArray, PdfBinary)): - return bytes(x) - elif isinstance(x, (int, float)): - return str(x).encode("us-ascii") - elif isinstance(x, time.struct_time): - return b"(D:" + time.strftime("%Y%m%d%H%M%SZ", x).encode("us-ascii") + b")" - elif isinstance(x, dict): - return bytes(PdfDict(x)) - elif isinstance(x, list): - return bytes(PdfArray(x)) - elif isinstance(x, str): - return pdf_repr(encode_text(x)) - elif isinstance(x, bytes): - # XXX escape more chars? handle binary garbage - x = x.replace(b"\\", b"\\\\") - x = x.replace(b"(", b"\\(") - x = x.replace(b")", b"\\)") - return b"(" + x + b")" - else: - return bytes(x) - - -class PdfParser: - """Based on - https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf - Supports PDF up to 1.4 - """ - - def __init__( - self, - filename: str | None = None, - f: IO[bytes] | None = None, - buf: bytes | bytearray | None = None, - start_offset: int = 0, - mode: str = "rb", - ) -> None: - if buf and f: - msg = "specify buf or f or filename, but not both buf and f" - raise RuntimeError(msg) - self.filename = filename - self.buf: bytes | bytearray | mmap.mmap | None = buf - self.f = f - self.start_offset = start_offset - self.should_close_buf = False - self.should_close_file = False - if filename is not None and f is None: - self.f = f = open(filename, mode) - self.should_close_file = True - if f is not None: - self.buf = self.get_buf_from_file(f) - self.should_close_buf = True - if not filename and hasattr(f, "name"): - self.filename = f.name - self.cached_objects: dict[IndirectReference, Any] = {} - self.root_ref: IndirectReference | None - self.info_ref: IndirectReference | None - self.pages_ref: IndirectReference | None - self.last_xref_section_offset: int | None - if self.buf: - self.read_pdf_info() - else: - self.file_size_total = self.file_size_this = 0 - self.root = PdfDict() - self.root_ref = None - self.info = PdfDict() - self.info_ref = None - self.page_tree_root = PdfDict() - self.pages: list[IndirectReference] = [] - self.orig_pages: list[IndirectReference] = [] - self.pages_ref = None - self.last_xref_section_offset = None - self.trailer_dict: dict[bytes, Any] = {} - self.xref_table = XrefTable() - self.xref_table.reading_finished = True - if f: - self.seek_end() - - def __enter__(self) -> PdfParser: - return self - - def __exit__(self, *args: object) -> None: - self.close() - - def start_writing(self) -> None: - self.close_buf() - self.seek_end() - - def close_buf(self) -> None: - if isinstance(self.buf, mmap.mmap): - self.buf.close() - self.buf = None - - def close(self) -> None: - if self.should_close_buf: - self.close_buf() - if self.f is not None and self.should_close_file: - self.f.close() - self.f = None - - def seek_end(self) -> None: - assert self.f is not None - self.f.seek(0, os.SEEK_END) - - def write_header(self) -> None: - assert self.f is not None - self.f.write(b"%PDF-1.4\n") - - def write_comment(self, s: str) -> None: - assert self.f is not None - self.f.write(f"% {s}\n".encode()) - - def write_catalog(self) -> IndirectReference: - assert self.f is not None - self.del_root() - self.root_ref = self.next_object_id(self.f.tell()) - self.pages_ref = self.next_object_id(0) - self.rewrite_pages() - self.write_obj(self.root_ref, Type=PdfName(b"Catalog"), Pages=self.pages_ref) - self.write_obj( - self.pages_ref, - Type=PdfName(b"Pages"), - Count=len(self.pages), - Kids=self.pages, - ) - return self.root_ref - - def rewrite_pages(self) -> None: - pages_tree_nodes_to_delete = [] - for i, page_ref in enumerate(self.orig_pages): - page_info = self.cached_objects[page_ref] - del self.xref_table[page_ref.object_id] - pages_tree_nodes_to_delete.append(page_info[PdfName(b"Parent")]) - if page_ref not in self.pages: - # the page has been deleted - continue - # make dict keys into strings for passing to write_page - stringified_page_info = {} - for key, value in page_info.items(): - # key should be a PdfName - stringified_page_info[key.name_as_str()] = value - stringified_page_info["Parent"] = self.pages_ref - new_page_ref = self.write_page(None, **stringified_page_info) - for j, cur_page_ref in enumerate(self.pages): - if cur_page_ref == page_ref: - # replace the page reference with the new one - self.pages[j] = new_page_ref - # delete redundant Pages tree nodes from xref table - for pages_tree_node_ref in pages_tree_nodes_to_delete: - while pages_tree_node_ref: - pages_tree_node = self.cached_objects[pages_tree_node_ref] - if pages_tree_node_ref.object_id in self.xref_table: - del self.xref_table[pages_tree_node_ref.object_id] - pages_tree_node_ref = pages_tree_node.get(b"Parent", None) - self.orig_pages = [] - - def write_xref_and_trailer( - self, new_root_ref: IndirectReference | None = None - ) -> None: - assert self.f is not None - if new_root_ref: - self.del_root() - self.root_ref = new_root_ref - if self.info: - self.info_ref = self.write_obj(None, self.info) - start_xref = self.xref_table.write(self.f) - num_entries = len(self.xref_table) - trailer_dict: dict[str | bytes, Any] = { - b"Root": self.root_ref, - b"Size": num_entries, - } - if self.last_xref_section_offset is not None: - trailer_dict[b"Prev"] = self.last_xref_section_offset - if self.info: - trailer_dict[b"Info"] = self.info_ref - self.last_xref_section_offset = start_xref - self.f.write( - b"trailer\n" - + bytes(PdfDict(trailer_dict)) - + b"\nstartxref\n%d\n%%%%EOF" % start_xref - ) - - def write_page( - self, ref: int | IndirectReference | None, *objs: Any, **dict_obj: Any - ) -> IndirectReference: - obj_ref = self.pages[ref] if isinstance(ref, int) else ref - if "Type" not in dict_obj: - dict_obj["Type"] = PdfName(b"Page") - if "Parent" not in dict_obj: - dict_obj["Parent"] = self.pages_ref - return self.write_obj(obj_ref, *objs, **dict_obj) - - def write_obj( - self, ref: IndirectReference | None, *objs: Any, **dict_obj: Any - ) -> IndirectReference: - assert self.f is not None - f = self.f - if ref is None: - ref = self.next_object_id(f.tell()) - else: - self.xref_table[ref.object_id] = (f.tell(), ref.generation) - f.write(bytes(IndirectObjectDef(*ref))) - stream = dict_obj.pop("stream", None) - if stream is not None: - dict_obj["Length"] = len(stream) - if dict_obj: - f.write(pdf_repr(dict_obj)) - for obj in objs: - f.write(pdf_repr(obj)) - if stream is not None: - f.write(b"stream\n") - f.write(stream) - f.write(b"\nendstream\n") - f.write(b"endobj\n") - return ref - - def del_root(self) -> None: - if self.root_ref is None: - return - del self.xref_table[self.root_ref.object_id] - del self.xref_table[self.root[b"Pages"].object_id] - - @staticmethod - def get_buf_from_file(f: IO[bytes]) -> bytes | mmap.mmap: - if hasattr(f, "getbuffer"): - return f.getbuffer() - elif hasattr(f, "getvalue"): - return f.getvalue() - else: - try: - return mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) - except ValueError: # cannot mmap an empty file - return b"" - - def read_pdf_info(self) -> None: - assert self.buf is not None - self.file_size_total = len(self.buf) - self.file_size_this = self.file_size_total - self.start_offset - self.read_trailer() - check_format_condition( - self.trailer_dict.get(b"Root") is not None, "Root is missing" - ) - self.root_ref = self.trailer_dict[b"Root"] - assert self.root_ref is not None - self.info_ref = self.trailer_dict.get(b"Info", None) - self.root = PdfDict(self.read_indirect(self.root_ref)) - if self.info_ref is None: - self.info = PdfDict() - else: - self.info = PdfDict(self.read_indirect(self.info_ref)) - check_format_condition(b"Type" in self.root, "/Type missing in Root") - check_format_condition( - self.root[b"Type"] == b"Catalog", "/Type in Root is not /Catalog" - ) - check_format_condition( - self.root.get(b"Pages") is not None, "/Pages missing in Root" - ) - check_format_condition( - isinstance(self.root[b"Pages"], IndirectReference), - "/Pages in Root is not an indirect reference", - ) - self.pages_ref = self.root[b"Pages"] - assert self.pages_ref is not None - self.page_tree_root = self.read_indirect(self.pages_ref) - self.pages = self.linearize_page_tree(self.page_tree_root) - # save the original list of page references - # in case the user modifies, adds or deletes some pages - # and we need to rewrite the pages and their list - self.orig_pages = self.pages[:] - - def next_object_id(self, offset: int | None = None) -> IndirectReference: - try: - # TODO: support reuse of deleted objects - reference = IndirectReference(max(self.xref_table.keys()) + 1, 0) - except ValueError: - reference = IndirectReference(1, 0) - if offset is not None: - self.xref_table[reference.object_id] = (offset, 0) - return reference - - delimiter = rb"[][()<>{}/%]" - delimiter_or_ws = rb"[][()<>{}/%\000\011\012\014\015\040]" - whitespace = rb"[\000\011\012\014\015\040]" - whitespace_or_hex = rb"[\000\011\012\014\015\0400-9a-fA-F]" - whitespace_optional = whitespace + b"*" - whitespace_mandatory = whitespace + b"+" - # No "\012" aka "\n" or "\015" aka "\r": - whitespace_optional_no_nl = rb"[\000\011\014\040]*" - newline_only = rb"[\r\n]+" - newline = whitespace_optional_no_nl + newline_only + whitespace_optional_no_nl - re_trailer_end = re.compile( - whitespace_mandatory - + rb"trailer" - + whitespace_optional - + rb"<<(.*>>)" - + newline - + rb"startxref" - + newline - + rb"([0-9]+)" - + newline - + rb"%%EOF" - + whitespace_optional - + rb"$", - re.DOTALL, - ) - re_trailer_prev = re.compile( - whitespace_optional - + rb"trailer" - + whitespace_optional - + rb"<<(.*?>>)" - + newline - + rb"startxref" - + newline - + rb"([0-9]+)" - + newline - + rb"%%EOF" - + whitespace_optional, - re.DOTALL, - ) - - def read_trailer(self) -> None: - assert self.buf is not None - search_start_offset = len(self.buf) - 16384 - if search_start_offset < self.start_offset: - search_start_offset = self.start_offset - m = self.re_trailer_end.search(self.buf, search_start_offset) - check_format_condition(m is not None, "trailer end not found") - # make sure we found the LAST trailer - last_match = m - while m: - last_match = m - m = self.re_trailer_end.search(self.buf, m.start() + 16) - if not m: - m = last_match - assert m is not None - trailer_data = m.group(1) - self.last_xref_section_offset = int(m.group(2)) - self.trailer_dict = self.interpret_trailer(trailer_data) - self.xref_table = XrefTable() - self.read_xref_table(xref_section_offset=self.last_xref_section_offset) - if b"Prev" in self.trailer_dict: - self.read_prev_trailer(self.trailer_dict[b"Prev"]) - - def read_prev_trailer( - self, xref_section_offset: int, processed_offsets: list[int] = [] - ) -> None: - assert self.buf is not None - trailer_offset = self.read_xref_table(xref_section_offset=xref_section_offset) - m = self.re_trailer_prev.search( - self.buf[trailer_offset : trailer_offset + 16384] - ) - check_format_condition(m is not None, "previous trailer not found") - assert m is not None - trailer_data = m.group(1) - check_format_condition( - int(m.group(2)) == xref_section_offset, - "xref section offset in previous trailer doesn't match what was expected", - ) - trailer_dict = self.interpret_trailer(trailer_data) - if b"Prev" in trailer_dict: - processed_offsets.append(xref_section_offset) - check_format_condition( - trailer_dict[b"Prev"] not in processed_offsets, "trailer loop found" - ) - self.read_prev_trailer(trailer_dict[b"Prev"], processed_offsets) - - re_whitespace_optional = re.compile(whitespace_optional) - re_name = re.compile( - whitespace_optional - + rb"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" - + delimiter_or_ws - + rb")" - ) - re_dict_start = re.compile(whitespace_optional + rb"<<") - re_dict_end = re.compile(whitespace_optional + rb">>" + whitespace_optional) - - @classmethod - def interpret_trailer(cls, trailer_data: bytes) -> dict[bytes, Any]: - trailer = {} - offset = 0 - while True: - m = cls.re_name.match(trailer_data, offset) - if not m: - m = cls.re_dict_end.match(trailer_data, offset) - check_format_condition( - m is not None and m.end() == len(trailer_data), - "name not found in trailer, remaining data: " - + repr(trailer_data[offset:]), - ) - break - key = cls.interpret_name(m.group(1)) - assert isinstance(key, bytes) - value, value_offset = cls.get_value(trailer_data, m.end()) - trailer[key] = value - if value_offset is None: - break - offset = value_offset - check_format_condition( - b"Size" in trailer and isinstance(trailer[b"Size"], int), - "/Size not in trailer or not an integer", - ) - check_format_condition( - b"Root" in trailer and isinstance(trailer[b"Root"], IndirectReference), - "/Root not in trailer or not an indirect reference", - ) - return trailer - - re_hashes_in_name = re.compile(rb"([^#]*)(#([0-9a-fA-F]{2}))?") - - @classmethod - def interpret_name(cls, raw: bytes, as_text: bool = False) -> str | bytes: - name = b"" - for m in cls.re_hashes_in_name.finditer(raw): - if m.group(3): - name += m.group(1) + bytearray.fromhex(m.group(3).decode("us-ascii")) - else: - name += m.group(1) - if as_text: - return name.decode("utf-8") - else: - return bytes(name) - - re_null = re.compile(whitespace_optional + rb"null(?=" + delimiter_or_ws + rb")") - re_true = re.compile(whitespace_optional + rb"true(?=" + delimiter_or_ws + rb")") - re_false = re.compile(whitespace_optional + rb"false(?=" + delimiter_or_ws + rb")") - re_int = re.compile( - whitespace_optional + rb"([-+]?[0-9]+)(?=" + delimiter_or_ws + rb")" - ) - re_real = re.compile( - whitespace_optional - + rb"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" - + delimiter_or_ws - + rb")" - ) - re_array_start = re.compile(whitespace_optional + rb"\[") - re_array_end = re.compile(whitespace_optional + rb"]") - re_string_hex = re.compile( - whitespace_optional + rb"<(" + whitespace_or_hex + rb"*)>" - ) - re_string_lit = re.compile(whitespace_optional + rb"\(") - re_indirect_reference = re.compile( - whitespace_optional - + rb"([-+]?[0-9]+)" - + whitespace_mandatory - + rb"([-+]?[0-9]+)" - + whitespace_mandatory - + rb"R(?=" - + delimiter_or_ws - + rb")" - ) - re_indirect_def_start = re.compile( - whitespace_optional - + rb"([-+]?[0-9]+)" - + whitespace_mandatory - + rb"([-+]?[0-9]+)" - + whitespace_mandatory - + rb"obj(?=" - + delimiter_or_ws - + rb")" - ) - re_indirect_def_end = re.compile( - whitespace_optional + rb"endobj(?=" + delimiter_or_ws + rb")" - ) - re_comment = re.compile( - rb"(" + whitespace_optional + rb"%[^\r\n]*" + newline + rb")*" - ) - re_stream_start = re.compile(whitespace_optional + rb"stream\r?\n") - re_stream_end = re.compile( - whitespace_optional + rb"endstream(?=" + delimiter_or_ws + rb")" - ) - - @classmethod - def get_value( - cls, - data: bytes | bytearray | mmap.mmap, - offset: int, - expect_indirect: IndirectReference | None = None, - max_nesting: int = -1, - ) -> tuple[Any, int | None]: - if max_nesting == 0: - return None, None - m = cls.re_comment.match(data, offset) - if m: - offset = m.end() - m = cls.re_indirect_def_start.match(data, offset) - if m: - check_format_condition( - int(m.group(1)) > 0, - "indirect object definition: object ID must be greater than 0", - ) - check_format_condition( - int(m.group(2)) >= 0, - "indirect object definition: generation must be non-negative", - ) - check_format_condition( - expect_indirect is None - or expect_indirect - == IndirectReference(int(m.group(1)), int(m.group(2))), - "indirect object definition different than expected", - ) - object, object_offset = cls.get_value( - data, m.end(), max_nesting=max_nesting - 1 - ) - if object_offset is None: - return object, None - m = cls.re_indirect_def_end.match(data, object_offset) - check_format_condition( - m is not None, "indirect object definition end not found" - ) - assert m is not None - return object, m.end() - check_format_condition( - not expect_indirect, "indirect object definition not found" - ) - m = cls.re_indirect_reference.match(data, offset) - if m: - check_format_condition( - int(m.group(1)) > 0, - "indirect object reference: object ID must be greater than 0", - ) - check_format_condition( - int(m.group(2)) >= 0, - "indirect object reference: generation must be non-negative", - ) - return IndirectReference(int(m.group(1)), int(m.group(2))), m.end() - m = cls.re_dict_start.match(data, offset) - if m: - offset = m.end() - result: dict[Any, Any] = {} - m = cls.re_dict_end.match(data, offset) - current_offset: int | None = offset - while not m: - assert current_offset is not None - key, current_offset = cls.get_value( - data, current_offset, max_nesting=max_nesting - 1 - ) - if current_offset is None: - return result, None - value, current_offset = cls.get_value( - data, current_offset, max_nesting=max_nesting - 1 - ) - result[key] = value - if current_offset is None: - return result, None - m = cls.re_dict_end.match(data, current_offset) - current_offset = m.end() - m = cls.re_stream_start.match(data, current_offset) - if m: - stream_len = result.get(b"Length") - if stream_len is None or not isinstance(stream_len, int): - msg = f"bad or missing Length in stream dict ({stream_len})" - raise PdfFormatError(msg) - stream_data = data[m.end() : m.end() + stream_len] - m = cls.re_stream_end.match(data, m.end() + stream_len) - check_format_condition(m is not None, "stream end not found") - assert m is not None - current_offset = m.end() - return PdfStream(PdfDict(result), stream_data), current_offset - return PdfDict(result), current_offset - m = cls.re_array_start.match(data, offset) - if m: - offset = m.end() - results = [] - m = cls.re_array_end.match(data, offset) - current_offset = offset - while not m: - assert current_offset is not None - value, current_offset = cls.get_value( - data, current_offset, max_nesting=max_nesting - 1 - ) - results.append(value) - if current_offset is None: - return results, None - m = cls.re_array_end.match(data, current_offset) - return results, m.end() - m = cls.re_null.match(data, offset) - if m: - return None, m.end() - m = cls.re_true.match(data, offset) - if m: - return True, m.end() - m = cls.re_false.match(data, offset) - if m: - return False, m.end() - m = cls.re_name.match(data, offset) - if m: - return PdfName(cls.interpret_name(m.group(1))), m.end() - m = cls.re_int.match(data, offset) - if m: - return int(m.group(1)), m.end() - m = cls.re_real.match(data, offset) - if m: - # XXX Decimal instead of float??? - return float(m.group(1)), m.end() - m = cls.re_string_hex.match(data, offset) - if m: - # filter out whitespace - hex_string = bytearray( - b for b in m.group(1) if b in b"0123456789abcdefABCDEF" - ) - if len(hex_string) % 2 == 1: - # append a 0 if the length is not even - yes, at the end - hex_string.append(ord(b"0")) - return bytearray.fromhex(hex_string.decode("us-ascii")), m.end() - m = cls.re_string_lit.match(data, offset) - if m: - return cls.get_literal_string(data, m.end()) - # return None, offset # fallback (only for debugging) - msg = f"unrecognized object: {repr(data[offset : offset + 32])}" - raise PdfFormatError(msg) - - re_lit_str_token = re.compile( - rb"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))" - ) - escaped_chars = { - b"n": b"\n", - b"r": b"\r", - b"t": b"\t", - b"b": b"\b", - b"f": b"\f", - b"(": b"(", - b")": b")", - b"\\": b"\\", - ord(b"n"): b"\n", - ord(b"r"): b"\r", - ord(b"t"): b"\t", - ord(b"b"): b"\b", - ord(b"f"): b"\f", - ord(b"("): b"(", - ord(b")"): b")", - ord(b"\\"): b"\\", - } - - @classmethod - def get_literal_string( - cls, data: bytes | bytearray | mmap.mmap, offset: int - ) -> tuple[bytes, int]: - nesting_depth = 0 - result = bytearray() - for m in cls.re_lit_str_token.finditer(data, offset): - result.extend(data[offset : m.start()]) - if m.group(1): - result.extend(cls.escaped_chars[m.group(1)[1]]) - elif m.group(2): - result.append(int(m.group(2)[1:], 8)) - elif m.group(3): - pass - elif m.group(5): - result.extend(b"\n") - elif m.group(6): - result.extend(b"(") - nesting_depth += 1 - elif m.group(7): - if nesting_depth == 0: - return bytes(result), m.end() - result.extend(b")") - nesting_depth -= 1 - offset = m.end() - msg = "unfinished literal string" - raise PdfFormatError(msg) - - re_xref_section_start = re.compile(whitespace_optional + rb"xref" + newline) - re_xref_subsection_start = re.compile( - whitespace_optional - + rb"([0-9]+)" - + whitespace_mandatory - + rb"([0-9]+)" - + whitespace_optional - + newline_only - ) - re_xref_entry = re.compile(rb"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)") - - def read_xref_table(self, xref_section_offset: int) -> int: - assert self.buf is not None - subsection_found = False - m = self.re_xref_section_start.match( - self.buf, xref_section_offset + self.start_offset - ) - check_format_condition(m is not None, "xref section start not found") - assert m is not None - offset = m.end() - while True: - m = self.re_xref_subsection_start.match(self.buf, offset) - if not m: - check_format_condition( - subsection_found, "xref subsection start not found" - ) - break - subsection_found = True - offset = m.end() - first_object = int(m.group(1)) - num_objects = int(m.group(2)) - for i in range(first_object, first_object + num_objects): - m = self.re_xref_entry.match(self.buf, offset) - check_format_condition(m is not None, "xref entry not found") - assert m is not None - offset = m.end() - is_free = m.group(3) == b"f" - if not is_free: - generation = int(m.group(2)) - new_entry = (int(m.group(1)), generation) - if i not in self.xref_table: - self.xref_table[i] = new_entry - return offset - - def read_indirect(self, ref: IndirectReference, max_nesting: int = -1) -> Any: - offset, generation = self.xref_table[ref[0]] - check_format_condition( - generation == ref[1], - f"expected to find generation {ref[1]} for object ID {ref[0]} in xref " - f"table, instead found generation {generation} at offset {offset}", - ) - assert self.buf is not None - value = self.get_value( - self.buf, - offset + self.start_offset, - expect_indirect=IndirectReference(*ref), - max_nesting=max_nesting, - )[0] - self.cached_objects[ref] = value - return value - - def linearize_page_tree( - self, node: PdfDict | None = None - ) -> list[IndirectReference]: - page_node = node if node is not None else self.page_tree_root - check_format_condition( - page_node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages" - ) - pages = [] - for kid in page_node[b"Kids"]: - kid_object = self.read_indirect(kid) - if kid_object[b"Type"] == b"Page": - pages.append(kid) - else: - pages.extend(self.linearize_page_tree(node=kid_object)) - return pages diff --git a/.venv/lib/python3.12/site-packages/PIL/PixarImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/PixarImagePlugin.py deleted file mode 100644 index d2b6d0a9..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PixarImagePlugin.py +++ /dev/null @@ -1,72 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PIXAR raster support for PIL -# -# history: -# 97-01-29 fl Created -# -# notes: -# This is incomplete; it is based on a few samples created with -# Photoshop 2.5 and 3.0, and a summary description provided by -# Greg Coats . Hopefully, "L" and -# "RGBA" support will be added in future versions. -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile -from ._binary import i16le as i16 - -# -# helpers - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"\200\350\000\000") - - -## -# Image plugin for PIXAR raster images. - - -class PixarImageFile(ImageFile.ImageFile): - format = "PIXAR" - format_description = "PIXAR raster image" - - def _open(self) -> None: - # assuming a 4-byte magic label - assert self.fp is not None - - s = self.fp.read(4) - if not _accept(s): - msg = "not a PIXAR file" - raise SyntaxError(msg) - - # read rest of header - s = s + self.fp.read(508) - - self._size = i16(s, 418), i16(s, 416) - - # get channel/depth descriptions - mode = i16(s, 424), i16(s, 426) - - if mode == (14, 2): - self._mode = "RGB" - # FIXME: to be continued... - - # create tile descriptor (assuming "dumped") - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 1024, self.mode)] - - -# -# -------------------------------------------------------------------- - -Image.register_open(PixarImageFile.format, PixarImageFile, _accept) - -Image.register_extension(PixarImageFile.format, ".pxr") diff --git a/.venv/lib/python3.12/site-packages/PIL/PngImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/PngImagePlugin.py deleted file mode 100644 index 76a15bd0..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PngImagePlugin.py +++ /dev/null @@ -1,1563 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PNG support code -# -# See "PNG (Portable Network Graphics) Specification, version 1.0; -# W3C Recommendation", 1996-10-01, Thomas Boutell (ed.). -# -# history: -# 1996-05-06 fl Created (couldn't resist it) -# 1996-12-14 fl Upgraded, added read and verify support (0.2) -# 1996-12-15 fl Separate PNG stream parser -# 1996-12-29 fl Added write support, added getchunks -# 1996-12-30 fl Eliminated circular references in decoder (0.3) -# 1998-07-12 fl Read/write 16-bit images as mode I (0.4) -# 2001-02-08 fl Added transparency support (from Zircon) (0.5) -# 2001-04-16 fl Don't close data source in "open" method (0.6) -# 2004-02-24 fl Don't even pretend to support interlaced files (0.7) -# 2004-08-31 fl Do basic sanity check on chunk identifiers (0.8) -# 2004-09-20 fl Added PngInfo chunk container -# 2004-12-18 fl Added DPI read support (based on code by Niki Spahiev) -# 2008-08-13 fl Added tRNS support for RGB images -# 2009-03-06 fl Support for preserving ICC profiles (by Florian Hoech) -# 2009-03-08 fl Added zTXT support (from Lowell Alleman) -# 2009-03-29 fl Read interlaced PNG files (from Conrado Porto Lopes Gouvua) -# -# Copyright (c) 1997-2009 by Secret Labs AB -# Copyright (c) 1996 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import itertools -import logging -import re -import struct -import warnings -import zlib -from enum import IntEnum -from fractions import Fraction -from typing import IO, NamedTuple, cast - -from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._binary import o8 -from ._binary import o16be as o16 -from ._binary import o32be as o32 -from ._deprecate import deprecate -from ._util import DeferredError - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from typing import Any, NoReturn - - from . import _imaging - -logger = logging.getLogger(__name__) - -is_cid = re.compile(rb"\w\w\w\w").match - - -_MAGIC = b"\211PNG\r\n\032\n" - - -_MODES = { - # supported bits/color combinations, and corresponding modes/rawmodes - # Grayscale - (1, 0): ("1", "1"), - (2, 0): ("L", "L;2"), - (4, 0): ("L", "L;4"), - (8, 0): ("L", "L"), - (16, 0): ("I;16", "I;16B"), - # Truecolour - (8, 2): ("RGB", "RGB"), - (16, 2): ("RGB", "RGB;16B"), - # Indexed-colour - (1, 3): ("P", "P;1"), - (2, 3): ("P", "P;2"), - (4, 3): ("P", "P;4"), - (8, 3): ("P", "P"), - # Grayscale with alpha - (8, 4): ("LA", "LA"), - (16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available - # Truecolour with alpha - (8, 6): ("RGBA", "RGBA"), - (16, 6): ("RGBA", "RGBA;16B"), -} - - -_simple_palette = re.compile(b"^\xff*\x00\xff*$") - -MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK -""" -Maximum decompressed size for a iTXt or zTXt chunk. -Eliminates decompression bombs where compressed chunks can expand 1000x. -See :ref:`Text in PNG File Format`. -""" -MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK -""" -Set the maximum total text chunk size. -See :ref:`Text in PNG File Format`. -""" - - -# APNG frame disposal modes -class Disposal(IntEnum): - OP_NONE = 0 - """ - No disposal is done on this frame before rendering the next frame. - See :ref:`Saving APNG sequences`. - """ - OP_BACKGROUND = 1 - """ - This frame’s modified region is cleared to fully transparent black before rendering - the next frame. - See :ref:`Saving APNG sequences`. - """ - OP_PREVIOUS = 2 - """ - This frame’s modified region is reverted to the previous frame’s contents before - rendering the next frame. - See :ref:`Saving APNG sequences`. - """ - - -# APNG frame blend modes -class Blend(IntEnum): - OP_SOURCE = 0 - """ - All color components of this frame, including alpha, overwrite the previous output - image contents. - See :ref:`Saving APNG sequences`. - """ - OP_OVER = 1 - """ - This frame should be alpha composited with the previous output image contents. - See :ref:`Saving APNG sequences`. - """ - - -def _safe_zlib_decompress(s: bytes) -> bytes: - dobj = zlib.decompressobj() - plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) - if dobj.unconsumed_tail: - msg = "Decompressed data too large for PngImagePlugin.MAX_TEXT_CHUNK" - raise ValueError(msg) - return plaintext - - -def _crc32(data: bytes, seed: int = 0) -> int: - return zlib.crc32(data, seed) & 0xFFFFFFFF - - -# -------------------------------------------------------------------- -# Support classes. Suitable for PNG and related formats like MNG etc. - - -class ChunkStream: - def __init__(self, fp: IO[bytes]) -> None: - self.fp: IO[bytes] | None = fp - self.queue: list[tuple[bytes, int, int]] | None = [] - - def read(self) -> tuple[bytes, int, int]: - """Fetch a new chunk. Returns header information.""" - cid = None - - assert self.fp is not None - if self.queue: - cid, pos, length = self.queue.pop() - self.fp.seek(pos) - else: - s = self.fp.read(8) - cid = s[4:] - pos = self.fp.tell() - length = i32(s) - - if not is_cid(cid): - if not ImageFile.LOAD_TRUNCATED_IMAGES: - msg = f"broken PNG file (chunk {repr(cid)})" - raise SyntaxError(msg) - - return cid, pos, length - - def __enter__(self) -> ChunkStream: - return self - - def __exit__(self, *args: object) -> None: - self.close() - - def close(self) -> None: - self.queue = self.fp = None - - def push(self, cid: bytes, pos: int, length: int) -> None: - assert self.queue is not None - self.queue.append((cid, pos, length)) - - def call(self, cid: bytes, pos: int, length: int) -> bytes: - """Call the appropriate chunk handler""" - - logger.debug("STREAM %r %s %s", cid, pos, length) - return getattr(self, f"chunk_{cid.decode('ascii')}")(pos, length) - - def crc(self, cid: bytes, data: bytes) -> None: - """Read and verify checksum""" - - # Skip CRC checks for ancillary chunks if allowed to load truncated - # images - # 5th byte of first char is 1 [specs, section 5.4] - if ImageFile.LOAD_TRUNCATED_IMAGES and (cid[0] >> 5 & 1): - self.crc_skip(cid, data) - return - - assert self.fp is not None - try: - crc1 = _crc32(data, _crc32(cid)) - crc2 = i32(self.fp.read(4)) - if crc1 != crc2: - msg = f"broken PNG file (bad header checksum in {repr(cid)})" - raise SyntaxError(msg) - except struct.error as e: - msg = f"broken PNG file (incomplete checksum in {repr(cid)})" - raise SyntaxError(msg) from e - - def crc_skip(self, cid: bytes, data: bytes) -> None: - """Read checksum""" - - assert self.fp is not None - self.fp.read(4) - - def verify(self, endchunk: bytes = b"IEND") -> list[bytes]: - # Simple approach; just calculate checksum for all remaining - # blocks. Must be called directly after open. - - cids = [] - - assert self.fp is not None - while True: - try: - cid, pos, length = self.read() - except struct.error as e: - msg = "truncated PNG file" - raise OSError(msg) from e - - if cid == endchunk: - break - self.crc(cid, ImageFile._safe_read(self.fp, length)) - cids.append(cid) - - return cids - - -class iTXt(str): - """ - Subclass of string to allow iTXt chunks to look like strings while - keeping their extra information - - """ - - lang: str | bytes | None - tkey: str | bytes | None - - @staticmethod - def __new__( - cls, text: str, lang: str | None = None, tkey: str | None = None - ) -> iTXt: - """ - :param cls: the class to use when creating the instance - :param text: value for this key - :param lang: language code - :param tkey: UTF-8 version of the key name - """ - - self = str.__new__(cls, text) - self.lang = lang - self.tkey = tkey - return self - - -class PngInfo: - """ - PNG chunk container (for use with save(pnginfo=)) - - """ - - def __init__(self) -> None: - self.chunks: list[tuple[bytes, bytes, bool]] = [] - - def add(self, cid: bytes, data: bytes, after_idat: bool = False) -> None: - """Appends an arbitrary chunk. Use with caution. - - :param cid: a byte string, 4 bytes long. - :param data: a byte string of the encoded data - :param after_idat: for use with private chunks. Whether the chunk - should be written after IDAT - - """ - - self.chunks.append((cid, data, after_idat)) - - def add_itxt( - self, - key: str | bytes, - value: str | bytes, - lang: str | bytes = "", - tkey: str | bytes = "", - zip: bool = False, - ) -> None: - """Appends an iTXt chunk. - - :param key: latin-1 encodable text key name - :param value: value for this key - :param lang: language code - :param tkey: UTF-8 version of the key name - :param zip: compression flag - - """ - - if not isinstance(key, bytes): - key = key.encode("latin-1", "strict") - if not isinstance(value, bytes): - value = value.encode("utf-8", "strict") - if not isinstance(lang, bytes): - lang = lang.encode("utf-8", "strict") - if not isinstance(tkey, bytes): - tkey = tkey.encode("utf-8", "strict") - - if zip: - self.add( - b"iTXt", - key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" + zlib.compress(value), - ) - else: - self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" + value) - - def add_text( - self, key: str | bytes, value: str | bytes | iTXt, zip: bool = False - ) -> None: - """Appends a text chunk. - - :param key: latin-1 encodable text key name - :param value: value for this key, text or an - :py:class:`PIL.PngImagePlugin.iTXt` instance - :param zip: compression flag - - """ - if isinstance(value, iTXt): - return self.add_itxt( - key, - value, - value.lang if value.lang is not None else b"", - value.tkey if value.tkey is not None else b"", - zip=zip, - ) - - # The tEXt chunk stores latin-1 text - if not isinstance(value, bytes): - try: - value = value.encode("latin-1", "strict") - except UnicodeError: - return self.add_itxt(key, value, zip=zip) - - if not isinstance(key, bytes): - key = key.encode("latin-1", "strict") - - if zip: - self.add(b"zTXt", key + b"\0\0" + zlib.compress(value)) - else: - self.add(b"tEXt", key + b"\0" + value) - - -# -------------------------------------------------------------------- -# PNG image stream (IHDR/IEND) - - -class _RewindState(NamedTuple): - info: dict[str | tuple[int, int], Any] - tile: list[ImageFile._Tile] - seq_num: int | None - - -class PngStream(ChunkStream): - def __init__(self, fp: IO[bytes]) -> None: - super().__init__(fp) - - # local copies of Image attributes - self.im_info: dict[str | tuple[int, int], Any] = {} - self.im_text: dict[str, str | iTXt] = {} - self.im_size = (0, 0) - self.im_mode = "" - self.im_tile: list[ImageFile._Tile] = [] - self.im_palette: tuple[str, bytes] | None = None - self.im_custom_mimetype: str | None = None - self.im_n_frames: int | None = None - self._seq_num: int | None = None - self.rewind_state = _RewindState({}, [], None) - - self.text_memory = 0 - - def check_text_memory(self, chunklen: int) -> None: - self.text_memory += chunklen - if self.text_memory > MAX_TEXT_MEMORY: - msg = ( - "Too much memory used in text chunks: " - f"{self.text_memory}>MAX_TEXT_MEMORY" - ) - raise ValueError(msg) - - def save_rewind(self) -> None: - self.rewind_state = _RewindState( - self.im_info.copy(), - self.im_tile, - self._seq_num, - ) - - def rewind(self) -> None: - self.im_info = self.rewind_state.info.copy() - self.im_tile = self.rewind_state.tile - self._seq_num = self.rewind_state.seq_num - - def chunk_iCCP(self, pos: int, length: int) -> bytes: - # ICC profile - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - # according to PNG spec, the iCCP chunk contains: - # Profile name 1-79 bytes (character string) - # Null separator 1 byte (null character) - # Compression method 1 byte (0) - # Compressed profile n bytes (zlib with deflate compression) - i = s.find(b"\0") - logger.debug("iCCP profile name %r", s[:i]) - comp_method = s[i + 1] - logger.debug("Compression method %s", comp_method) - if comp_method != 0: - msg = f"Unknown compression method {comp_method} in iCCP chunk" - raise SyntaxError(msg) - try: - icc_profile = _safe_zlib_decompress(s[i + 2 :]) - except ValueError: - if ImageFile.LOAD_TRUNCATED_IMAGES: - icc_profile = None - else: - raise - except zlib.error: - icc_profile = None # FIXME - self.im_info["icc_profile"] = icc_profile - return s - - def chunk_IHDR(self, pos: int, length: int) -> bytes: - # image header - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if length < 13: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - msg = "Truncated IHDR chunk" - raise ValueError(msg) - self.im_size = i32(s, 0), i32(s, 4) - try: - self.im_mode, self.im_rawmode = _MODES[(s[8], s[9])] - except Exception: - pass - if s[12]: - self.im_info["interlace"] = 1 - if s[11]: - msg = "unknown filter category" - raise SyntaxError(msg) - return s - - def chunk_IDAT(self, pos: int, length: int) -> NoReturn: - # image data - if "bbox" in self.im_info: - tile = [ImageFile._Tile("zip", self.im_info["bbox"], pos, self.im_rawmode)] - else: - if self.im_n_frames is not None: - self.im_info["default_image"] = True - tile = [ImageFile._Tile("zip", (0, 0) + self.im_size, pos, self.im_rawmode)] - self.im_tile = tile - self.im_idat = length - msg = "image data found" - raise EOFError(msg) - - def chunk_IEND(self, pos: int, length: int) -> NoReturn: - msg = "end of PNG image" - raise EOFError(msg) - - def chunk_PLTE(self, pos: int, length: int) -> bytes: - # palette - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if self.im_mode == "P": - self.im_palette = "RGB", s - return s - - def chunk_tRNS(self, pos: int, length: int) -> bytes: - # transparency - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if self.im_mode == "P": - if _simple_palette.match(s): - # tRNS contains only one full-transparent entry, - # other entries are full opaque - i = s.find(b"\0") - if i >= 0: - self.im_info["transparency"] = i - else: - # otherwise, we have a byte string with one alpha value - # for each palette entry - self.im_info["transparency"] = s - elif self.im_mode == "1": - self.im_info["transparency"] = 255 if i16(s) else 0 - elif self.im_mode in ("L", "I;16"): - self.im_info["transparency"] = i16(s) - elif self.im_mode == "RGB": - self.im_info["transparency"] = i16(s), i16(s, 2), i16(s, 4) - return s - - def chunk_gAMA(self, pos: int, length: int) -> bytes: - # gamma setting - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - self.im_info["gamma"] = i32(s) / 100000.0 - return s - - def chunk_cHRM(self, pos: int, length: int) -> bytes: - # chromaticity, 8 unsigned ints, actual value is scaled by 100,000 - # WP x,y, Red x,y, Green x,y Blue x,y - - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - raw_vals = struct.unpack(f">{len(s) // 4}I", s) - self.im_info["chromaticity"] = tuple(elt / 100000.0 for elt in raw_vals) - return s - - def chunk_sRGB(self, pos: int, length: int) -> bytes: - # srgb rendering intent, 1 byte - # 0 perceptual - # 1 relative colorimetric - # 2 saturation - # 3 absolute colorimetric - - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if length < 1: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - msg = "Truncated sRGB chunk" - raise ValueError(msg) - self.im_info["srgb"] = s[0] - return s - - def chunk_pHYs(self, pos: int, length: int) -> bytes: - # pixels per unit - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if length < 9: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - msg = "Truncated pHYs chunk" - raise ValueError(msg) - px, py = i32(s, 0), i32(s, 4) - unit = s[8] - if unit == 1: # meter - dpi = px * 0.0254, py * 0.0254 - self.im_info["dpi"] = dpi - elif unit == 0: - self.im_info["aspect"] = px, py - return s - - def chunk_tEXt(self, pos: int, length: int) -> bytes: - # text - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - try: - k, v = s.split(b"\0", 1) - except ValueError: - # fallback for broken tEXt tags - k = s - v = b"" - if k: - k_str = k.decode("latin-1", "strict") - v_str = v.decode("latin-1", "replace") - - self.im_info[k_str] = v if k == b"exif" else v_str - self.im_text[k_str] = v_str - self.check_text_memory(len(v_str)) - - return s - - def chunk_zTXt(self, pos: int, length: int) -> bytes: - # compressed text - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - try: - k, v = s.split(b"\0", 1) - except ValueError: - k = s - v = b"" - if v: - comp_method = v[0] - else: - comp_method = 0 - if comp_method != 0: - msg = f"Unknown compression method {comp_method} in zTXt chunk" - raise SyntaxError(msg) - try: - v = _safe_zlib_decompress(v[1:]) - except ValueError: - if ImageFile.LOAD_TRUNCATED_IMAGES: - v = b"" - else: - raise - except zlib.error: - v = b"" - - if k: - k_str = k.decode("latin-1", "strict") - v_str = v.decode("latin-1", "replace") - - self.im_info[k_str] = self.im_text[k_str] = v_str - self.check_text_memory(len(v_str)) - - return s - - def chunk_iTXt(self, pos: int, length: int) -> bytes: - # international text - assert self.fp is not None - r = s = ImageFile._safe_read(self.fp, length) - try: - k, r = r.split(b"\0", 1) - except ValueError: - return s - if len(r) < 2: - return s - cf, cm, r = r[0], r[1], r[2:] - try: - lang, tk, v = r.split(b"\0", 2) - except ValueError: - return s - if cf != 0: - if cm == 0: - try: - v = _safe_zlib_decompress(v) - except ValueError: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - else: - raise - except zlib.error: - return s - else: - return s - if k == b"XML:com.adobe.xmp": - self.im_info["xmp"] = v - try: - k_str = k.decode("latin-1", "strict") - lang_str = lang.decode("utf-8", "strict") - tk_str = tk.decode("utf-8", "strict") - v_str = v.decode("utf-8", "strict") - except UnicodeError: - return s - - self.im_info[k_str] = self.im_text[k_str] = iTXt(v_str, lang_str, tk_str) - self.check_text_memory(len(v_str)) - - return s - - def chunk_eXIf(self, pos: int, length: int) -> bytes: - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - self.im_info["exif"] = b"Exif\x00\x00" + s - return s - - # APNG chunks - def chunk_acTL(self, pos: int, length: int) -> bytes: - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if length < 8: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - msg = "APNG contains truncated acTL chunk" - raise ValueError(msg) - if self.im_n_frames is not None: - self.im_n_frames = None - warnings.warn("Invalid APNG, will use default PNG image if possible") - return s - n_frames = i32(s) - if n_frames == 0 or n_frames > 0x80000000: - warnings.warn("Invalid APNG, will use default PNG image if possible") - return s - self.im_n_frames = n_frames - self.im_info["loop"] = i32(s, 4) - self.im_custom_mimetype = "image/apng" - return s - - def chunk_fcTL(self, pos: int, length: int) -> bytes: - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if length < 26: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - msg = "APNG contains truncated fcTL chunk" - raise ValueError(msg) - seq = i32(s) - if (self._seq_num is None and seq != 0) or ( - self._seq_num is not None and self._seq_num != seq - 1 - ): - msg = "APNG contains frame sequence errors" - raise SyntaxError(msg) - self._seq_num = seq - width, height = i32(s, 4), i32(s, 8) - px, py = i32(s, 12), i32(s, 16) - im_w, im_h = self.im_size - if px + width > im_w or py + height > im_h: - msg = "APNG contains invalid frames" - raise SyntaxError(msg) - self.im_info["bbox"] = (px, py, px + width, py + height) - delay_num, delay_den = i16(s, 20), i16(s, 22) - if delay_den == 0: - delay_den = 100 - self.im_info["duration"] = float(delay_num) / float(delay_den) * 1000 - self.im_info["disposal"] = s[24] - self.im_info["blend"] = s[25] - return s - - def chunk_fdAT(self, pos: int, length: int) -> bytes: - assert self.fp is not None - if length < 4: - if ImageFile.LOAD_TRUNCATED_IMAGES: - s = ImageFile._safe_read(self.fp, length) - return s - msg = "APNG contains truncated fDAT chunk" - raise ValueError(msg) - s = ImageFile._safe_read(self.fp, 4) - seq = i32(s) - if self._seq_num != seq - 1: - msg = "APNG contains frame sequence errors" - raise SyntaxError(msg) - self._seq_num = seq - return self.chunk_IDAT(pos + 4, length - 4) - - -# -------------------------------------------------------------------- -# PNG reader - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(_MAGIC) - - -## -# Image plugin for PNG images. - - -class PngImageFile(ImageFile.ImageFile): - format = "PNG" - format_description = "Portable network graphics" - - def _open(self) -> None: - assert self.fp is not None - if not _accept(self.fp.read(8)): - msg = "not a PNG file" - raise SyntaxError(msg) - self._fp = self.fp - self.__frame = 0 - - # - # Parse headers up to the first IDAT or fDAT chunk - - self.private_chunks: list[tuple[bytes, bytes] | tuple[bytes, bytes, bool]] = [] - self.png: PngStream | None = PngStream(self.fp) - - while True: - # - # get next chunk - - cid, pos, length = self.png.read() - - try: - s = self.png.call(cid, pos, length) - except EOFError: - break - except AttributeError: - logger.debug("%r %s %s (unknown)", cid, pos, length) - s = ImageFile._safe_read(self.fp, length) - if cid[1:2].islower(): - self.private_chunks.append((cid, s)) - - self.png.crc(cid, s) - - # - # Copy relevant attributes from the PngStream. An alternative - # would be to let the PngStream class modify these attributes - # directly, but that introduces circular references which are - # difficult to break if things go wrong in the decoder... - # (believe me, I've tried ;-) - - self._mode = self.png.im_mode - self._size = self.png.im_size - self.info = self.png.im_info - self._text: dict[str, str | iTXt] | None = None - self.tile = self.png.im_tile - self.custom_mimetype = self.png.im_custom_mimetype - self.n_frames = self.png.im_n_frames or 1 - self.default_image = self.info.get("default_image", False) - - if self.png.im_palette: - rawmode, data = self.png.im_palette - self.palette = ImagePalette.raw(rawmode, data) - - if cid == b"fdAT": - self.__prepare_idat = length - 4 - else: - self.__prepare_idat = length # used by load_prepare() - - if self.png.im_n_frames is not None: - self._close_exclusive_fp_after_loading = False - self.png.save_rewind() - self.__rewind_idat = self.__prepare_idat - self.__rewind = self._fp.tell() - if self.default_image: - # IDAT chunk contains default image and not first animation frame - self.n_frames += 1 - self._seek(0) - self.is_animated = self.n_frames > 1 - - @property - def text(self) -> dict[str, str | iTXt]: - # experimental - if self._text is None: - # iTxt, tEXt and zTXt chunks may appear at the end of the file - # So load the file to ensure that they are read - if self.is_animated: - frame = self.__frame - # for APNG, seek to the final frame before loading - self.seek(self.n_frames - 1) - self.load() - if self.is_animated: - self.seek(frame) - assert self._text is not None - return self._text - - def verify(self) -> None: - """Verify PNG file""" - - if self.fp is None: - msg = "verify must be called directly after open" - raise RuntimeError(msg) - - # back up to beginning of IDAT block - self.fp.seek(self.tile[0][2] - 8) - - assert self.png is not None - self.png.verify() - self.png.close() - - super().verify() - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if frame < self.__frame: - self._seek(0, True) - - last_frame = self.__frame - try: - for f in range(self.__frame + 1, frame + 1): - self._seek(f) - except EOFError as e: - self.seek(last_frame) - msg = "no more images in APNG file" - raise EOFError(msg) from e - - def _seek(self, frame: int, rewind: bool = False) -> None: - assert self.png is not None - if isinstance(self._fp, DeferredError): - raise self._fp.ex - - self.dispose: _imaging.ImagingCore | None - dispose_extent = None - if frame == 0: - if rewind: - self._fp.seek(self.__rewind) - self.png.rewind() - self.__prepare_idat = self.__rewind_idat - self._im = None - self.info = self.png.im_info - self.tile = self.png.im_tile - self.fp = self._fp - self._prev_im = None - self.dispose = None - self.default_image = self.info.get("default_image", False) - self.dispose_op = self.info.get("disposal") - self.blend_op = self.info.get("blend") - dispose_extent = self.info.get("bbox") - self.__frame = 0 - else: - if frame != self.__frame + 1: - msg = f"cannot seek to frame {frame}" - raise ValueError(msg) - - # ensure previous frame was loaded - self.load() - - if self.dispose: - self.im.paste(self.dispose, self.dispose_extent) - self._prev_im = self.im.copy() - - self.fp = self._fp - - # advance to the next frame - if self.__prepare_idat: - ImageFile._safe_read(self.fp, self.__prepare_idat) - self.__prepare_idat = 0 - frame_start = False - while True: - self.fp.read(4) # CRC - - try: - cid, pos, length = self.png.read() - except (struct.error, SyntaxError): - break - - if cid == b"IEND": - msg = "No more images in APNG file" - raise EOFError(msg) - if cid == b"fcTL": - if frame_start: - # there must be at least one fdAT chunk between fcTL chunks - msg = "APNG missing frame data" - raise SyntaxError(msg) - frame_start = True - - try: - self.png.call(cid, pos, length) - except UnicodeDecodeError: - break - except EOFError: - if cid == b"fdAT": - length -= 4 - if frame_start: - self.__prepare_idat = length - break - ImageFile._safe_read(self.fp, length) - except AttributeError: - logger.debug("%r %s %s (unknown)", cid, pos, length) - ImageFile._safe_read(self.fp, length) - - self.__frame = frame - self.tile = self.png.im_tile - self.dispose_op = self.info.get("disposal") - self.blend_op = self.info.get("blend") - dispose_extent = self.info.get("bbox") - - if not self.tile: - msg = "image not found in APNG frame" - raise EOFError(msg) - if dispose_extent: - self.dispose_extent: tuple[float, float, float, float] = dispose_extent - - # setup frame disposal (actual disposal done when needed in the next _seek()) - if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS: - self.dispose_op = Disposal.OP_BACKGROUND - - self.dispose = None - if self.dispose_op == Disposal.OP_PREVIOUS: - if self._prev_im: - self.dispose = self._prev_im.copy() - self.dispose = self._crop(self.dispose, self.dispose_extent) - elif self.dispose_op == Disposal.OP_BACKGROUND: - self.dispose = Image.core.fill(self.mode, self.size) - self.dispose = self._crop(self.dispose, self.dispose_extent) - - def tell(self) -> int: - return self.__frame - - def load_prepare(self) -> None: - """internal: prepare to read PNG file""" - - if self.info.get("interlace"): - self.decoderconfig = self.decoderconfig + (1,) - - self.__idat = self.__prepare_idat # used by load_read() - ImageFile.ImageFile.load_prepare(self) - - def load_read(self, read_bytes: int) -> bytes: - """internal: read more image data""" - - assert self.png is not None - assert self.fp is not None - while self.__idat == 0: - # end of chunk, skip forward to next one - - self.fp.read(4) # CRC - - cid, pos, length = self.png.read() - - if cid not in [b"IDAT", b"DDAT", b"fdAT"]: - self.png.push(cid, pos, length) - return b"" - - if cid == b"fdAT": - try: - self.png.call(cid, pos, length) - except EOFError: - pass - self.__idat = length - 4 # sequence_num has already been read - else: - self.__idat = length # empty chunks are allowed - - # read more data from this chunk - if read_bytes <= 0: - read_bytes = self.__idat - else: - read_bytes = min(read_bytes, self.__idat) - - self.__idat = self.__idat - read_bytes - - return self.fp.read(read_bytes) - - def load_end(self) -> None: - """internal: finished reading image data""" - assert self.png is not None - assert self.fp is not None - if self.__idat != 0: - self.fp.read(self.__idat) - while True: - self.fp.read(4) # CRC - - try: - cid, pos, length = self.png.read() - except (struct.error, SyntaxError): - break - - if cid == b"IEND": - break - elif cid == b"fcTL" and self.is_animated: - # start of the next frame, stop reading - self.__prepare_idat = 0 - self.png.push(cid, pos, length) - break - - try: - self.png.call(cid, pos, length) - except UnicodeDecodeError: - break - except EOFError: - if cid == b"fdAT": - length -= 4 - try: - ImageFile._safe_read(self.fp, length) - except OSError as e: - if ImageFile.LOAD_TRUNCATED_IMAGES: - break - else: - raise e - except AttributeError: - logger.debug("%r %s %s (unknown)", cid, pos, length) - s = ImageFile._safe_read(self.fp, length) - if cid[1:2].islower(): - self.private_chunks.append((cid, s, True)) - self._text = self.png.im_text - if not self.is_animated: - self.png.close() - self.png = None - else: - if self._prev_im and self.blend_op == Blend.OP_OVER: - updated = self._crop(self.im, self.dispose_extent) - if self.im.mode == "RGB" and "transparency" in self.info: - mask = updated.convert_transparent( - "RGBA", self.info["transparency"] - ) - else: - if self.im.mode == "P" and "transparency" in self.info: - t = self.info["transparency"] - if isinstance(t, bytes): - updated.putpalettealphas(t) - elif isinstance(t, int): - updated.putpalettealpha(t) - mask = updated.convert("RGBA") - self._prev_im.paste(updated, self.dispose_extent, mask) - self.im = self._prev_im - - def _getexif(self) -> dict[int, Any] | None: - if "exif" not in self.info: - self.load() - if "exif" not in self.info and "Raw profile type exif" not in self.info: - return None - return self.getexif()._get_merged_dict() - - def getexif(self) -> Image.Exif: - if "exif" not in self.info: - self.load() - - return super().getexif() - - -# -------------------------------------------------------------------- -# PNG writer - -_OUTMODES = { - # supported PIL modes, and corresponding rawmode, bit depth and color type - "1": ("1", b"\x01", b"\x00"), - "L;1": ("L;1", b"\x01", b"\x00"), - "L;2": ("L;2", b"\x02", b"\x00"), - "L;4": ("L;4", b"\x04", b"\x00"), - "L": ("L", b"\x08", b"\x00"), - "LA": ("LA", b"\x08", b"\x04"), - "I": ("I;16B", b"\x10", b"\x00"), - "I;16": ("I;16B", b"\x10", b"\x00"), - "I;16B": ("I;16B", b"\x10", b"\x00"), - "P;1": ("P;1", b"\x01", b"\x03"), - "P;2": ("P;2", b"\x02", b"\x03"), - "P;4": ("P;4", b"\x04", b"\x03"), - "P": ("P", b"\x08", b"\x03"), - "RGB": ("RGB", b"\x08", b"\x02"), - "RGBA": ("RGBA", b"\x08", b"\x06"), -} - - -def putchunk(fp: IO[bytes], cid: bytes, *data: bytes) -> None: - """Write a PNG chunk (including CRC field)""" - - byte_data = b"".join(data) - - fp.write(o32(len(byte_data)) + cid) - fp.write(byte_data) - crc = _crc32(byte_data, _crc32(cid)) - fp.write(o32(crc)) - - -class _idat: - # wrap output from the encoder in IDAT chunks - - def __init__(self, fp: IO[bytes], chunk: Callable[..., None]) -> None: - self.fp = fp - self.chunk = chunk - - def write(self, data: bytes) -> None: - self.chunk(self.fp, b"IDAT", data) - - -class _fdat: - # wrap encoder output in fdAT chunks - - def __init__(self, fp: IO[bytes], chunk: Callable[..., None], seq_num: int) -> None: - self.fp = fp - self.chunk = chunk - self.seq_num = seq_num - - def write(self, data: bytes) -> None: - self.chunk(self.fp, b"fdAT", o32(self.seq_num), data) - self.seq_num += 1 - - -def _apply_encoderinfo(im: Image.Image, encoderinfo: dict[str, Any]) -> None: - im.encoderconfig = ( - encoderinfo.get("optimize", False), - encoderinfo.get("compress_level", -1), - encoderinfo.get("compress_type", -1), - encoderinfo.get("dictionary", b""), - ) - - -class _Frame(NamedTuple): - im: Image.Image - bbox: tuple[int, int, int, int] | None - encoderinfo: dict[str, Any] - - -def _write_multiple_frames( - im: Image.Image, - fp: IO[bytes], - chunk: Callable[..., None], - mode: str, - rawmode: str, - default_image: Image.Image | None, - append_images: list[Image.Image], -) -> Image.Image | None: - duration = im.encoderinfo.get("duration") - loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) - disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE)) - blend = im.encoderinfo.get("blend", im.info.get("blend", Blend.OP_SOURCE)) - - if default_image: - chain = itertools.chain(append_images) - else: - chain = itertools.chain([im], append_images) - - im_frames: list[_Frame] = [] - frame_count = 0 - for im_seq in chain: - for im_frame in ImageSequence.Iterator(im_seq): - if im_frame.mode == mode: - im_frame = im_frame.copy() - else: - im_frame = im_frame.convert(mode) - encoderinfo = im.encoderinfo.copy() - if isinstance(duration, (list, tuple)): - encoderinfo["duration"] = duration[frame_count] - elif duration is None and "duration" in im_frame.info: - encoderinfo["duration"] = im_frame.info["duration"] - if isinstance(disposal, (list, tuple)): - encoderinfo["disposal"] = disposal[frame_count] - if isinstance(blend, (list, tuple)): - encoderinfo["blend"] = blend[frame_count] - frame_count += 1 - - if im_frames: - previous = im_frames[-1] - prev_disposal = previous.encoderinfo.get("disposal") - prev_blend = previous.encoderinfo.get("blend") - if prev_disposal == Disposal.OP_PREVIOUS and len(im_frames) < 2: - prev_disposal = Disposal.OP_BACKGROUND - - if prev_disposal == Disposal.OP_BACKGROUND: - base_im = previous.im.copy() - dispose = Image.core.fill("RGBA", im.size, (0, 0, 0, 0)) - bbox = previous.bbox - if bbox: - dispose = dispose.crop(bbox) - else: - bbox = (0, 0) + im.size - base_im.paste(dispose, bbox) - elif prev_disposal == Disposal.OP_PREVIOUS: - base_im = im_frames[-2].im - else: - base_im = previous.im - delta = ImageChops.subtract_modulo( - im_frame.convert("RGBA"), base_im.convert("RGBA") - ) - bbox = delta.getbbox(alpha_only=False) - if ( - not bbox - and prev_disposal == encoderinfo.get("disposal") - and prev_blend == encoderinfo.get("blend") - and "duration" in encoderinfo - ): - previous.encoderinfo["duration"] += encoderinfo["duration"] - continue - else: - bbox = None - im_frames.append(_Frame(im_frame, bbox, encoderinfo)) - - if len(im_frames) == 1 and not default_image: - return im_frames[0].im - - # animation control - chunk( - fp, - b"acTL", - o32(len(im_frames)), # 0: num_frames - o32(loop), # 4: num_plays - ) - - # default image IDAT (if it exists) - if default_image: - default_im = im if im.mode == mode else im.convert(mode) - _apply_encoderinfo(default_im, im.encoderinfo) - ImageFile._save( - default_im, - cast(IO[bytes], _idat(fp, chunk)), - [ImageFile._Tile("zip", (0, 0) + im.size, 0, rawmode)], - ) - - seq_num = 0 - for frame, frame_data in enumerate(im_frames): - im_frame = frame_data.im - if not frame_data.bbox: - bbox = (0, 0) + im_frame.size - else: - bbox = frame_data.bbox - im_frame = im_frame.crop(bbox) - size = im_frame.size - encoderinfo = frame_data.encoderinfo - frame_duration = encoderinfo.get("duration", 0) - delay = Fraction(frame_duration / 1000).limit_denominator(65535) - if delay.numerator > 65535: - msg = "cannot write duration" - raise ValueError(msg) - frame_disposal = encoderinfo.get("disposal", disposal) - frame_blend = encoderinfo.get("blend", blend) - # frame control - chunk( - fp, - b"fcTL", - o32(seq_num), # sequence_number - o32(size[0]), # width - o32(size[1]), # height - o32(bbox[0]), # x_offset - o32(bbox[1]), # y_offset - o16(delay.numerator), # delay_numerator - o16(delay.denominator), # delay_denominator - o8(frame_disposal), # dispose_op - o8(frame_blend), # blend_op - ) - seq_num += 1 - # frame data - _apply_encoderinfo(im_frame, im.encoderinfo) - if frame == 0 and not default_image: - # first frame must be in IDAT chunks for backwards compatibility - ImageFile._save( - im_frame, - cast(IO[bytes], _idat(fp, chunk)), - [ImageFile._Tile("zip", (0, 0) + im_frame.size, 0, rawmode)], - ) - else: - fdat_chunks = _fdat(fp, chunk, seq_num) - ImageFile._save( - im_frame, - cast(IO[bytes], fdat_chunks), - [ImageFile._Tile("zip", (0, 0) + im_frame.size, 0, rawmode)], - ) - seq_num = fdat_chunks.seq_num - return None - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, save_all=True) - - -def _save( - im: Image.Image, - fp: IO[bytes], - filename: str | bytes, - chunk: Callable[..., None] = putchunk, - save_all: bool = False, -) -> None: - # save an image to disk (called by the save method) - - if save_all: - default_image = im.encoderinfo.get( - "default_image", im.info.get("default_image") - ) - modes = set() - sizes = set() - append_images = im.encoderinfo.get("append_images", []) - for im_seq in itertools.chain([im], append_images): - for im_frame in ImageSequence.Iterator(im_seq): - modes.add(im_frame.mode) - sizes.add(im_frame.size) - for mode in ("RGBA", "RGB", "P"): - if mode in modes: - break - else: - mode = modes.pop() - size = tuple(max(frame_size[i] for frame_size in sizes) for i in range(2)) - else: - size = im.size - mode = im.mode - - outmode = mode - palette = [] - if im.palette: - palette = im.getpalette() or [] - if mode == "P": - # - # attempt to minimize storage requirements for palette images - if "bits" in im.encoderinfo: - # number of bits specified by user - colors = min(1 << im.encoderinfo["bits"], 256) - else: - # check palette contents - if im.palette: - colors = max(min(len(palette) // 3, 256), 1) - else: - colors = 256 - - if colors <= 16: - if colors <= 2: - bits = 1 - elif colors <= 4: - bits = 2 - else: - bits = 4 - outmode += f";{bits}" - - # get the corresponding PNG mode - try: - rawmode, bit_depth, color_type = _OUTMODES[outmode] - except KeyError as e: - msg = f"cannot write mode {mode} as PNG" - raise OSError(msg) from e - if outmode == "I": - deprecate("Saving I mode images as PNG", 13, stacklevel=4) - - # - # write minimal PNG file - - fp.write(_MAGIC) - - chunk( - fp, - b"IHDR", - o32(size[0]), # 0: size - o32(size[1]), - bit_depth, - color_type, - b"\0", # 10: compression - b"\0", # 11: filter category - b"\0", # 12: interlace flag - ) - - chunks = [b"cHRM", b"cICP", b"gAMA", b"sBIT", b"sRGB", b"tIME"] - - if icc := im.encoderinfo.get("icc_profile", im.info.get("icc_profile")): - # ICC profile - # according to PNG spec, the iCCP chunk contains: - # Profile name 1-79 bytes (character string) - # Null separator 1 byte (null character) - # Compression method 1 byte (0) - # Compressed profile n bytes (zlib with deflate compression) - name = b"ICC Profile" - data = name + b"\0\0" + zlib.compress(icc) - chunk(fp, b"iCCP", data) - - # You must either have sRGB or iCCP. - # Disallow sRGB chunks when an iCCP-chunk has been emitted. - chunks.remove(b"sRGB") - - if info := im.encoderinfo.get("pnginfo"): - chunks_multiple_allowed = [b"sPLT", b"iTXt", b"tEXt", b"zTXt"] - for info_chunk in info.chunks: - cid, data = info_chunk[:2] - if cid in chunks: - chunks.remove(cid) - chunk(fp, cid, data) - elif cid in chunks_multiple_allowed: - chunk(fp, cid, data) - elif cid[1:2].islower(): - # Private chunk - after_idat = len(info_chunk) == 3 and info_chunk[2] - if not after_idat: - chunk(fp, cid, data) - - if im.mode == "P": - palette_byte_number = colors * 3 - palette_bytes = bytes(palette[:palette_byte_number]) - while len(palette_bytes) < palette_byte_number: - palette_bytes += b"\0" - chunk(fp, b"PLTE", palette_bytes) - - transparency = im.encoderinfo.get("transparency", im.info.get("transparency", None)) - - if transparency or transparency == 0: - if im.mode == "P": - # limit to actual palette size - alpha_bytes = colors - if isinstance(transparency, bytes): - chunk(fp, b"tRNS", transparency[:alpha_bytes]) - else: - transparency = max(0, min(255, transparency)) - alpha = b"\xff" * transparency + b"\0" - chunk(fp, b"tRNS", alpha[:alpha_bytes]) - elif im.mode in ("1", "L", "I", "I;16"): - transparency = max(0, min(65535, transparency)) - chunk(fp, b"tRNS", o16(transparency)) - elif im.mode == "RGB": - red, green, blue = transparency - chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue)) - else: - if "transparency" in im.encoderinfo: - # don't bother with transparency if it's an RGBA - # and it's in the info dict. It's probably just stale. - msg = "cannot use transparency for this mode" - raise OSError(msg) - else: - if im.mode == "P" and im.im.getpalettemode() == "RGBA": - alpha = im.im.getpalette("RGBA", "A") - alpha_bytes = colors - chunk(fp, b"tRNS", alpha[:alpha_bytes]) - - if dpi := im.encoderinfo.get("dpi"): - chunk( - fp, - b"pHYs", - o32(int(dpi[0] / 0.0254 + 0.5)), - o32(int(dpi[1] / 0.0254 + 0.5)), - b"\x01", - ) - - if info: - chunks = [b"bKGD", b"hIST"] - for info_chunk in info.chunks: - cid, data = info_chunk[:2] - if cid in chunks: - chunks.remove(cid) - chunk(fp, cid, data) - - if exif := im.encoderinfo.get("exif"): - if isinstance(exif, Image.Exif): - exif = exif.tobytes(8) - if exif.startswith(b"Exif\x00\x00"): - exif = exif[6:] - chunk(fp, b"eXIf", exif) - - single_im: Image.Image | None = im - if save_all: - single_im = _write_multiple_frames( - im, fp, chunk, mode, rawmode, default_image, append_images - ) - if single_im: - _apply_encoderinfo(single_im, im.encoderinfo) - ImageFile._save( - single_im, - cast(IO[bytes], _idat(fp, chunk)), - [ImageFile._Tile("zip", (0, 0) + single_im.size, 0, rawmode)], - ) - - if info: - for info_chunk in info.chunks: - cid, data = info_chunk[:2] - if cid[1:2].islower(): - # Private chunk - after_idat = len(info_chunk) == 3 and info_chunk[2] - if after_idat: - chunk(fp, cid, data) - - chunk(fp, b"IEND", b"") - - if hasattr(fp, "flush"): - fp.flush() - - -# -------------------------------------------------------------------- -# PNG chunk converter - - -def getchunks(im: Image.Image, **params: Any) -> list[tuple[bytes, bytes, bytes]]: - """Return a list of PNG chunks representing this image.""" - from io import BytesIO - - chunks = [] - - def append(fp: IO[bytes], cid: bytes, *data: bytes) -> None: - byte_data = b"".join(data) - crc = o32(_crc32(byte_data, _crc32(cid))) - chunks.append((cid, byte_data, crc)) - - fp = BytesIO() - - try: - im.encoderinfo = params - _save(im, fp, "", append) - finally: - del im.encoderinfo - - return chunks - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(PngImageFile.format, PngImageFile, _accept) -Image.register_save(PngImageFile.format, _save) -Image.register_save_all(PngImageFile.format, _save_all) - -Image.register_extensions(PngImageFile.format, [".png", ".apng"]) - -Image.register_mime(PngImageFile.format, "image/png") diff --git a/.venv/lib/python3.12/site-packages/PIL/PpmImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/PpmImagePlugin.py deleted file mode 100644 index 307bc97f..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PpmImagePlugin.py +++ /dev/null @@ -1,375 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PPM support for PIL -# -# History: -# 96-03-24 fl Created -# 98-03-06 fl Write RGBA images (as RGB, that is) -# -# Copyright (c) Secret Labs AB 1997-98. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import math -from typing import IO - -from . import Image, ImageFile -from ._binary import i16be as i16 -from ._binary import o8 -from ._binary import o32le as o32 - -# -# -------------------------------------------------------------------- - -b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" - -MODES = { - # standard - b"P1": "1", - b"P2": "L", - b"P3": "RGB", - b"P4": "1", - b"P5": "L", - b"P6": "RGB", - # extensions - b"P0CMYK": "CMYK", - b"Pf": "F", - # PIL extensions (for test purposes only) - b"PyP": "P", - b"PyRGBA": "RGBA", - b"PyCMYK": "CMYK", -} - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 2 and prefix.startswith(b"P") and prefix[1] in b"0123456fy" - - -## -# Image plugin for PBM, PGM, and PPM images. - - -class PpmImageFile(ImageFile.ImageFile): - format = "PPM" - format_description = "Pbmplus image" - - def _read_magic(self) -> bytes: - assert self.fp is not None - - magic = b"" - # read until whitespace or longest available magic number - for _ in range(6): - c = self.fp.read(1) - if not c or c in b_whitespace: - break - magic += c - return magic - - def _read_token(self) -> bytes: - assert self.fp is not None - - token = b"" - while len(token) <= 10: # read until next whitespace or limit of 10 characters - c = self.fp.read(1) - if not c: - break - elif c in b_whitespace: # token ended - if not token: - # skip whitespace at start - continue - break - elif c == b"#": - # ignores rest of the line; stops at CR, LF or EOF - while self.fp.read(1) not in b"\r\n": - pass - continue - token += c - if not token: - # Token was not even 1 byte - msg = "Reached EOF while reading header" - raise ValueError(msg) - elif len(token) > 10: - msg_too_long = b"Token too long in file header: %s" % token - raise ValueError(msg_too_long) - return token - - def _open(self) -> None: - assert self.fp is not None - - magic_number = self._read_magic() - try: - mode = MODES[magic_number] - except KeyError: - msg = "not a PPM file" - raise SyntaxError(msg) - self._mode = mode - - if magic_number in (b"P1", b"P4"): - self.custom_mimetype = "image/x-portable-bitmap" - elif magic_number in (b"P2", b"P5"): - self.custom_mimetype = "image/x-portable-graymap" - elif magic_number in (b"P3", b"P6"): - self.custom_mimetype = "image/x-portable-pixmap" - - self._size = int(self._read_token()), int(self._read_token()) - - decoder_name = "raw" - if magic_number in (b"P1", b"P2", b"P3"): - decoder_name = "ppm_plain" - - args: str | tuple[str | int, ...] - if mode == "1": - args = "1;I" - elif mode == "F": - scale = float(self._read_token()) - if scale == 0.0 or not math.isfinite(scale): - msg = "scale must be finite and non-zero" - raise ValueError(msg) - self.info["scale"] = abs(scale) - - rawmode = "F;32F" if scale < 0 else "F;32BF" - args = (rawmode, 0, -1) - else: - maxval = int(self._read_token()) - if not 0 < maxval < 65536: - msg = "maxval must be greater than 0 and less than 65536" - raise ValueError(msg) - if maxval > 255 and mode == "L": - self._mode = "I" - - rawmode = mode - if decoder_name != "ppm_plain": - # If maxval matches a bit depth, use the raw decoder directly - if maxval == 65535 and mode == "L": - rawmode = "I;16B" - elif maxval != 255: - decoder_name = "ppm" - - args = rawmode if decoder_name == "raw" else (rawmode, maxval) - self.tile = [ - ImageFile._Tile(decoder_name, (0, 0) + self.size, self.fp.tell(), args) - ] - - -# -# -------------------------------------------------------------------- - - -class PpmPlainDecoder(ImageFile.PyDecoder): - _pulls_fd = True - _comment_spans: bool - - def _read_block(self) -> bytes: - assert self.fd is not None - - return self.fd.read(ImageFile.SAFEBLOCK) - - def _find_comment_end(self, block: bytes, start: int = 0) -> int: - a = block.find(b"\n", start) - b = block.find(b"\r", start) - return min(a, b) if a * b > 0 else max(a, b) # lowest nonnegative index (or -1) - - def _ignore_comments(self, block: bytes) -> bytes: - if self._comment_spans: - # Finish current comment - while block: - comment_end = self._find_comment_end(block) - if comment_end != -1: - # Comment ends in this block - # Delete tail of comment - block = block[comment_end + 1 :] - break - else: - # Comment spans whole block - # So read the next block, looking for the end - block = self._read_block() - - # Search for any further comments - self._comment_spans = False - while True: - comment_start = block.find(b"#") - if comment_start == -1: - # No comment found - break - comment_end = self._find_comment_end(block, comment_start) - if comment_end != -1: - # Comment ends in this block - # Delete comment - block = block[:comment_start] + block[comment_end + 1 :] - else: - # Comment continues to next block(s) - block = block[:comment_start] - self._comment_spans = True - break - return block - - def _decode_bitonal(self) -> bytearray: - """ - This is a separate method because in the plain PBM format, all data tokens are - exactly one byte, so the inter-token whitespace is optional. - """ - data = bytearray() - total_bytes = self.state.xsize * self.state.ysize - - while len(data) != total_bytes: - block = self._read_block() # read next block - if not block: - # eof - break - - block = self._ignore_comments(block) - - tokens = b"".join(block.split()) - for token in tokens: - if token not in (48, 49): - msg = b"Invalid token for this mode: %s" % bytes([token]) - raise ValueError(msg) - data = (data + tokens)[:total_bytes] - invert = bytes.maketrans(b"01", b"\xff\x00") - return data.translate(invert) - - def _decode_blocks(self, maxval: int) -> bytearray: - data = bytearray() - max_len = 10 - out_byte_count = 4 if self.mode == "I" else 1 - out_max = 65535 if self.mode == "I" else 255 - bands = Image.getmodebands(self.mode) - total_bytes = self.state.xsize * self.state.ysize * bands * out_byte_count - - half_token = b"" - while len(data) != total_bytes: - block = self._read_block() # read next block - if not block: - if half_token: - block = bytearray(b" ") # flush half_token - else: - # eof - break - - block = self._ignore_comments(block) - - if half_token: - block = half_token + block # stitch half_token to new block - half_token = b"" - - tokens = block.split() - - if block and not block[-1:].isspace(): # block might split token - half_token = tokens.pop() # save half token for later - if len(half_token) > max_len: # prevent buildup of half_token - msg = ( - b"Token too long found in data: %s" % half_token[: max_len + 1] - ) - raise ValueError(msg) - - for token in tokens: - if len(token) > max_len: - msg = b"Token too long found in data: %s" % token[: max_len + 1] - raise ValueError(msg) - value = int(token) - if value < 0: - msg_str = f"Channel value is negative: {value}" - raise ValueError(msg_str) - if value > maxval: - msg_str = f"Channel value too large for this mode: {value}" - raise ValueError(msg_str) - value = round(value / maxval * out_max) - data += o32(value) if self.mode == "I" else o8(value) - if len(data) == total_bytes: # finished! - break - return data - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - self._comment_spans = False - if self.mode == "1": - data = self._decode_bitonal() - rawmode = "1;8" - else: - maxval = self.args[-1] - data = self._decode_blocks(maxval) - rawmode = "I;32" if self.mode == "I" else self.mode - self.set_as_raw(bytes(data), rawmode) - return -1, 0 - - -class PpmDecoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - - data = bytearray() - maxval = self.args[-1] - in_byte_count = 1 if maxval < 256 else 2 - out_byte_count = 4 if self.mode == "I" else 1 - out_max = 65535 if self.mode == "I" else 255 - bands = Image.getmodebands(self.mode) - dest_length = self.state.xsize * self.state.ysize * bands * out_byte_count - while len(data) < dest_length: - pixels = self.fd.read(in_byte_count * bands) - if len(pixels) < in_byte_count * bands: - # eof - break - for b in range(bands): - value = ( - pixels[b] if in_byte_count == 1 else i16(pixels, b * in_byte_count) - ) - value = min(out_max, round(value / maxval * out_max)) - data += o32(value) if self.mode == "I" else o8(value) - rawmode = "I;32" if self.mode == "I" else self.mode - self.set_as_raw(bytes(data), rawmode) - return -1, 0 - - -# -# -------------------------------------------------------------------- - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode == "1": - rawmode, head = "1;I", b"P4" - elif im.mode == "L": - rawmode, head = "L", b"P5" - elif im.mode in ("I", "I;16"): - rawmode, head = "I;16B", b"P5" - elif im.mode in ("RGB", "RGBA"): - rawmode, head = "RGB", b"P6" - elif im.mode == "F": - rawmode, head = "F;32F", b"Pf" - else: - msg = f"cannot write mode {im.mode} as PPM" - raise OSError(msg) - fp.write(head + b"\n%d %d\n" % im.size) - if head == b"P6": - fp.write(b"255\n") - elif head == b"P5": - if rawmode == "L": - fp.write(b"255\n") - else: - fp.write(b"65535\n") - elif head == b"Pf": - fp.write(b"-1.0\n") - row_order = -1 if im.mode == "F" else 1 - ImageFile._save( - im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, row_order))] - ) - - -# -# -------------------------------------------------------------------- - - -Image.register_open(PpmImageFile.format, PpmImageFile, _accept) -Image.register_save(PpmImageFile.format, _save) - -Image.register_decoder("ppm", PpmDecoder) -Image.register_decoder("ppm_plain", PpmPlainDecoder) - -Image.register_extensions(PpmImageFile.format, [".pbm", ".pgm", ".ppm", ".pnm", ".pfm"]) - -Image.register_mime(PpmImageFile.format, "image/x-portable-anymap") diff --git a/.venv/lib/python3.12/site-packages/PIL/PsdImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/PsdImagePlugin.py deleted file mode 100644 index dd3d5ab9..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/PsdImagePlugin.py +++ /dev/null @@ -1,337 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# Adobe PSD 2.5/3.0 file handling -# -# History: -# 1995-09-01 fl Created -# 1997-01-03 fl Read most PSD images -# 1997-01-18 fl Fixed P and CMYK support -# 2001-10-21 fl Added seek/tell support (for layers) -# -# Copyright (c) 1997-2001 by Secret Labs AB. -# Copyright (c) 1995-2001 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -from functools import cached_property -from typing import IO - -from . import Image, ImageFile, ImagePalette -from ._binary import i8 -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._binary import si16be as si16 -from ._binary import si32be as si32 -from ._util import DeferredError - -MODES = { - # (photoshop mode, bits) -> (pil mode, required channels) - (0, 1): ("1", 1), - (0, 8): ("L", 1), - (1, 8): ("L", 1), - (2, 8): ("P", 1), - (3, 8): ("RGB", 3), - (4, 8): ("CMYK", 4), - (7, 8): ("L", 1), # FIXME: multilayer - (8, 8): ("L", 1), # duotone - (9, 8): ("LAB", 3), -} - - -# --------------------------------------------------------------------. -# read PSD images - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"8BPS") - - -## -# Image plugin for Photoshop images. - - -class PsdImageFile(ImageFile.ImageFile): - format = "PSD" - format_description = "Adobe Photoshop" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - assert self.fp is not None - read = self.fp.read - - # - # header - - s = read(26) - if not _accept(s) or i16(s, 4) != 1: - msg = "not a PSD file" - raise SyntaxError(msg) - - psd_bits = i16(s, 22) - psd_channels = i16(s, 12) - psd_mode = i16(s, 24) - - mode, channels = MODES[(psd_mode, psd_bits)] - - if channels > psd_channels: - msg = "not enough channels" - raise OSError(msg) - if mode == "RGB" and psd_channels == 4: - mode = "RGBA" - channels = 4 - - self._mode = mode - self._size = i32(s, 18), i32(s, 14) - - # - # color mode data - - size = i32(read(4)) - if size: - data = read(size) - if mode == "P" and size == 768: - self.palette = ImagePalette.raw("RGB;L", data) - - # - # image resources - - self.resources = [] - - size = i32(read(4)) - if size: - # load resources - end = self.fp.tell() + size - while self.fp.tell() < end: - read(4) # signature - id = i16(read(2)) - name = read(i8(read(1))) - if not (len(name) & 1): - read(1) # padding - data = read(i32(read(4))) - if len(data) & 1: - read(1) # padding - self.resources.append((id, name, data)) - if id == 1039: # ICC profile - self.info["icc_profile"] = data - - # - # layer and mask information - - self._layers_position = None - - size = i32(read(4)) - if size: - end = self.fp.tell() + size - size = i32(read(4)) - if size: - self._layers_position = self.fp.tell() - self._layers_size = size - self.fp.seek(end) - self._n_frames: int | None = None - - # - # image descriptor - - self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) - - # keep the file open - self._fp = self.fp - self.frame = 1 - self._min_frame = 1 - - @cached_property - def layers( - self, - ) -> list[tuple[str, str, tuple[int, int, int, int], list[ImageFile._Tile]]]: - layers = [] - if self._layers_position is not None: - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self._fp.seek(self._layers_position) - _layer_data = io.BytesIO(ImageFile._safe_read(self._fp, self._layers_size)) - layers = _layerinfo(_layer_data, self._layers_size) - self._n_frames = len(layers) - return layers - - @property - def n_frames(self) -> int: - if self._n_frames is None: - self._n_frames = len(self.layers) - return self._n_frames - - @property - def is_animated(self) -> bool: - return len(self.layers) > 1 - - def seek(self, layer: int) -> None: - if not self._seek_check(layer): - return - if isinstance(self._fp, DeferredError): - raise self._fp.ex - - # seek to given layer (1..max) - if layer > len(self.layers): - msg = "no more images in PSD file" - raise EOFError(msg) - _, mode, _, tile = self.layers[layer - 1] - self._mode = mode - self.tile = tile - self.frame = layer - self.fp = self._fp - - def tell(self) -> int: - # return layer number (0=image, 1..max=layers) - return self.frame - - -def _layerinfo( - fp: IO[bytes], ct_bytes: int -) -> list[tuple[str, str, tuple[int, int, int, int], list[ImageFile._Tile]]]: - # read layerinfo block - layers = [] - - def read(size: int) -> bytes: - return ImageFile._safe_read(fp, size) - - ct = si16(read(2)) - - # sanity check - if ct_bytes < (abs(ct) * 20): - msg = "Layer block too short for number of layers requested" - raise SyntaxError(msg) - - for _ in range(abs(ct)): - # bounding box - y0 = si32(read(4)) - x0 = si32(read(4)) - y1 = si32(read(4)) - x1 = si32(read(4)) - - # image info - bands = [] - ct_types = i16(read(2)) - if ct_types > 4: - fp.seek(ct_types * 6 + 12, io.SEEK_CUR) - size = i32(read(4)) - fp.seek(size, io.SEEK_CUR) - continue - - for _ in range(ct_types): - type = i16(read(2)) - - if type == 65535: - b = "A" - else: - b = "RGBA"[type] - - bands.append(b) - read(4) # size - - # figure out the image mode - bands.sort() - if bands == ["R"]: - mode = "L" - elif bands == ["B", "G", "R"]: - mode = "RGB" - elif bands == ["A", "B", "G", "R"]: - mode = "RGBA" - else: - mode = "" # unknown - - # skip over blend flags and extra information - read(12) # filler - name = "" - size = i32(read(4)) # length of the extra data field - if size: - data_end = fp.tell() + size - - length = i32(read(4)) - if length: - fp.seek(length - 16, io.SEEK_CUR) - - length = i32(read(4)) - if length: - fp.seek(length, io.SEEK_CUR) - - length = i8(read(1)) - if length: - # Don't know the proper encoding, - # Latin-1 should be a good guess - name = read(length).decode("latin-1", "replace") - - fp.seek(data_end) - layers.append((name, mode, (x0, y0, x1, y1))) - - # get tiles - layerinfo = [] - for i, (name, mode, bbox) in enumerate(layers): - tile = [] - for m in mode: - t = _maketile(fp, m, bbox, 1) - if t: - tile.extend(t) - layerinfo.append((name, mode, bbox, tile)) - - return layerinfo - - -def _maketile( - file: IO[bytes], mode: str, bbox: tuple[int, int, int, int], channels: int -) -> list[ImageFile._Tile]: - tiles = [] - read = file.read - - compression = i16(read(2)) - - xsize = bbox[2] - bbox[0] - ysize = bbox[3] - bbox[1] - - offset = file.tell() - - if compression == 0: - # - # raw compression - for channel in range(channels): - layer = mode[channel] - if mode == "CMYK": - layer += ";I" - tiles.append(ImageFile._Tile("raw", bbox, offset, layer)) - offset = offset + xsize * ysize - - elif compression == 1: - # - # packbits compression - i = 0 - bytecount = read(channels * ysize * 2) - offset = file.tell() - for channel in range(channels): - layer = mode[channel] - if mode == "CMYK": - layer += ";I" - tiles.append(ImageFile._Tile("packbits", bbox, offset, layer)) - for y in range(ysize): - offset = offset + i16(bytecount, i) - i += 2 - - file.seek(offset) - - if offset & 1: - read(1) # padding - - return tiles - - -# -------------------------------------------------------------------- -# registry - - -Image.register_open(PsdImageFile.format, PsdImageFile, _accept) - -Image.register_extension(PsdImageFile.format, ".psd") - -Image.register_mime(PsdImageFile.format, "image/vnd.adobe.photoshop") diff --git a/.venv/lib/python3.12/site-packages/PIL/QoiImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/QoiImagePlugin.py deleted file mode 100644 index d0709b11..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/QoiImagePlugin.py +++ /dev/null @@ -1,235 +0,0 @@ -# -# The Python Imaging Library. -# -# QOI support for PIL -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import IO - -from . import Image, ImageFile -from ._binary import i32be as i32 -from ._binary import o8 -from ._binary import o32be as o32 - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"qoif") - - -class QoiImageFile(ImageFile.ImageFile): - format = "QOI" - format_description = "Quite OK Image" - - def _open(self) -> None: - assert self.fp is not None - if not _accept(self.fp.read(4)): - msg = "not a QOI file" - raise SyntaxError(msg) - - self._size = i32(self.fp.read(4)), i32(self.fp.read(4)) - - channels = self.fp.read(1)[0] - self._mode = "RGB" if channels == 3 else "RGBA" - - self.fp.seek(1, os.SEEK_CUR) # colorspace - self.tile = [ImageFile._Tile("qoi", (0, 0) + self._size, self.fp.tell())] - - -class QoiDecoder(ImageFile.PyDecoder): - _pulls_fd = True - _previous_pixel: bytes | bytearray | None = None - _previously_seen_pixels: dict[int, bytes | bytearray] = {} - - def _add_to_previous_pixels(self, value: bytes | bytearray) -> None: - self._previous_pixel = value - - r, g, b, a = value - hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64 - self._previously_seen_pixels[hash_value] = value - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - - self._previously_seen_pixels = {} - self._previous_pixel = bytearray((0, 0, 0, 255)) - - data = bytearray() - bands = Image.getmodebands(self.mode) - dest_length = self.state.xsize * self.state.ysize * bands - while len(data) < dest_length: - byte = self.fd.read(1)[0] - value: bytes | bytearray - if byte == 0b11111110 and self._previous_pixel: # QOI_OP_RGB - value = bytearray(self.fd.read(3)) + self._previous_pixel[3:] - elif byte == 0b11111111: # QOI_OP_RGBA - value = self.fd.read(4) - else: - op = byte >> 6 - if op == 0: # QOI_OP_INDEX - op_index = byte & 0b00111111 - value = self._previously_seen_pixels.get( - op_index, bytearray((0, 0, 0, 0)) - ) - elif op == 1 and self._previous_pixel: # QOI_OP_DIFF - value = bytearray( - ( - (self._previous_pixel[0] + ((byte & 0b00110000) >> 4) - 2) - % 256, - (self._previous_pixel[1] + ((byte & 0b00001100) >> 2) - 2) - % 256, - (self._previous_pixel[2] + (byte & 0b00000011) - 2) % 256, - self._previous_pixel[3], - ) - ) - elif op == 2 and self._previous_pixel: # QOI_OP_LUMA - second_byte = self.fd.read(1)[0] - diff_green = (byte & 0b00111111) - 32 - diff_red = ((second_byte & 0b11110000) >> 4) - 8 - diff_blue = (second_byte & 0b00001111) - 8 - - value = bytearray( - tuple( - (self._previous_pixel[i] + diff_green + diff) % 256 - for i, diff in enumerate((diff_red, 0, diff_blue)) - ) - ) - value += self._previous_pixel[3:] - elif op == 3 and self._previous_pixel: # QOI_OP_RUN - run_length = (byte & 0b00111111) + 1 - value = self._previous_pixel - if bands == 3: - value = value[:3] - data += value * run_length - continue - self._add_to_previous_pixels(value) - - if bands == 3: - value = value[:3] - data += value - self.set_as_raw(data) - return -1, 0 - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode == "RGB": - channels = 3 - elif im.mode == "RGBA": - channels = 4 - else: - msg = "Unsupported QOI image mode" - raise ValueError(msg) - - colorspace = 0 if im.encoderinfo.get("colorspace") == "sRGB" else 1 - - fp.write(b"qoif") - fp.write(o32(im.size[0])) - fp.write(o32(im.size[1])) - fp.write(o8(channels)) - fp.write(o8(colorspace)) - - ImageFile._save(im, fp, [ImageFile._Tile("qoi", (0, 0) + im.size)]) - - -class QoiEncoder(ImageFile.PyEncoder): - _pushes_fd = True - _previous_pixel: tuple[int, int, int, int] | None = None - _previously_seen_pixels: dict[int, tuple[int, int, int, int]] = {} - _run = 0 - - def _write_run(self) -> bytes: - data = o8(0b11000000 | (self._run - 1)) # QOI_OP_RUN - self._run = 0 - return data - - def _delta(self, left: int, right: int) -> int: - result = (left - right) & 255 - if result >= 128: - result -= 256 - return result - - def encode(self, bufsize: int) -> tuple[int, int, bytes]: - assert self.im is not None - - self._previously_seen_pixels = {0: (0, 0, 0, 0)} - self._previous_pixel = (0, 0, 0, 255) - - data = bytearray() - w, h = self.im.size - bands = Image.getmodebands(self.mode) - - for y in range(h): - for x in range(w): - pixel = self.im.getpixel((x, y)) - if bands == 3: - pixel = (*pixel, 255) - - if pixel == self._previous_pixel: - self._run += 1 - if self._run == 62: - data += self._write_run() - else: - if self._run: - data += self._write_run() - - r, g, b, a = pixel - hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64 - if self._previously_seen_pixels.get(hash_value) == pixel: - data += o8(hash_value) # QOI_OP_INDEX - elif self._previous_pixel: - self._previously_seen_pixels[hash_value] = pixel - - prev_r, prev_g, prev_b, prev_a = self._previous_pixel - if prev_a == a: - delta_r = self._delta(r, prev_r) - delta_g = self._delta(g, prev_g) - delta_b = self._delta(b, prev_b) - - if ( - -2 <= delta_r < 2 - and -2 <= delta_g < 2 - and -2 <= delta_b < 2 - ): - data += o8( - 0b01000000 - | (delta_r + 2) << 4 - | (delta_g + 2) << 2 - | (delta_b + 2) - ) # QOI_OP_DIFF - else: - delta_gr = self._delta(delta_r, delta_g) - delta_gb = self._delta(delta_b, delta_g) - if ( - -8 <= delta_gr < 8 - and -32 <= delta_g < 32 - and -8 <= delta_gb < 8 - ): - data += o8( - 0b10000000 | (delta_g + 32) - ) # QOI_OP_LUMA - data += o8((delta_gr + 8) << 4 | (delta_gb + 8)) - else: - data += o8(0b11111110) # QOI_OP_RGB - data += bytes(pixel[:3]) - else: - data += o8(0b11111111) # QOI_OP_RGBA - data += bytes(pixel) - - self._previous_pixel = pixel - - if self._run: - data += self._write_run() - data += bytes((0, 0, 0, 0, 0, 0, 0, 1)) # padding - - return len(data), 0, data - - -Image.register_open(QoiImageFile.format, QoiImageFile, _accept) -Image.register_decoder("qoi", QoiDecoder) -Image.register_extension(QoiImageFile.format, ".qoi") - -Image.register_save(QoiImageFile.format, _save) -Image.register_encoder("qoi", QoiEncoder) diff --git a/.venv/lib/python3.12/site-packages/PIL/SgiImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/SgiImagePlugin.py deleted file mode 100644 index 85302215..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/SgiImagePlugin.py +++ /dev/null @@ -1,231 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# SGI image file handling -# -# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. -# -# -# -# History: -# 2017-22-07 mb Add RLE decompression -# 2016-16-10 mb Add save method without compression -# 1995-09-10 fl Created -# -# Copyright (c) 2016 by Mickael Bonfill. -# Copyright (c) 2008 by Karsten Hiddemann. -# Copyright (c) 1997 by Secret Labs AB. -# Copyright (c) 1995 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -import struct -from typing import IO - -from . import Image, ImageFile -from ._binary import i16be as i16 -from ._binary import o8 - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 2 and i16(prefix) == 474 - - -MODES = { - (1, 1, 1): "L", - (1, 2, 1): "L", - (2, 1, 1): "L;16B", - (2, 2, 1): "L;16B", - (1, 3, 3): "RGB", - (2, 3, 3): "RGB;16B", - (1, 3, 4): "RGBA", - (2, 3, 4): "RGBA;16B", -} - - -## -# Image plugin for SGI images. -class SgiImageFile(ImageFile.ImageFile): - format = "SGI" - format_description = "SGI Image File Format" - - def _open(self) -> None: - # HEAD - assert self.fp is not None - - headlen = 512 - s = self.fp.read(headlen) - - if not _accept(s): - msg = "Not an SGI image file" - raise ValueError(msg) - - # compression : verbatim or RLE - compression = s[2] - - # bpc : 1 or 2 bytes (8bits or 16bits) - bpc = s[3] - - # dimension : 1, 2 or 3 (depending on xsize, ysize and zsize) - dimension = i16(s, 4) - - # xsize : width - xsize = i16(s, 6) - - # ysize : height - ysize = i16(s, 8) - - # zsize : channels count - zsize = i16(s, 10) - - # determine mode from bits/zsize - try: - rawmode = MODES[(bpc, dimension, zsize)] - except KeyError: - msg = "Unsupported SGI image mode" - raise ValueError(msg) - - self._size = xsize, ysize - self._mode = rawmode.split(";")[0] - if self.mode == "RGB": - self.custom_mimetype = "image/rgb" - - # orientation -1 : scanlines begins at the bottom-left corner - orientation = -1 - - # decoder info - if compression == 0: - pagesize = xsize * ysize * bpc - if bpc == 2: - self.tile = [ - ImageFile._Tile( - "SGI16", - (0, 0) + self.size, - headlen, - (self.mode, 0, orientation), - ) - ] - else: - self.tile = [] - offset = headlen - for layer in self.mode: - self.tile.append( - ImageFile._Tile( - "raw", (0, 0) + self.size, offset, (layer, 0, orientation) - ) - ) - offset += pagesize - elif compression == 1: - self.tile = [ - ImageFile._Tile( - "sgi_rle", (0, 0) + self.size, headlen, (rawmode, orientation, bpc) - ) - ] - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode not in {"RGB", "RGBA", "L"}: - msg = "Unsupported SGI image mode" - raise ValueError(msg) - - # Get the keyword arguments - info = im.encoderinfo - - # Byte-per-pixel precision, 1 = 8bits per pixel - bpc = info.get("bpc", 1) - - if bpc not in (1, 2): - msg = "Unsupported number of bytes per pixel" - raise ValueError(msg) - - # Flip the image, since the origin of SGI file is the bottom-left corner - orientation = -1 - # Define the file as SGI File Format - magic_number = 474 - # Run-Length Encoding Compression - Unsupported at this time - rle = 0 - - # X Dimension = width / Y Dimension = height - x, y = im.size - # Z Dimension: Number of channels - z = len(im.mode) - # Number of dimensions (x,y,z) - if im.mode == "L": - dimension = 1 if y == 1 else 2 - else: - dimension = 3 - - # Minimum Byte value - pinmin = 0 - # Maximum Byte value (255 = 8bits per pixel) - pinmax = 255 - # Image name (79 characters max, truncated below in write) - img_name = os.path.splitext(os.path.basename(filename))[0] - if isinstance(img_name, str): - img_name = img_name.encode("ascii", "ignore") - # Standard representation of pixel in the file - colormap = 0 - fp.write(struct.pack(">h", magic_number)) - fp.write(o8(rle)) - fp.write(o8(bpc)) - fp.write(struct.pack(">H", dimension)) - fp.write(struct.pack(">H", x)) - fp.write(struct.pack(">H", y)) - fp.write(struct.pack(">H", z)) - fp.write(struct.pack(">l", pinmin)) - fp.write(struct.pack(">l", pinmax)) - fp.write(struct.pack("4s", b"")) # dummy - fp.write(struct.pack("79s", img_name)) # truncates to 79 chars - fp.write(struct.pack("s", b"")) # force null byte after img_name - fp.write(struct.pack(">l", colormap)) - fp.write(struct.pack("404s", b"")) # dummy - - rawmode = "L" - if bpc == 2: - rawmode = "L;16B" - - for channel in im.split(): - fp.write(channel.tobytes("raw", rawmode, 0, orientation)) - - if hasattr(fp, "flush"): - fp.flush() - - -class SGI16Decoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - assert self.im is not None - - rawmode, stride, orientation = self.args - pagesize = self.state.xsize * self.state.ysize - zsize = len(self.mode) - self.fd.seek(512) - - for band in range(zsize): - channel = Image.new("L", (self.state.xsize, self.state.ysize)) - channel.frombytes( - self.fd.read(2 * pagesize), "raw", "L;16B", stride, orientation - ) - self.im.putband(channel.im, band) - - return -1, 0 - - -# -# registry - - -Image.register_decoder("SGI16", SGI16Decoder) -Image.register_open(SgiImageFile.format, SgiImageFile, _accept) -Image.register_save(SgiImageFile.format, _save) -Image.register_mime(SgiImageFile.format, "image/sgi") - -Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"]) - -# End of file diff --git a/.venv/lib/python3.12/site-packages/PIL/SpiderImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/SpiderImagePlugin.py deleted file mode 100644 index 11d90699..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/SpiderImagePlugin.py +++ /dev/null @@ -1,332 +0,0 @@ -# -# The Python Imaging Library. -# -# SPIDER image file handling -# -# History: -# 2004-08-02 Created BB -# 2006-03-02 added save method -# 2006-03-13 added support for stack images -# -# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144. -# Copyright (c) 2004 by William Baxter. -# Copyright (c) 2004 by Secret Labs AB. -# Copyright (c) 2004 by Fredrik Lundh. -# - -## -# Image plugin for the Spider image format. This format is used -# by the SPIDER software, in processing image data from electron -# microscopy and tomography. -## - -# -# SpiderImagePlugin.py -# -# The Spider image format is used by SPIDER software, in processing -# image data from electron microscopy and tomography. -# -# Spider home page: -# https://spider.wadsworth.org/spider_doc/spider/docs/spider.html -# -# Details about the Spider image format: -# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html -# -from __future__ import annotations - -import os -import struct -import sys -from typing import IO, Any, cast - -from . import Image, ImageFile -from ._util import DeferredError - -TYPE_CHECKING = False - - -def isInt(f: Any) -> int: - try: - i = int(f) - if f - i == 0: - return 1 - else: - return 0 - except (ValueError, OverflowError): - return 0 - - -iforms = [1, 3, -11, -12, -21, -22] - - -# There is no magic number to identify Spider files, so just check a -# series of header locations to see if they have reasonable values. -# Returns no. of bytes in the header, if it is a valid Spider header, -# otherwise returns 0 - - -def isSpiderHeader(t: tuple[float, ...]) -> int: - h = (99,) + t # add 1 value so can use spider header index start=1 - # header values 1,2,5,12,13,22,23 should be integers - for i in [1, 2, 5, 12, 13, 22, 23]: - if not isInt(h[i]): - return 0 - # check iform - iform = int(h[5]) - if iform not in iforms: - return 0 - # check other header values - labrec = int(h[13]) # no. records in file header - labbyt = int(h[22]) # total no. of bytes in header - lenbyt = int(h[23]) # record length in bytes - if labbyt != (labrec * lenbyt): - return 0 - # looks like a valid header - return labbyt - - -def isSpiderImage(filename: str) -> int: - with open(filename, "rb") as fp: - f = fp.read(92) # read 23 * 4 bytes - t = struct.unpack(">23f", f) # try big-endian first - hdrlen = isSpiderHeader(t) - if hdrlen == 0: - t = struct.unpack("<23f", f) # little-endian - hdrlen = isSpiderHeader(t) - return hdrlen - - -class SpiderImageFile(ImageFile.ImageFile): - format = "SPIDER" - format_description = "Spider 2D image" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - # check header - n = 27 * 4 # read 27 float values - assert self.fp is not None - f = self.fp.read(n) - - try: - self.bigendian = 1 - t = struct.unpack(">27f", f) # try big-endian first - hdrlen = isSpiderHeader(t) - if hdrlen == 0: - self.bigendian = 0 - t = struct.unpack("<27f", f) # little-endian - hdrlen = isSpiderHeader(t) - if hdrlen == 0: - msg = "not a valid Spider file" - raise SyntaxError(msg) - except struct.error as e: - msg = "not a valid Spider file" - raise SyntaxError(msg) from e - - h = (99,) + t # add 1 value : spider header index starts at 1 - iform = int(h[5]) - if iform != 1: - msg = "not a Spider 2D image" - raise SyntaxError(msg) - - self._size = int(h[12]), int(h[2]) # size in pixels (width, height) - self.istack = int(h[24]) - self.imgnumber = int(h[27]) - - if self.istack == 0 and self.imgnumber == 0: - # stk=0, img=0: a regular 2D image - offset = hdrlen - self._nimages = 1 - elif self.istack > 0 and self.imgnumber == 0: - # stk>0, img=0: Opening the stack for the first time - self.imgbytes = int(h[12]) * int(h[2]) * 4 - self.hdrlen = hdrlen - self._nimages = int(h[26]) - # Point to the first image in the stack - offset = hdrlen * 2 - self.imgnumber = 1 - elif self.istack == 0 and self.imgnumber > 0: - # stk=0, img>0: an image within the stack - offset = hdrlen + self.stkoffset - self.istack = 2 # So Image knows it's still a stack - else: - msg = "inconsistent stack header values" - raise SyntaxError(msg) - - if self.bigendian: - self.rawmode = "F;32BF" - else: - self.rawmode = "F;32F" - self._mode = "F" - - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, offset, self.rawmode)] - self._fp = self.fp # FIXME: hack - - @property - def n_frames(self) -> int: - return self._nimages - - @property - def is_animated(self) -> bool: - return self._nimages > 1 - - # 1st image index is zero (although SPIDER imgnumber starts at 1) - def tell(self) -> int: - if self.imgnumber < 1: - return 0 - else: - return self.imgnumber - 1 - - def seek(self, frame: int) -> None: - if self.istack == 0: - msg = "attempt to seek in a non-stack file" - raise EOFError(msg) - if not self._seek_check(frame): - return - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) - self.fp = self._fp - self.fp.seek(self.stkoffset) - self._open() - - # returns a byte image after rescaling to 0..255 - def convert2byte(self, depth: int = 255) -> Image.Image: - extrema = self.getextrema() - assert isinstance(extrema[0], float) - minimum, maximum = cast(tuple[float, float], extrema) - m: float = 1 - if maximum != minimum: - m = depth / (maximum - minimum) - b = -m * minimum - return self.point(lambda i: i * m + b).convert("L") - - if TYPE_CHECKING: - from . import ImageTk - - # returns a ImageTk.PhotoImage object, after rescaling to 0..255 - def tkPhotoImage(self) -> ImageTk.PhotoImage: - from . import ImageTk - - return ImageTk.PhotoImage(self.convert2byte(), palette=256) - - -# -------------------------------------------------------------------- -# Image series - - -# given a list of filenames, return a list of images -def loadImageSeries(filelist: list[str] | None = None) -> list[Image.Image] | None: - """create a list of :py:class:`~PIL.Image.Image` objects for use in a montage""" - if filelist is None or len(filelist) < 1: - return None - - byte_imgs = [] - for img in filelist: - if not os.path.exists(img): - print(f"unable to find {img}") - continue - try: - with Image.open(img) as im: - assert isinstance(im, SpiderImageFile) - byte_im = im.convert2byte() - except Exception: - if not isSpiderImage(img): - print(f"{img} is not a Spider image file") - continue - byte_im.info["filename"] = img - byte_imgs.append(byte_im) - return byte_imgs - - -# -------------------------------------------------------------------- -# For saving images in Spider format - - -def makeSpiderHeader(im: Image.Image) -> list[bytes]: - nsam, nrow = im.size - lenbyt = max(1, nsam) * 4 # There are labrec records in the header - labrec = int(1024 / lenbyt) - if 1024 % lenbyt != 0: - labrec += 1 - labbyt = labrec * lenbyt - nvalues = int(labbyt / 4) - if nvalues < 23: - return [] - - hdr = [0.0] * nvalues - - # NB these are Fortran indices - hdr[1] = 1.0 # nslice (=1 for an image) - hdr[2] = float(nrow) # number of rows per slice - hdr[3] = float(nrow) # number of records in the image - hdr[5] = 1.0 # iform for 2D image - hdr[12] = float(nsam) # number of pixels per line - hdr[13] = float(labrec) # number of records in file header - hdr[22] = float(labbyt) # total number of bytes in header - hdr[23] = float(lenbyt) # record length in bytes - - # adjust for Fortran indexing - hdr = hdr[1:] - hdr.append(0.0) - # pack binary data into a string - return [struct.pack("f", v) for v in hdr] - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode != "F": - im = im.convert("F") - - hdr = makeSpiderHeader(im) - if len(hdr) < 256: - msg = "Error creating Spider header" - raise OSError(msg) - - # write the SPIDER header - fp.writelines(hdr) - - rawmode = "F;32NF" # 32-bit native floating point - ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, rawmode)]) - - -def _save_spider(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - # get the filename extension and register it with Image - if filename_ext := os.path.splitext(filename)[1]: - ext = filename_ext.decode() if isinstance(filename_ext, bytes) else filename_ext - Image.register_extension(SpiderImageFile.format, ext) - _save(im, fp, filename) - - -# -------------------------------------------------------------------- - - -Image.register_open(SpiderImageFile.format, SpiderImageFile) -Image.register_save(SpiderImageFile.format, _save_spider) - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Syntax: python3 SpiderImagePlugin.py [infile] [outfile]") - sys.exit() - - filename = sys.argv[1] - if not isSpiderImage(filename): - print("input image must be in Spider format") - sys.exit() - - with Image.open(filename) as im: - print(f"image: {im}") - print(f"format: {im.format}") - print(f"size: {im.size}") - print(f"mode: {im.mode}") - print("max, min: ", end=" ") - print(im.getextrema()) - - if len(sys.argv) > 2: - outfile = sys.argv[2] - - # perform some image operation - transposed_im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) - print( - f"saving a flipped version of {os.path.basename(filename)} " - f"as {outfile} " - ) - transposed_im.save(outfile, SpiderImageFile.format) diff --git a/.venv/lib/python3.12/site-packages/PIL/SunImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/SunImagePlugin.py deleted file mode 100644 index 8912379e..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/SunImagePlugin.py +++ /dev/null @@ -1,145 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Sun image file handling -# -# History: -# 1995-09-10 fl Created -# 1996-05-28 fl Fixed 32-bit alignment -# 1998-12-29 fl Import ImagePalette module -# 2001-12-18 fl Fixed palette loading (from Jean-Claude Rimbault) -# -# Copyright (c) 1997-2001 by Secret Labs AB -# Copyright (c) 1995-1996 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile, ImagePalette -from ._binary import i32be as i32 - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 4 and i32(prefix) == 0x59A66A95 - - -## -# Image plugin for Sun raster files. - - -class SunImageFile(ImageFile.ImageFile): - format = "SUN" - format_description = "Sun Raster File" - - def _open(self) -> None: - # The Sun Raster file header is 32 bytes in length - # and has the following format: - - # typedef struct _SunRaster - # { - # DWORD MagicNumber; /* Magic (identification) number */ - # DWORD Width; /* Width of image in pixels */ - # DWORD Height; /* Height of image in pixels */ - # DWORD Depth; /* Number of bits per pixel */ - # DWORD Length; /* Size of image data in bytes */ - # DWORD Type; /* Type of raster file */ - # DWORD ColorMapType; /* Type of color map */ - # DWORD ColorMapLength; /* Size of the color map in bytes */ - # } SUNRASTER; - - assert self.fp is not None - - # HEAD - s = self.fp.read(32) - if not _accept(s): - msg = "not an SUN raster file" - raise SyntaxError(msg) - - offset = 32 - - self._size = i32(s, 4), i32(s, 8) - - depth = i32(s, 12) - # data_length = i32(s, 16) # unreliable, ignore. - file_type = i32(s, 20) - palette_type = i32(s, 24) # 0: None, 1: RGB, 2: Raw/arbitrary - palette_length = i32(s, 28) - - if depth == 1: - self._mode, rawmode = "1", "1;I" - elif depth == 4: - self._mode, rawmode = "L", "L;4" - elif depth == 8: - self._mode = rawmode = "L" - elif depth == 24: - if file_type == 3: - self._mode, rawmode = "RGB", "RGB" - else: - self._mode, rawmode = "RGB", "BGR" - elif depth == 32: - if file_type == 3: - self._mode, rawmode = "RGB", "RGBX" - else: - self._mode, rawmode = "RGB", "BGRX" - else: - msg = "Unsupported Mode/Bit Depth" - raise SyntaxError(msg) - - if palette_length: - if palette_length > 1024: - msg = "Unsupported Color Palette Length" - raise SyntaxError(msg) - - if palette_type != 1: - msg = "Unsupported Palette Type" - raise SyntaxError(msg) - - offset = offset + palette_length - self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length)) - if self.mode == "L": - self._mode = "P" - rawmode = rawmode.replace("L", "P") - - # 16 bit boundaries on stride - stride = ((self.size[0] * depth + 15) // 16) * 2 - - # file type: Type is the version (or flavor) of the bitmap - # file. The following values are typically found in the Type - # field: - # 0000h Old - # 0001h Standard - # 0002h Byte-encoded - # 0003h RGB format - # 0004h TIFF format - # 0005h IFF format - # FFFFh Experimental - - # Old and standard are the same, except for the length tag. - # byte-encoded is run-length-encoded - # RGB looks similar to standard, but RGB byte order - # TIFF and IFF mean that they were converted from T/IFF - # Experimental means that it's something else. - # (https://www.fileformat.info/format/sunraster/egff.htm) - - if file_type in (0, 1, 3, 4, 5): - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, offset, (rawmode, stride)) - ] - elif file_type == 2: - self.tile = [ - ImageFile._Tile("sun_rle", (0, 0) + self.size, offset, rawmode) - ] - else: - msg = "Unsupported Sun Raster file type" - raise SyntaxError(msg) - - -# -# registry - - -Image.register_open(SunImageFile.format, SunImageFile, _accept) - -Image.register_extension(SunImageFile.format, ".ras") diff --git a/.venv/lib/python3.12/site-packages/PIL/TarIO.py b/.venv/lib/python3.12/site-packages/PIL/TarIO.py deleted file mode 100644 index 86490a49..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/TarIO.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# read files from within a tar file -# -# History: -# 95-06-18 fl Created -# 96-05-28 fl Open files in binary mode -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1995-96. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io - -from . import ContainerIO - - -class TarIO(ContainerIO.ContainerIO[bytes]): - """A file object that provides read access to a given member of a TAR file.""" - - def __init__(self, tarfile: str, file: str) -> None: - """ - Create file object. - - :param tarfile: Name of TAR file. - :param file: Name of member file. - """ - self.fh = open(tarfile, "rb") - - while True: - s = self.fh.read(512) - if len(s) != 512: - self.fh.close() - - msg = "unexpected end of tar file" - raise OSError(msg) - - name = s[:100].decode("utf-8") - i = name.find("\0") - if i == 0: - self.fh.close() - - msg = "cannot find subfile" - raise OSError(msg) - if i > 0: - name = name[:i] - - size = int(s[124:135], 8) - - if file == name: - break - - self.fh.seek((size + 511) & (~511), io.SEEK_CUR) - - # Open region - super().__init__(self.fh, self.fh.tell(), size) diff --git a/.venv/lib/python3.12/site-packages/PIL/TgaImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/TgaImagePlugin.py deleted file mode 100644 index b2989a4b..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/TgaImagePlugin.py +++ /dev/null @@ -1,280 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# TGA file handling -# -# History: -# 95-09-01 fl created (reads 24-bit files only) -# 97-01-04 fl support more TGA versions, including compressed images -# 98-07-04 fl fixed orientation and alpha layer bugs -# 98-09-11 fl fixed orientation for runlength decoder -# -# Copyright (c) Secret Labs AB 1997-98. -# Copyright (c) Fredrik Lundh 1995-97. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -import warnings -from typing import IO - -from . import Image, ImageFile, ImagePalette -from ._binary import i16le as i16 -from ._binary import i32le as i32 -from ._binary import o8 -from ._binary import o16le as o16 - -# -# -------------------------------------------------------------------- -# Read RGA file - - -MODES = { - # map imagetype/depth to rawmode - (1, 8): "P", - (3, 1): "1", - (3, 8): "L", - (3, 16): "LA", - (2, 16): "BGRA;15Z", - (2, 24): "BGR", - (2, 32): "BGRA", -} - - -## -# Image plugin for Targa files. - - -class TgaImageFile(ImageFile.ImageFile): - format = "TGA" - format_description = "Targa" - - def _open(self) -> None: - # process header - assert self.fp is not None - - s = self.fp.read(18) - - id_len = s[0] - - colormaptype = s[1] - imagetype = s[2] - - depth = s[16] - - flags = s[17] - - self._size = i16(s, 12), i16(s, 14) - - # validate header fields - if ( - colormaptype not in (0, 1) - or self.size[0] <= 0 - or self.size[1] <= 0 - or depth not in (1, 8, 16, 24, 32) - ): - msg = "not a TGA file" - raise SyntaxError(msg) - - # image mode - if imagetype in (3, 11): - self._mode = "L" - if depth == 1: - self._mode = "1" # ??? - elif depth == 16: - self._mode = "LA" - elif imagetype in (1, 9): - self._mode = "P" if colormaptype else "L" - elif imagetype in (2, 10): - self._mode = "RGB" if depth == 24 else "RGBA" - else: - msg = "unknown TGA mode" - raise SyntaxError(msg) - - # orientation - orientation = flags & 0x30 - self._flip_horizontally = orientation in [0x10, 0x30] - if orientation in [0x20, 0x30]: - orientation = 1 - elif orientation in [0, 0x10]: - orientation = -1 - else: - msg = "unknown TGA orientation" - raise SyntaxError(msg) - - self.info["orientation"] = orientation - - if imagetype & 8: - self.info["compression"] = "tga_rle" - - if id_len: - self.info["id_section"] = self.fp.read(id_len) - - if colormaptype: - # read palette - start, size, mapdepth = i16(s, 3), i16(s, 5), s[7] - if mapdepth == 16: - self.palette = ImagePalette.raw( - "BGRA;15Z", bytes(2 * start) + self.fp.read(2 * size) - ) - self.palette.mode = "RGBA" - elif mapdepth == 24: - self.palette = ImagePalette.raw( - "BGR", bytes(3 * start) + self.fp.read(3 * size) - ) - elif mapdepth == 32: - self.palette = ImagePalette.raw( - "BGRA", bytes(4 * start) + self.fp.read(4 * size) - ) - else: - msg = "unknown TGA map depth" - raise SyntaxError(msg) - - # setup tile descriptor - try: - rawmode = MODES[(imagetype & 7, depth)] - if imagetype & 8: - # compressed - self.tile = [ - ImageFile._Tile( - "tga_rle", - (0, 0) + self.size, - self.fp.tell(), - (rawmode, orientation, depth), - ) - ] - else: - self.tile = [ - ImageFile._Tile( - "raw", - (0, 0) + self.size, - self.fp.tell(), - (rawmode, 0, orientation), - ) - ] - except KeyError: - pass # cannot decode - - def load_end(self) -> None: - if self.mode == "RGBA": - assert self.fp is not None - self.fp.seek(-26, os.SEEK_END) - footer = self.fp.read(26) - if footer.endswith(b"TRUEVISION-XFILE.\x00"): - # version 2 - extension_offset = i32(footer) - if extension_offset: - self.fp.seek(extension_offset + 494) - attributes_type = self.fp.read(1) - if attributes_type == b"\x00": - # No alpha - self.im.fillband(3, 255) - - if self._flip_horizontally: - self.im = self.im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) - - -# -# -------------------------------------------------------------------- -# Write TGA file - - -SAVE = { - "1": ("1", 1, 0, 3), - "L": ("L", 8, 0, 3), - "LA": ("LA", 16, 0, 3), - "P": ("P", 8, 1, 1), - "RGB": ("BGR", 24, 0, 2), - "RGBA": ("BGRA", 32, 0, 2), -} - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - try: - rawmode, bits, colormaptype, imagetype = SAVE[im.mode] - except KeyError as e: - msg = f"cannot write mode {im.mode} as TGA" - raise OSError(msg) from e - - if "rle" in im.encoderinfo: - rle = im.encoderinfo["rle"] - else: - compression = im.encoderinfo.get("compression", im.info.get("compression")) - rle = compression == "tga_rle" - if rle: - imagetype += 8 - - id_section = im.encoderinfo.get("id_section", im.info.get("id_section", "")) - id_len = len(id_section) - if id_len > 255: - id_len = 255 - id_section = id_section[:255] - warnings.warn("id_section has been trimmed to 255 characters") - - if colormaptype: - palette = im.im.getpalette("RGB", "BGR") - colormaplength, colormapentry = len(palette) // 3, 24 - else: - colormaplength, colormapentry = 0, 0 - - if im.mode in ("LA", "RGBA"): - flags = 8 - else: - flags = 0 - - orientation = im.encoderinfo.get("orientation", im.info.get("orientation", -1)) - if orientation > 0: - flags = flags | 0x20 - - fp.write( - o8(id_len) - + o8(colormaptype) - + o8(imagetype) - + o16(0) # colormapfirst - + o16(colormaplength) - + o8(colormapentry) - + o16(0) - + o16(0) - + o16(im.size[0]) - + o16(im.size[1]) - + o8(bits) - + o8(flags) - ) - - if id_section: - fp.write(id_section) - - if colormaptype: - fp.write(palette) - - if rle: - ImageFile._save( - im, - fp, - [ImageFile._Tile("tga_rle", (0, 0) + im.size, 0, (rawmode, orientation))], - ) - else: - ImageFile._save( - im, - fp, - [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))], - ) - - # write targa version 2 footer - fp.write(b"\000" * 8 + b"TRUEVISION-XFILE." + b"\000") - - -# -# -------------------------------------------------------------------- -# Registry - - -Image.register_open(TgaImageFile.format, TgaImageFile) -Image.register_save(TgaImageFile.format, _save) - -Image.register_extensions(TgaImageFile.format, [".tga", ".icb", ".vda", ".vst"]) - -Image.register_mime(TgaImageFile.format, "image/x-tga") diff --git a/.venv/lib/python3.12/site-packages/PIL/TiffImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/TiffImagePlugin.py deleted file mode 100644 index 5094faa1..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/TiffImagePlugin.py +++ /dev/null @@ -1,2353 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# TIFF file handling -# -# TIFF is a flexible, if somewhat aged, image file format originally -# defined by Aldus. Although TIFF supports a wide variety of pixel -# layouts and compression methods, the name doesn't really stand for -# "thousands of incompatible file formats," it just feels that way. -# -# To read TIFF data from a stream, the stream must be seekable. For -# progressive decoding, make sure to use TIFF files where the tag -# directory is placed first in the file. -# -# History: -# 1995-09-01 fl Created -# 1996-05-04 fl Handle JPEGTABLES tag -# 1996-05-18 fl Fixed COLORMAP support -# 1997-01-05 fl Fixed PREDICTOR support -# 1997-08-27 fl Added support for rational tags (from Perry Stoll) -# 1998-01-10 fl Fixed seek/tell (from Jan Blom) -# 1998-07-15 fl Use private names for internal variables -# 1999-06-13 fl Rewritten for PIL 1.0 (1.0) -# 2000-10-11 fl Additional fixes for Python 2.0 (1.1) -# 2001-04-17 fl Fixed rewind support (seek to frame 0) (1.2) -# 2001-05-12 fl Added write support for more tags (from Greg Couch) (1.3) -# 2001-12-18 fl Added workaround for broken Matrox library -# 2002-01-18 fl Don't mess up if photometric tag is missing (D. Alan Stewart) -# 2003-05-19 fl Check FILLORDER tag -# 2003-09-26 fl Added RGBa support -# 2004-02-24 fl Added DPI support; fixed rational write support -# 2005-02-07 fl Added workaround for broken Corel Draw 10 files -# 2006-01-09 fl Added support for float/double tags (from Russell Nelson) -# -# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved. -# Copyright (c) 1995-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import itertools -import logging -import math -import os -import struct -import warnings -from collections.abc import Callable, MutableMapping -from fractions import Fraction -from numbers import Number, Rational -from typing import IO, Any, cast - -from . import ExifTags, Image, ImageFile, ImageOps, ImagePalette, TiffTags -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._binary import o8 -from ._util import DeferredError, is_path -from .TiffTags import TYPES - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Iterator - from typing import NoReturn - - from ._typing import Buffer, IntegralLike, StrOrBytesPath - -logger = logging.getLogger(__name__) - -# Set these to true to force use of libtiff for reading or writing. -READ_LIBTIFF = False -WRITE_LIBTIFF = False -STRIP_SIZE = 65536 - -II = b"II" # little-endian (Intel style) -MM = b"MM" # big-endian (Motorola style) - -# -# -------------------------------------------------------------------- -# Read TIFF files - -# a few tag names, just to make the code below a bit more readable -OSUBFILETYPE = 255 -IMAGEWIDTH = 256 -IMAGELENGTH = 257 -BITSPERSAMPLE = 258 -COMPRESSION = 259 -PHOTOMETRIC_INTERPRETATION = 262 -FILLORDER = 266 -IMAGEDESCRIPTION = 270 -STRIPOFFSETS = 273 -SAMPLESPERPIXEL = 277 -ROWSPERSTRIP = 278 -STRIPBYTECOUNTS = 279 -X_RESOLUTION = 282 -Y_RESOLUTION = 283 -PLANAR_CONFIGURATION = 284 -RESOLUTION_UNIT = 296 -TRANSFERFUNCTION = 301 -SOFTWARE = 305 -DATE_TIME = 306 -ARTIST = 315 -PREDICTOR = 317 -COLORMAP = 320 -TILEWIDTH = 322 -TILELENGTH = 323 -TILEOFFSETS = 324 -TILEBYTECOUNTS = 325 -SUBIFD = 330 -EXTRASAMPLES = 338 -SAMPLEFORMAT = 339 -JPEGTABLES = 347 -YCBCRSUBSAMPLING = 530 -REFERENCEBLACKWHITE = 532 -COPYRIGHT = 33432 -IPTC_NAA_CHUNK = 33723 # newsphoto properties -PHOTOSHOP_CHUNK = 34377 # photoshop properties -ICCPROFILE = 34675 -EXIFIFD = 34665 -XMP = 700 -JPEGQUALITY = 65537 # pseudo-tag by libtiff - -# https://github.com/imagej/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java -IMAGEJ_META_DATA_BYTE_COUNTS = 50838 -IMAGEJ_META_DATA = 50839 - -COMPRESSION_INFO = { - # Compression => pil compression name - 1: "raw", - 2: "tiff_ccitt", - 3: "group3", - 4: "group4", - 5: "tiff_lzw", - 6: "tiff_jpeg", # obsolete - 7: "jpeg", - 8: "tiff_adobe_deflate", - 32771: "tiff_raw_16", # 16-bit padding - 32773: "packbits", - 32809: "tiff_thunderscan", - 32946: "tiff_deflate", - 34676: "tiff_sgilog", - 34677: "tiff_sgilog24", - 34925: "lzma", - 50000: "zstd", - 50001: "webp", -} - -COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()} - -OPEN_INFO = { - # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, - # ExtraSamples) => mode, rawmode - (II, 0, (1,), 1, (1,), ()): ("1", "1;I"), - (MM, 0, (1,), 1, (1,), ()): ("1", "1;I"), - (II, 0, (1,), 2, (1,), ()): ("1", "1;IR"), - (MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"), - (II, 1, (1,), 1, (1,), ()): ("1", "1"), - (MM, 1, (1,), 1, (1,), ()): ("1", "1"), - (II, 1, (1,), 2, (1,), ()): ("1", "1;R"), - (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"), - (II, 0, (1,), 1, (2,), ()): ("L", "L;2I"), - (MM, 0, (1,), 1, (2,), ()): ("L", "L;2I"), - (II, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), - (MM, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), - (II, 1, (1,), 1, (2,), ()): ("L", "L;2"), - (MM, 1, (1,), 1, (2,), ()): ("L", "L;2"), - (II, 1, (1,), 2, (2,), ()): ("L", "L;2R"), - (MM, 1, (1,), 2, (2,), ()): ("L", "L;2R"), - (II, 0, (1,), 1, (4,), ()): ("L", "L;4I"), - (MM, 0, (1,), 1, (4,), ()): ("L", "L;4I"), - (II, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), - (MM, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), - (II, 1, (1,), 1, (4,), ()): ("L", "L;4"), - (MM, 1, (1,), 1, (4,), ()): ("L", "L;4"), - (II, 1, (1,), 2, (4,), ()): ("L", "L;4R"), - (MM, 1, (1,), 2, (4,), ()): ("L", "L;4R"), - (II, 0, (1,), 1, (8,), ()): ("L", "L;I"), - (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"), - (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"), - (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), - (II, 1, (1,), 1, (8,), ()): ("L", "L"), - (MM, 1, (1,), 1, (8,), ()): ("L", "L"), - (II, 1, (2,), 1, (8,), ()): ("L", "L"), - (MM, 1, (2,), 1, (8,), ()): ("L", "L"), - (II, 1, (1,), 2, (8,), ()): ("L", "L;R"), - (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"), - (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"), - (II, 0, (1,), 1, (16,), ()): ("I;16", "I;16"), - (II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"), - (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"), - (II, 1, (1,), 2, (16,), ()): ("I;16", "I;16R"), - (II, 1, (2,), 1, (16,), ()): ("I", "I;16S"), - (MM, 1, (2,), 1, (16,), ()): ("I", "I;16BS"), - (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"), - (MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"), - (II, 1, (1,), 1, (32,), ()): ("I", "I;32N"), - (II, 1, (2,), 1, (32,), ()): ("I", "I;32S"), - (MM, 1, (2,), 1, (32,), ()): ("I", "I;32BS"), - (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"), - (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"), - (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), - (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), - (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), - (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), - (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), - (MM, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), - (II, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples - (MM, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples - (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGB", "RGBX"), - (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGB", "RGBX"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGB", "RGBXX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGB", "RGBXX"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGB", "RGBXXX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGB", "RGBXXX"), - (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), - (MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"), - (II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), - (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"), - (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 - (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 - (II, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16L"), - (MM, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16B"), - (II, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16L"), - (MM, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16B"), - (II, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGB", "RGBX;16L"), - (MM, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGB", "RGBX;16B"), - (II, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16L"), - (MM, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16B"), - (II, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16L"), - (MM, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16B"), - (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), - (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"), - (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), - (MM, 3, (1,), 2, (1,), ()): ("P", "P;1R"), - (II, 3, (1,), 1, (2,), ()): ("P", "P;2"), - (MM, 3, (1,), 1, (2,), ()): ("P", "P;2"), - (II, 3, (1,), 2, (2,), ()): ("P", "P;2R"), - (MM, 3, (1,), 2, (2,), ()): ("P", "P;2R"), - (II, 3, (1,), 1, (4,), ()): ("P", "P;4"), - (MM, 3, (1,), 1, (4,), ()): ("P", "P;4"), - (II, 3, (1,), 2, (4,), ()): ("P", "P;4R"), - (MM, 3, (1,), 2, (4,), ()): ("P", "P;4R"), - (II, 3, (1,), 1, (8,), ()): ("P", "P"), - (MM, 3, (1,), 1, (8,), ()): ("P", "P"), - (II, 3, (1,), 1, (8, 8), (0,)): ("P", "PX"), - (MM, 3, (1,), 1, (8, 8), (0,)): ("P", "PX"), - (II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), - (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), - (II, 3, (1,), 2, (8,), ()): ("P", "P;R"), - (MM, 3, (1,), 2, (8,), ()): ("P", "P;R"), - (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), - (MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), - (II, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"), - (MM, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"), - (II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), - (MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), - (II, 5, (1,), 1, (16, 16, 16, 16), ()): ("CMYK", "CMYK;16L"), - (MM, 5, (1,), 1, (16, 16, 16, 16), ()): ("CMYK", "CMYK;16B"), - (II, 6, (1,), 1, (8,), ()): ("L", "L"), - (MM, 6, (1,), 1, (8,), ()): ("L", "L"), - # JPEG compressed images handled by LibTiff and auto-converted to RGBX - # Minimal Baseline TIFF requires YCbCr images to have 3 SamplesPerPixel - (II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"), - (MM, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"), - (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), - (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), -} - -MAX_SAMPLESPERPIXEL = max(len(key_tp[4]) for key_tp in OPEN_INFO) - -PREFIXES = [ - b"MM\x00\x2a", # Valid TIFF header with big-endian byte order - b"II\x2a\x00", # Valid TIFF header with little-endian byte order - b"MM\x2a\x00", # Invalid TIFF header, assume big-endian - b"II\x00\x2a", # Invalid TIFF header, assume little-endian - b"MM\x00\x2b", # BigTIFF with big-endian byte order - b"II\x2b\x00", # BigTIFF with little-endian byte order -] - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(tuple(PREFIXES)) - - -def _limit_rational( - val: float | Fraction | IFDRational, max_val: int -) -> tuple[IntegralLike, IntegralLike]: - inv = abs(val) > 1 - n_d = IFDRational(1 / val if inv else val).limit_rational(max_val) - return n_d[::-1] if inv else n_d - - -def _limit_signed_rational( - val: IFDRational, max_val: int, min_val: int -) -> tuple[IntegralLike, IntegralLike]: - frac = Fraction(val) - n_d: tuple[IntegralLike, IntegralLike] = frac.numerator, frac.denominator - - if min(float(i) for i in n_d) < min_val: - n_d = _limit_rational(val, abs(min_val)) - - n_d_float = tuple(float(i) for i in n_d) - if max(n_d_float) > max_val: - n_d = _limit_rational(n_d_float[0] / n_d_float[1], max_val) - - return n_d - - -## -# Wrapper for TIFF IFDs. - -_load_dispatch = {} -_write_dispatch = {} - - -def _delegate(op: str) -> Any: - def delegate( - self: IFDRational, *args: tuple[float, ...] - ) -> bool | float | Fraction: - return getattr(self._val, op)(*args) - - return delegate - - -class IFDRational(Rational): - """Implements a rational class where 0/0 is a legal value to match - the in the wild use of exif rationals. - - e.g., DigitalZoomRatio - 0.00/0.00 indicates that no digital zoom was used - """ - - """ If the denominator is 0, store this as a float('nan'), otherwise store - as a fractions.Fraction(). Delegate as appropriate - - """ - - __slots__ = ("_numerator", "_denominator", "_val") - - def __init__( - self, value: float | Fraction | IFDRational, denominator: int = 1 - ) -> None: - """ - :param value: either an integer numerator, a - float/rational/other number, or an IFDRational - :param denominator: Optional integer denominator - """ - self._val: Fraction | float - if isinstance(value, IFDRational): - self._numerator = value.numerator - self._denominator = value.denominator - self._val = value._val - return - - if isinstance(value, Fraction): - self._numerator = value.numerator - self._denominator = value.denominator - else: - if TYPE_CHECKING: - self._numerator = cast(IntegralLike, value) - else: - self._numerator = value - self._denominator = denominator - - if denominator == 0: - self._val = float("nan") - elif denominator == 1: - self._val = Fraction(value) - elif int(value) == value: - self._val = Fraction(int(value), denominator) - else: - self._val = Fraction(value / denominator) - - @property - def numerator(self) -> IntegralLike: - return self._numerator - - @property - def denominator(self) -> int: - return self._denominator - - def limit_rational(self, max_denominator: int) -> tuple[IntegralLike, int]: - """ - - :param max_denominator: Integer, the maximum denominator value - :returns: Tuple of (numerator, denominator) - """ - - if self.denominator == 0: - return self.numerator, self.denominator - - assert isinstance(self._val, Fraction) - f = self._val.limit_denominator(max_denominator) - return f.numerator, f.denominator - - def __repr__(self) -> str: - return str(float(self._val)) - - def __hash__(self) -> int: # type: ignore[override] - return self._val.__hash__() - - def __eq__(self, other: object) -> bool: - val = self._val - if isinstance(other, IFDRational): - other = other._val - if isinstance(other, float): - val = float(val) - return val == other - - def __getstate__(self) -> list[float | Fraction | IntegralLike]: - return [self._val, self._numerator, self._denominator] - - def __setstate__(self, state: list[float | Fraction | IntegralLike]) -> None: - IFDRational.__init__(self, 0) - _val, _numerator, _denominator = state - assert isinstance(_val, (float, Fraction)) - self._val = _val - if TYPE_CHECKING: - self._numerator = cast(IntegralLike, _numerator) - else: - self._numerator = _numerator - assert isinstance(_denominator, int) - self._denominator = _denominator - - """ a = ['add','radd', 'sub', 'rsub', 'mul', 'rmul', - 'truediv', 'rtruediv', 'floordiv', 'rfloordiv', - 'mod','rmod', 'pow','rpow', 'pos', 'neg', - 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'bool', - 'ceil', 'floor', 'round'] - print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a)) - """ - - __add__ = _delegate("__add__") - __radd__ = _delegate("__radd__") - __sub__ = _delegate("__sub__") - __rsub__ = _delegate("__rsub__") - __mul__ = _delegate("__mul__") - __rmul__ = _delegate("__rmul__") - __truediv__ = _delegate("__truediv__") - __rtruediv__ = _delegate("__rtruediv__") - __floordiv__ = _delegate("__floordiv__") - __rfloordiv__ = _delegate("__rfloordiv__") - __mod__ = _delegate("__mod__") - __rmod__ = _delegate("__rmod__") - __pow__ = _delegate("__pow__") - __rpow__ = _delegate("__rpow__") - __pos__ = _delegate("__pos__") - __neg__ = _delegate("__neg__") - __abs__ = _delegate("__abs__") - __trunc__ = _delegate("__trunc__") - __lt__ = _delegate("__lt__") - __gt__ = _delegate("__gt__") - __le__ = _delegate("__le__") - __ge__ = _delegate("__ge__") - __bool__ = _delegate("__bool__") - __ceil__ = _delegate("__ceil__") - __floor__ = _delegate("__floor__") - __round__ = _delegate("__round__") - # Python >= 3.11 - if hasattr(Fraction, "__int__"): - __int__ = _delegate("__int__") - - -_LoaderFunc = Callable[["ImageFileDirectory_v2", bytes, bool], Any] - - -def _register_loader(idx: int, size: int) -> Callable[[_LoaderFunc], _LoaderFunc]: - def decorator(func: _LoaderFunc) -> _LoaderFunc: - from .TiffTags import TYPES - - if func.__name__.startswith("load_"): - TYPES[idx] = func.__name__[5:].replace("_", " ") - _load_dispatch[idx] = size, func # noqa: F821 - return func - - return decorator - - -def _register_writer(idx: int) -> Callable[[Callable[..., Any]], Callable[..., Any]]: - def decorator(func: Callable[..., Any]) -> Callable[..., Any]: - _write_dispatch[idx] = func # noqa: F821 - return func - - return decorator - - -def _register_basic(idx_fmt_name: tuple[int, str, str]) -> None: - from .TiffTags import TYPES - - idx, fmt, name = idx_fmt_name - TYPES[idx] = name - size = struct.calcsize(f"={fmt}") - - def basic_handler( - self: ImageFileDirectory_v2, data: bytes, legacy_api: bool = True - ) -> tuple[Any, ...]: - return self._unpack(f"{len(data) // size}{fmt}", data) - - _load_dispatch[idx] = size, basic_handler # noqa: F821 - _write_dispatch[idx] = lambda self, *values: ( # noqa: F821 - b"".join(self._pack(fmt, value) for value in values) - ) - - -if TYPE_CHECKING: - _IFDv2Base = MutableMapping[int, Any] -else: - _IFDv2Base = MutableMapping - - -class ImageFileDirectory_v2(_IFDv2Base): - """This class represents a TIFF tag directory. To speed things up, we - don't decode tags unless they're asked for. - - Exposes a dictionary interface of the tags in the directory:: - - ifd = ImageFileDirectory_v2() - ifd[key] = 'Some Data' - ifd.tagtype[key] = TiffTags.ASCII - print(ifd[key]) - 'Some Data' - - Individual values are returned as the strings or numbers, sequences are - returned as tuples of the values. - - The tiff metadata type of each item is stored in a dictionary of - tag types in - :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v2.tagtype`. The types - are read from a tiff file, guessed from the type added, or added - manually. - - Data Structures: - - * ``self.tagtype = {}`` - - * Key: numerical TIFF tag number - * Value: integer corresponding to the data type from - :py:data:`.TiffTags.TYPES` - - .. versionadded:: 3.0.0 - - 'Internal' data structures: - - * ``self._tags_v2 = {}`` - - * Key: numerical TIFF tag number - * Value: decoded data, as tuple for multiple values - - * ``self._tagdata = {}`` - - * Key: numerical TIFF tag number - * Value: undecoded byte string from file - - * ``self._tags_v1 = {}`` - - * Key: numerical TIFF tag number - * Value: decoded data in the v1 format - - Tags will be found in the private attributes ``self._tagdata``, and in - ``self._tags_v2`` once decoded. - - ``self.legacy_api`` is a value for internal use, and shouldn't be changed - from outside code. In cooperation with - :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1`, if ``legacy_api`` - is true, then decoded tags will be populated into both ``_tags_v1`` and - ``_tags_v2``. ``_tags_v2`` will be used if this IFD is used in the TIFF - save routine. Tags should be read from ``_tags_v1`` if - ``legacy_api == true``. - - """ - - _load_dispatch: dict[int, tuple[int, _LoaderFunc]] = {} - _write_dispatch: dict[int, Callable[..., Any]] = {} - - def __init__( - self, - ifh: bytes = b"II\x2a\x00\x00\x00\x00\x00", - prefix: bytes | None = None, - group: int | None = None, - ) -> None: - """Initialize an ImageFileDirectory. - - To construct an ImageFileDirectory from a real file, pass the 8-byte - magic header to the constructor. To only set the endianness, pass it - as the 'prefix' keyword argument. - - :param ifh: One of the accepted magic headers (cf. PREFIXES); also sets - endianness. - :param prefix: Override the endianness of the file. - """ - if not _accept(ifh): - msg = f"not a TIFF file (header {repr(ifh)} not valid)" - raise SyntaxError(msg) - self._prefix = prefix if prefix is not None else ifh[:2] - if self._prefix == MM: - self._endian = ">" - elif self._prefix == II: - self._endian = "<" - else: - msg = "not a TIFF IFD" - raise SyntaxError(msg) - self._bigtiff = ifh[2] == 43 - self.group = group - self.tagtype: dict[int, int] = {} - """ Dictionary of tag types """ - self.reset() - self.next = ( - self._unpack("Q", ifh[8:])[0] - if self._bigtiff - else self._unpack("L", ifh[4:])[0] - ) - self._legacy_api = False - - prefix = property(lambda self: self._prefix) - offset = property(lambda self: self._offset) - - @property - def legacy_api(self) -> bool: - return self._legacy_api - - @legacy_api.setter - def legacy_api(self, value: bool) -> NoReturn: - msg = "Not allowing setting of legacy api" - raise Exception(msg) - - def reset(self) -> None: - self._tags_v1: dict[int, Any] = {} # will remain empty if legacy_api is false - self._tags_v2: dict[int, Any] = {} # main tag storage - self._tagdata: dict[int, bytes] = {} - self.tagtype = {} # added 2008-06-05 by Florian Hoech - self._next = None - self._offset: int | None = None - - def __str__(self) -> str: - return str(dict(self)) - - def named(self) -> dict[str, Any]: - """ - :returns: dict of name|key: value - - Returns the complete tag dictionary, with named tags where possible. - """ - return { - TiffTags.lookup(code, self.group).name: value - for code, value in self.items() - } - - def __len__(self) -> int: - return len(set(self._tagdata) | set(self._tags_v2)) - - def __getitem__(self, tag: int) -> Any: - if tag not in self._tags_v2: # unpack on the fly - data = self._tagdata[tag] - typ = self.tagtype[tag] - size, handler = self._load_dispatch[typ] - self[tag] = handler(self, data, self.legacy_api) # check type - val = self._tags_v2[tag] - if self.legacy_api and not isinstance(val, (tuple, bytes)): - val = (val,) - return val - - def __contains__(self, tag: object) -> bool: - return tag in self._tags_v2 or tag in self._tagdata - - def __setitem__(self, tag: int, value: Any) -> None: - self._setitem(tag, value, self.legacy_api) - - def _setitem(self, tag: int, value: Any, legacy_api: bool) -> None: - basetypes = (Number, bytes, str) - - info = TiffTags.lookup(tag, self.group) - values = [value] if isinstance(value, basetypes) else value - - if tag not in self.tagtype: - if info.type: - self.tagtype[tag] = info.type - else: - self.tagtype[tag] = TiffTags.UNDEFINED - if all(isinstance(v, IFDRational) for v in values): - for v in values: - assert isinstance(v, IFDRational) - if v < 0: - self.tagtype[tag] = TiffTags.SIGNED_RATIONAL - break - else: - self.tagtype[tag] = TiffTags.RATIONAL - elif all(isinstance(v, int) for v in values): - short = True - signed_short = True - long = True - for v in values: - assert isinstance(v, int) - if short and not (0 <= v < 2**16): - short = False - if signed_short and not (-(2**15) < v < 2**15): - signed_short = False - if long and v < 0: - long = False - if short: - self.tagtype[tag] = TiffTags.SHORT - elif signed_short: - self.tagtype[tag] = TiffTags.SIGNED_SHORT - elif long: - self.tagtype[tag] = TiffTags.LONG - else: - self.tagtype[tag] = TiffTags.SIGNED_LONG - elif all(isinstance(v, float) for v in values): - self.tagtype[tag] = TiffTags.DOUBLE - elif all(isinstance(v, str) for v in values): - self.tagtype[tag] = TiffTags.ASCII - elif all(isinstance(v, bytes) for v in values): - self.tagtype[tag] = TiffTags.BYTE - - if self.tagtype[tag] == TiffTags.UNDEFINED: - values = [ - v.encode("ascii", "replace") if isinstance(v, str) else v - for v in values - ] - elif self.tagtype[tag] == TiffTags.RATIONAL: - values = [float(v) if isinstance(v, int) else v for v in values] - - is_ifd = self.tagtype[tag] == TiffTags.LONG and isinstance(values, dict) - if not is_ifd: - values = tuple( - info.cvt_enum(value) if isinstance(value, str) else value - for value in values - ) - - dest = self._tags_v1 if legacy_api else self._tags_v2 - - # Three branches: - # Spec'd length == 1, Actual length 1, store as element - # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed. - # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple. - # Don't mess with the legacy api, since it's frozen. - if not is_ifd and ( - (info.length == 1) - or self.tagtype[tag] == TiffTags.BYTE - or (info.length is None and len(values) == 1 and not legacy_api) - ): - # Don't mess with the legacy api, since it's frozen. - if legacy_api and self.tagtype[tag] in [ - TiffTags.RATIONAL, - TiffTags.SIGNED_RATIONAL, - ]: # rationals - values = (values,) - try: - (dest[tag],) = values - except ValueError: - # We've got a builtin tag with 1 expected entry - warnings.warn( - f"Metadata Warning, tag {tag} had too many entries: " - f"{len(values)}, expected 1" - ) - dest[tag] = values[0] - - else: - # Spec'd length > 1 or undefined - # Unspec'd, and length > 1 - dest[tag] = values - - def __delitem__(self, tag: int) -> None: - self._tags_v2.pop(tag, None) - self._tags_v1.pop(tag, None) - self._tagdata.pop(tag, None) - - def __iter__(self) -> Iterator[int]: - return iter(set(self._tagdata) | set(self._tags_v2)) - - def _unpack(self, fmt: str, data: bytes) -> tuple[Any, ...]: - return struct.unpack(self._endian + fmt, data) - - def _pack(self, fmt: str, *values: Any) -> bytes: - return struct.pack(self._endian + fmt, *values) - - list( - map( - _register_basic, - [ - (TiffTags.SHORT, "H", "short"), - (TiffTags.LONG, "L", "long"), - (TiffTags.SIGNED_BYTE, "b", "signed byte"), - (TiffTags.SIGNED_SHORT, "h", "signed short"), - (TiffTags.SIGNED_LONG, "l", "signed long"), - (TiffTags.FLOAT, "f", "float"), - (TiffTags.DOUBLE, "d", "double"), - (TiffTags.IFD, "L", "long"), - (TiffTags.LONG8, "Q", "long8"), - ], - ) - ) - - @_register_loader(1, 1) # Basic type, except for the legacy API. - def load_byte(self, data: bytes, legacy_api: bool = True) -> bytes: - return data - - @_register_writer(1) # Basic type, except for the legacy API. - def write_byte(self, data: bytes | int | IFDRational) -> bytes: - if isinstance(data, IFDRational): - data = int(data) - if isinstance(data, int): - data = bytes((data,)) - return data - - @_register_loader(2, 1) - def load_string(self, data: bytes, legacy_api: bool = True) -> str: - if data.endswith(b"\0"): - data = data[:-1] - return data.decode("latin-1", "replace") - - @_register_writer(2) - def write_string(self, value: str | bytes | int) -> bytes: - # remerge of https://github.com/python-pillow/Pillow/pull/1416 - if isinstance(value, int): - value = str(value) - if not isinstance(value, bytes): - value = value.encode("ascii", "replace") - return value + b"\0" - - @_register_loader(5, 8) - def load_rational( - self, data: bytes, legacy_api: bool = True - ) -> tuple[tuple[int, int] | IFDRational, ...]: - vals = self._unpack(f"{len(data) // 4}L", data) - - def combine(a: int, b: int) -> tuple[int, int] | IFDRational: - return (a, b) if legacy_api else IFDRational(a, b) - - return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) - - @_register_writer(5) - def write_rational(self, *values: IFDRational) -> bytes: - return b"".join( - self._pack("2L", *_limit_rational(frac, 2**32 - 1)) for frac in values - ) - - @_register_loader(7, 1) - def load_undefined(self, data: bytes, legacy_api: bool = True) -> bytes: - return data - - @_register_writer(7) - def write_undefined(self, value: bytes | int | IFDRational) -> bytes: - if isinstance(value, IFDRational): - value = int(value) - if isinstance(value, int): - value = str(value).encode("ascii", "replace") - return value - - @_register_loader(10, 8) - def load_signed_rational( - self, data: bytes, legacy_api: bool = True - ) -> tuple[tuple[int, int] | IFDRational, ...]: - vals = self._unpack(f"{len(data) // 4}l", data) - - def combine(a: int, b: int) -> tuple[int, int] | IFDRational: - return (a, b) if legacy_api else IFDRational(a, b) - - return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) - - @_register_writer(10) - def write_signed_rational(self, *values: IFDRational) -> bytes: - return b"".join( - self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31))) - for frac in values - ) - - def _ensure_read(self, fp: IO[bytes], size: int) -> bytes: - ret = fp.read(size) - if len(ret) != size: - msg = ( - "Corrupt EXIF data. " - f"Expecting to read {size} bytes but only got {len(ret)}. " - ) - raise OSError(msg) - return ret - - def load(self, fp: IO[bytes]) -> None: - self.reset() - self._offset = fp.tell() - - try: - tag_count = ( - self._unpack("Q", self._ensure_read(fp, 8)) - if self._bigtiff - else self._unpack("H", self._ensure_read(fp, 2)) - )[0] - for i in range(tag_count): - tag, typ, count, data = ( - self._unpack("HHQ8s", self._ensure_read(fp, 20)) - if self._bigtiff - else self._unpack("HHL4s", self._ensure_read(fp, 12)) - ) - - tagname = TiffTags.lookup(tag, self.group).name - typname = TYPES.get(typ, "unknown") - msg = f"tag: {tagname} ({tag}) - type: {typname} ({typ})" - - try: - unit_size, handler = self._load_dispatch[typ] - except KeyError: - logger.debug("%s - unsupported type %s", msg, typ) - continue # ignore unsupported type - size = count * unit_size - if size > (8 if self._bigtiff else 4): - here = fp.tell() - (offset,) = self._unpack("Q" if self._bigtiff else "L", data) - msg += f" Tag Location: {here} - Data Location: {offset}" - fp.seek(offset) - data = ImageFile._safe_read(fp, size) - fp.seek(here) - else: - data = data[:size] - - if len(data) != size: - warnings.warn( - "Possibly corrupt EXIF data. " - f"Expecting to read {size} bytes but only got {len(data)}." - f" Skipping tag {tag}" - ) - logger.debug(msg) - continue - - if not data: - logger.debug(msg) - continue - - self._tagdata[tag] = data - self.tagtype[tag] = typ - - msg += " - value: " - msg += f"" if size > 32 else repr(data) - - logger.debug(msg) - - (self.next,) = ( - self._unpack("Q", self._ensure_read(fp, 8)) - if self._bigtiff - else self._unpack("L", self._ensure_read(fp, 4)) - ) - except OSError as msg: - warnings.warn(str(msg)) - return - - def _get_ifh(self) -> bytes: - ifh = self._prefix + self._pack("H", 43 if self._bigtiff else 42) - if self._bigtiff: - ifh += self._pack("HH", 8, 0) - ifh += self._pack("Q", 16) if self._bigtiff else self._pack("L", 8) - - return ifh - - def tobytes(self, offset: int = 0) -> bytes: - # FIXME What about tagdata? - result = self._pack("Q" if self._bigtiff else "H", len(self._tags_v2)) - - entries: list[tuple[int, int, int, bytes, bytes]] = [] - - fmt = "Q" if self._bigtiff else "L" - fmt_size = 8 if self._bigtiff else 4 - offset += ( - len(result) + len(self._tags_v2) * (20 if self._bigtiff else 12) + fmt_size - ) - stripoffsets = None - - # pass 1: convert tags to binary format - # always write tags in ascending order - for tag, value in sorted(self._tags_v2.items()): - if tag == STRIPOFFSETS: - stripoffsets = len(entries) - typ = self.tagtype[tag] - logger.debug("Tag %s, Type: %s, Value: %s", tag, typ, repr(value)) - is_ifd = typ == TiffTags.LONG and isinstance(value, dict) - if is_ifd: - ifd = ImageFileDirectory_v2(self._get_ifh(), group=tag) - values = self._tags_v2[tag] - for ifd_tag, ifd_value in values.items(): - ifd[ifd_tag] = ifd_value - data = ifd.tobytes(offset) - else: - values = value if isinstance(value, tuple) else (value,) - data = self._write_dispatch[typ](self, *values) - - tagname = TiffTags.lookup(tag, self.group).name - typname = "ifd" if is_ifd else TYPES.get(typ, "unknown") - msg = f"save: {tagname} ({tag}) - type: {typname} ({typ}) - value: " - msg += f"" if len(data) >= 16 else str(values) - logger.debug(msg) - - # count is sum of lengths for string and arbitrary data - if is_ifd: - count = 1 - elif typ in [TiffTags.BYTE, TiffTags.ASCII, TiffTags.UNDEFINED]: - count = len(data) - else: - count = len(values) - # figure out if data fits into the entry - if len(data) <= fmt_size: - entries.append((tag, typ, count, data.ljust(fmt_size, b"\0"), b"")) - else: - entries.append((tag, typ, count, self._pack(fmt, offset), data)) - offset += (len(data) + 1) // 2 * 2 # pad to word - - # update strip offset data to point beyond auxiliary data - if stripoffsets is not None: - tag, typ, count, value, data = entries[stripoffsets] - if data: - size, handler = self._load_dispatch[typ] - values = [val + offset for val in handler(self, data, self.legacy_api)] - data = self._write_dispatch[typ](self, *values) - else: - value = self._pack(fmt, self._unpack(fmt, value)[0] + offset) - entries[stripoffsets] = tag, typ, count, value, data - - # pass 2: write entries to file - for tag, typ, count, value, data in entries: - logger.debug("%s %s %s %s %s", tag, typ, count, repr(value), repr(data)) - result += self._pack( - "HHQ8s" if self._bigtiff else "HHL4s", tag, typ, count, value - ) - - # -- overwrite here for multi-page -- - result += self._pack(fmt, 0) # end of entries - - # pass 3: write auxiliary data to file - for tag, typ, count, value, data in entries: - result += data - if len(data) & 1: - result += b"\0" - - return result - - def save(self, fp: IO[bytes]) -> int: - if fp.tell() == 0: # skip TIFF header on subsequent pages - fp.write(self._get_ifh()) - - offset = fp.tell() - result = self.tobytes(offset) - fp.write(result) - return offset + len(result) - - -ImageFileDirectory_v2._load_dispatch = _load_dispatch -ImageFileDirectory_v2._write_dispatch = _write_dispatch -for idx, name in TYPES.items(): - name = name.replace(" ", "_") - setattr(ImageFileDirectory_v2, f"load_{name}", _load_dispatch[idx][1]) - setattr(ImageFileDirectory_v2, f"write_{name}", _write_dispatch[idx]) -del _load_dispatch, _write_dispatch, idx, name - - -# Legacy ImageFileDirectory support. -class ImageFileDirectory_v1(ImageFileDirectory_v2): - """This class represents the **legacy** interface to a TIFF tag directory. - - Exposes a dictionary interface of the tags in the directory:: - - ifd = ImageFileDirectory_v1() - ifd[key] = 'Some Data' - ifd.tagtype[key] = TiffTags.ASCII - print(ifd[key]) - ('Some Data',) - - Also contains a dictionary of tag types as read from the tiff image file, - :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype`. - - Values are returned as a tuple. - - .. deprecated:: 3.0.0 - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - self._legacy_api = True - - tags = property(lambda self: self._tags_v1) - tagdata = property(lambda self: self._tagdata) - - # defined in ImageFileDirectory_v2 - tagtype: dict[int, int] - """Dictionary of tag types""" - - @classmethod - def from_v2(cls, original: ImageFileDirectory_v2) -> ImageFileDirectory_v1: - """Returns an - :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` - instance with the same data as is contained in the original - :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` - instance. - - :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` - - """ - - ifd = cls(prefix=original.prefix) - ifd._tagdata = original._tagdata - ifd.tagtype = original.tagtype - ifd.next = original.next # an indicator for multipage tiffs - return ifd - - def to_v2(self) -> ImageFileDirectory_v2: - """Returns an - :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` - instance with the same data as is contained in the original - :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` - instance. - - :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` - - """ - - ifd = ImageFileDirectory_v2(prefix=self.prefix) - ifd._tagdata = dict(self._tagdata) - ifd.tagtype = dict(self.tagtype) - ifd._tags_v2 = dict(self._tags_v2) - return ifd - - def __contains__(self, tag: object) -> bool: - return tag in self._tags_v1 or tag in self._tagdata - - def __len__(self) -> int: - return len(set(self._tagdata) | set(self._tags_v1)) - - def __iter__(self) -> Iterator[int]: - return iter(set(self._tagdata) | set(self._tags_v1)) - - def __setitem__(self, tag: int, value: Any) -> None: - for legacy_api in (False, True): - self._setitem(tag, value, legacy_api) - - def __getitem__(self, tag: int) -> Any: - if tag not in self._tags_v1: # unpack on the fly - data = self._tagdata[tag] - typ = self.tagtype[tag] - size, handler = self._load_dispatch[typ] - for legacy in (False, True): - self._setitem(tag, handler(self, data, legacy), legacy) - val = self._tags_v1[tag] - if not isinstance(val, (tuple, bytes)): - val = (val,) - return val - - -# undone -- switch this pointer -ImageFileDirectory = ImageFileDirectory_v1 - - -## -# Image plugin for TIFF files. - - -class TiffImageFile(ImageFile.ImageFile): - format = "TIFF" - format_description = "Adobe TIFF" - _close_exclusive_fp_after_loading = False - - def __init__( - self, - fp: StrOrBytesPath | IO[bytes], - filename: str | bytes | None = None, - ) -> None: - self.tag_v2: ImageFileDirectory_v2 - """ Image file directory (tag dictionary) """ - - self.tag: ImageFileDirectory_v1 - """ Legacy tag entries """ - - super().__init__(fp, filename) - - def _open(self) -> None: - """Open the first image in a TIFF file""" - - # Header - assert self.fp is not None - ifh = self.fp.read(8) - if ifh[2] == 43: - ifh += self.fp.read(8) - - self.tag_v2 = ImageFileDirectory_v2(ifh) - - # setup frame pointers - self.__first = self.__next = self.tag_v2.next - self.__frame = -1 - self._fp = self.fp - self._frame_pos: list[int] = [] - self._n_frames: int | None = None - - logger.debug("*** TiffImageFile._open ***") - logger.debug("- __first: %s", self.__first) - logger.debug("- ifh: %s", repr(ifh)) # Use repr to avoid str(bytes) - - # and load the first frame - self._seek(0) - - @property - def n_frames(self) -> int: - current_n_frames = self._n_frames - if current_n_frames is None: - current = self.tell() - self._seek(len(self._frame_pos)) - while self._n_frames is None: - self._seek(self.tell() + 1) - self.seek(current) - assert self._n_frames is not None - return self._n_frames - - def seek(self, frame: int) -> None: - """Select a given frame as current image""" - if not self._seek_check(frame): - return - self._seek(frame) - if self._im is not None and ( - self.im.size != self._tile_size - or self.im.mode != self.mode - or self.readonly - ): - self._im = None - - def _seek(self, frame: int) -> None: - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self.fp = self._fp - - while len(self._frame_pos) <= frame: - if not self.__next: - msg = "no more images in TIFF file" - raise EOFError(msg) - logger.debug( - "Seeking to frame %s, on frame %s, __next %s, location: %s", - frame, - self.__frame, - self.__next, - self.fp.tell(), - ) - if self.__next >= 2**63: - msg = "Unable to seek to frame" - raise ValueError(msg) - self.fp.seek(self.__next) - self._frame_pos.append(self.__next) - logger.debug("Loading tags, location: %s", self.fp.tell()) - self.tag_v2.load(self.fp) - if self.tag_v2.next in self._frame_pos: - # This IFD has already been processed - # Declare this to be the end of the image - self.__next = 0 - else: - self.__next = self.tag_v2.next - if self.__next == 0: - self._n_frames = frame + 1 - if len(self._frame_pos) == 1: - self.is_animated = self.__next != 0 - self.__frame += 1 - self.fp.seek(self._frame_pos[frame]) - self.tag_v2.load(self.fp) - if XMP in self.tag_v2: - xmp = self.tag_v2[XMP] - if isinstance(xmp, tuple) and len(xmp) == 1: - xmp = xmp[0] - self.info["xmp"] = xmp - elif "xmp" in self.info: - del self.info["xmp"] - self._reload_exif() - # fill the legacy tag/ifd entries - self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2) - self.__frame = frame - self._setup() - - def tell(self) -> int: - """Return the current frame number""" - return self.__frame - - def get_photoshop_blocks(self) -> dict[int, dict[str, bytes]]: - """ - Returns a dictionary of Photoshop "Image Resource Blocks". - The keys are the image resource ID. For more information, see - https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037727 - - :returns: Photoshop "Image Resource Blocks" in a dictionary. - """ - blocks = {} - val = self.tag_v2.get(ExifTags.Base.ImageResources) - if val: - while val.startswith(b"8BIM") and len(val) >= 12: - id = i16(val[4:6]) - n = math.ceil((val[6] + 1) / 2) * 2 - try: - size = i32(val[6 + n : 10 + n]) - except struct.error: - break - data = val[10 + n : 10 + n + size] - blocks[id] = {"data": data} - - val = val[math.ceil((10 + n + size) / 2) * 2 :] - return blocks - - def load(self) -> Image.core.PixelAccess | None: - if self.tile and self.use_load_libtiff: - return self._load_libtiff() - return super().load() - - def load_prepare(self) -> None: - if self._im is None: - Image._decompression_bomb_check(self._tile_size) - self.im = Image.core.new(self.mode, self._tile_size) - ImageFile.ImageFile.load_prepare(self) - - def load_end(self) -> None: - # allow closing if we're on the first frame, there's no next - # This is the ImageFile.load path only, libtiff specific below. - if not self.is_animated: - self._close_exclusive_fp_after_loading = True - - # load IFD data from fp before it is closed - exif = self.getexif() - for key in TiffTags.TAGS_V2_GROUPS: - if key not in exif: - continue - exif.get_ifd(key) - - ImageOps.exif_transpose(self, in_place=True) - if ExifTags.Base.Orientation in self.tag_v2: - del self.tag_v2[ExifTags.Base.Orientation] - - def _load_libtiff(self) -> Image.core.PixelAccess | None: - """Overload method triggered when we detect a compressed tiff - Calls out to libtiff""" - - Image.Image.load(self) - - self.load_prepare() - - if not len(self.tile) == 1: - msg = "Not exactly one tile" - raise OSError(msg) - - # (self._compression, (extents tuple), - # 0, (rawmode, self._compression, fp)) - extents = self.tile[0][1] - args = self.tile[0][3] - - # To be nice on memory footprint, if there's a - # file descriptor, use that instead of reading - # into a string in python. - assert self.fp is not None - try: - fp = hasattr(self.fp, "fileno") and self.fp.fileno() - # flush the file descriptor, prevents error on pypy 2.4+ - # should also eliminate the need for fp.tell - # in _seek - if hasattr(self.fp, "flush"): - self.fp.flush() - except OSError: - # io.BytesIO have a fileno, but returns an OSError if - # it doesn't use a file descriptor. - fp = False - - if fp: - assert isinstance(args, tuple) - args_list = list(args) - args_list[2] = fp - args = tuple(args_list) - - decoder = Image._getdecoder(self.mode, "libtiff", args, self.decoderconfig) - try: - decoder.setimage(self.im, extents) - except ValueError as e: - msg = "Couldn't set the image" - raise OSError(msg) from e - - close_self_fp = self._exclusive_fp and not self.is_animated - if hasattr(self.fp, "getvalue"): - # We've got a stringio like thing passed in. Yay for all in memory. - # The decoder needs the entire file in one shot, so there's not - # a lot we can do here other than give it the entire file. - # unless we could do something like get the address of the - # underlying string for stringio. - # - # Rearranging for supporting byteio items, since they have a fileno - # that returns an OSError if there's no underlying fp. Easier to - # deal with here by reordering. - logger.debug("have getvalue. just sending in a string from getvalue") - n, err = decoder.decode(self.fp.getvalue()) - elif fp: - # we've got a actual file on disk, pass in the fp. - logger.debug("have fileno, calling fileno version of the decoder.") - if not close_self_fp: - self.fp.seek(0) - # Save and restore the file position, because libtiff will move it - # outside of the Python runtime, and that will confuse - # io.BufferedReader and possible others. - # NOTE: This must use os.lseek(), and not fp.tell()/fp.seek(), - # because the buffer read head already may not equal the actual - # file position, and fp.seek() may just adjust it's internal - # pointer and not actually seek the OS file handle. - pos = os.lseek(fp, 0, os.SEEK_CUR) - # 4 bytes, otherwise the trace might error out - n, err = decoder.decode(b"fpfp") - os.lseek(fp, pos, os.SEEK_SET) - else: - # we have something else. - logger.debug("don't have fileno or getvalue. just reading") - self.fp.seek(0) - # UNDONE -- so much for that buffer size thing. - n, err = decoder.decode(self.fp.read()) - - self.tile = [] - self.readonly = 0 - - self.load_end() - - if close_self_fp: - self.fp.close() - self.fp = None # might be shared - - if err < 0: - msg = f"decoder error {err}" - raise OSError(msg) - - return Image.Image.load(self) - - def _setup(self) -> None: - """Setup this image object based on current tags""" - - if 0xBC01 in self.tag_v2: - msg = "Windows Media Photo files not yet supported" - raise OSError(msg) - - # extract relevant tags - self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)] - self._planar_configuration = self.tag_v2.get(PLANAR_CONFIGURATION, 1) - - # photometric is a required tag, but not everyone is reading - # the specification - photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0) - - # old style jpeg compression images most certainly are YCbCr - if self._compression == "tiff_jpeg": - photo = 6 - - fillorder = self.tag_v2.get(FILLORDER, 1) - - logger.debug("*** Summary ***") - logger.debug("- compression: %s", self._compression) - logger.debug("- photometric_interpretation: %s", photo) - logger.debug("- planar_configuration: %s", self._planar_configuration) - logger.debug("- fill_order: %s", fillorder) - logger.debug("- YCbCr subsampling: %s", self.tag_v2.get(YCBCRSUBSAMPLING)) - - # size - try: - xsize = self.tag_v2[IMAGEWIDTH] - ysize = self.tag_v2[IMAGELENGTH] - except KeyError as e: - msg = "Missing dimensions" - raise TypeError(msg) from e - if not isinstance(xsize, int) or not isinstance(ysize, int): - msg = "Invalid dimensions" - raise ValueError(msg) - self._tile_size = xsize, ysize - orientation = self.tag_v2.get(ExifTags.Base.Orientation) - if orientation in (5, 6, 7, 8): - self._size = ysize, xsize - else: - self._size = xsize, ysize - - logger.debug("- size: %s", self.size) - - sample_format = self.tag_v2.get(SAMPLEFORMAT, (1,)) - if len(sample_format) > 1 and max(sample_format) == min(sample_format): - # SAMPLEFORMAT is properly per band, so an RGB image will - # be (1,1,1). But, we don't support per band pixel types, - # and anything more than one band is a uint8. So, just - # take the first element. Revisit this if adding support - # for more exotic images. - sample_format = (sample_format[0],) - - bps_tuple = self.tag_v2.get(BITSPERSAMPLE, (1,)) - extra_tuple = self.tag_v2.get(EXTRASAMPLES, ()) - samples_per_pixel = self.tag_v2.get( - SAMPLESPERPIXEL, - 3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1, - ) - if photo in (2, 6, 8): # RGB, YCbCr, LAB - bps_count = 3 - elif photo == 5: # CMYK - bps_count = 4 - else: - bps_count = 1 - if self._planar_configuration == 2 and extra_tuple and max(extra_tuple) == 0: - # If components are stored separately, - # then unspecified extra components at the end can be ignored - bps_tuple = bps_tuple[: -len(extra_tuple)] - samples_per_pixel -= len(extra_tuple) - extra_tuple = () - bps_count += len(extra_tuple) - bps_actual_count = len(bps_tuple) - - if samples_per_pixel > MAX_SAMPLESPERPIXEL: - # DOS check, samples_per_pixel can be a Long, and we extend the tuple below - logger.error( - "More samples per pixel than can be decoded: %s", samples_per_pixel - ) - msg = "Invalid value for samples per pixel" - raise SyntaxError(msg) - - if samples_per_pixel < bps_actual_count: - # If a file has more values in bps_tuple than expected, - # remove the excess. - bps_tuple = bps_tuple[:samples_per_pixel] - elif samples_per_pixel > bps_actual_count and bps_actual_count == 1: - # If a file has only one value in bps_tuple, when it should have more, - # presume it is the same number of bits for all of the samples. - bps_tuple = bps_tuple * samples_per_pixel - - if len(bps_tuple) != samples_per_pixel: - msg = "unknown data organization" - raise SyntaxError(msg) - - # mode: check photometric interpretation and bits per pixel - key = ( - self.tag_v2.prefix, - photo, - sample_format, - fillorder, - bps_tuple, - extra_tuple, - ) - logger.debug("format key: %s", key) - try: - self._mode, rawmode = OPEN_INFO[key] - except KeyError as e: - logger.debug("- unsupported format") - msg = "unknown pixel mode" - raise SyntaxError(msg) from e - - logger.debug("- raw mode: %s", rawmode) - logger.debug("- pil mode: %s", self.mode) - - self.info["compression"] = self._compression - - xres = self.tag_v2.get(X_RESOLUTION, 1) - yres = self.tag_v2.get(Y_RESOLUTION, 1) - - if xres and yres: - resunit = self.tag_v2.get(RESOLUTION_UNIT) - if resunit == 2: # dots per inch - self.info["dpi"] = (xres, yres) - elif resunit == 3: # dots per centimeter. convert to dpi - self.info["dpi"] = (xres * 2.54, yres * 2.54) - elif resunit is None: # used to default to 1, but now 2) - self.info["dpi"] = (xres, yres) - # For backward compatibility, - # we also preserve the old behavior - self.info["resolution"] = xres, yres - else: # No absolute unit of measurement - self.info["resolution"] = xres, yres - - # build tile descriptors - x = y = layer = 0 - self.tile = [] - self.use_load_libtiff = READ_LIBTIFF or self._compression != "raw" - if self.use_load_libtiff: - # Decoder expects entire file as one tile. - # There's a buffer size limit in load (64k) - # so large g4 images will fail if we use that - # function. - # - # Setup the one tile for the whole image, then - # use the _load_libtiff function. - - # libtiff handles the fillmode for us, so 1;IR should - # actually be 1;I. Including the R double reverses the - # bits, so stripes of the image are reversed. See - # https://github.com/python-pillow/Pillow/issues/279 - if fillorder == 2: - # Replace fillorder with fillorder=1 - key = key[:3] + (1,) + key[4:] - logger.debug("format key: %s", key) - # this should always work, since all the - # fillorder==2 modes have a corresponding - # fillorder=1 mode - self._mode, rawmode = OPEN_INFO[key] - # YCbCr images with new jpeg compression with pixels in one plane - # unpacked straight into RGB values - if ( - photo == 6 - and self._compression == "jpeg" - and self._planar_configuration == 1 - ): - rawmode = "RGB" - # libtiff always returns the bytes in native order. - # we're expecting image byte order. So, if the rawmode - # contains I;16, we need to convert from native to image - # byte order. - elif rawmode == "I;16": - rawmode = "I;16N" - elif rawmode.endswith((";16B", ";16L")): - rawmode = rawmode[:-1] + "N" - - # Offset in the tile tuple is 0, we go from 0,0 to - # w,h, and we only do this once -- eds - a = (rawmode, self._compression, False, self.tag_v2.offset) - self.tile.append(ImageFile._Tile("libtiff", (0, 0, xsize, ysize), 0, a)) - - elif STRIPOFFSETS in self.tag_v2 or TILEOFFSETS in self.tag_v2: - # striped image - if STRIPOFFSETS in self.tag_v2: - offsets = self.tag_v2[STRIPOFFSETS] - h = self.tag_v2.get(ROWSPERSTRIP, ysize) - w = xsize - else: - # tiled image - offsets = self.tag_v2[TILEOFFSETS] - tilewidth = self.tag_v2.get(TILEWIDTH) - h = self.tag_v2.get(TILELENGTH) - if not isinstance(tilewidth, int) or not isinstance(h, int): - msg = "Invalid tile dimensions" - raise ValueError(msg) - w = tilewidth - - if w == xsize and h == ysize and self._planar_configuration != 2: - # Every tile covers the image. Only use the last offset - offsets = offsets[-1:] - - for offset in offsets: - if x + w > xsize: - stride = w * sum(bps_tuple) / 8 # bytes per line - else: - stride = 0 - - tile_rawmode = rawmode - if self._planar_configuration == 2: - # each band on it's own layer - tile_rawmode = rawmode[layer] - # adjust stride width accordingly - stride /= bps_count - - args = (tile_rawmode, int(stride), 1) - self.tile.append( - ImageFile._Tile( - self._compression, - (x, y, min(x + w, xsize), min(y + h, ysize)), - offset, - args, - ) - ) - x += w - if x >= xsize: - x, y = 0, y + h - if y >= ysize: - y = 0 - layer += 1 - else: - logger.debug("- unsupported data organization") - msg = "unknown data organization" - raise SyntaxError(msg) - - # Fix up info. - if ICCPROFILE in self.tag_v2: - self.info["icc_profile"] = self.tag_v2[ICCPROFILE] - - # fixup palette descriptor - - if self.mode in ["P", "PA"]: - palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] - self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) - - -# -# -------------------------------------------------------------------- -# Write TIFF files - -# little endian is default except for image modes with -# explicit big endian byte-order - -SAVE_INFO = { - # mode => rawmode, byteorder, photometrics, - # sampleformat, bitspersample, extra - "1": ("1", II, 1, 1, (1,), None), - "L": ("L", II, 1, 1, (8,), None), - "LA": ("LA", II, 1, 1, (8, 8), 2), - "P": ("P", II, 3, 1, (8,), None), - "PA": ("PA", II, 3, 1, (8, 8), 2), - "I": ("I;32S", II, 1, 2, (32,), None), - "I;16": ("I;16", II, 1, 1, (16,), None), - "I;16L": ("I;16L", II, 1, 1, (16,), None), - "F": ("F;32F", II, 1, 3, (32,), None), - "RGB": ("RGB", II, 2, 1, (8, 8, 8), None), - "RGBX": ("RGBX", II, 2, 1, (8, 8, 8, 8), 0), - "RGBA": ("RGBA", II, 2, 1, (8, 8, 8, 8), 2), - "CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None), - "YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None), - "LAB": ("LAB", II, 8, 1, (8, 8, 8), None), - "I;16B": ("I;16B", MM, 1, 1, (16,), None), -} - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - try: - rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] - except KeyError as e: - msg = f"cannot write mode {im.mode} as TIFF" - raise OSError(msg) from e - - encoderinfo = im.encoderinfo - encoderconfig = im.encoderconfig - - ifd = ImageFileDirectory_v2(prefix=prefix) - if encoderinfo.get("big_tiff"): - ifd._bigtiff = True - - try: - compression = encoderinfo["compression"] - except KeyError: - compression = im.info.get("compression") - if isinstance(compression, int): - # compression value may be from BMP. Ignore it - compression = None - if compression is None: - compression = "raw" - elif compression == "tiff_jpeg": - # OJPEG is obsolete, so use new-style JPEG compression instead - compression = "jpeg" - elif compression == "tiff_deflate": - compression = "tiff_adobe_deflate" - - libtiff = WRITE_LIBTIFF or compression != "raw" - - # required for color libtiff images - ifd[PLANAR_CONFIGURATION] = 1 - - ifd[IMAGEWIDTH] = im.size[0] - ifd[IMAGELENGTH] = im.size[1] - - # write any arbitrary tags passed in as an ImageFileDirectory - if "tiffinfo" in encoderinfo: - info = encoderinfo["tiffinfo"] - elif "exif" in encoderinfo: - info = encoderinfo["exif"] - if isinstance(info, bytes): - exif = Image.Exif() - exif.load(info) - info = exif - else: - info = {} - logger.debug("Tiffinfo Keys: %s", list(info)) - if isinstance(info, ImageFileDirectory_v1): - info = info.to_v2() - for key in info: - if isinstance(info, Image.Exif) and key in TiffTags.TAGS_V2_GROUPS: - ifd[key] = info.get_ifd(key) - else: - ifd[key] = info.get(key) - try: - ifd.tagtype[key] = info.tagtype[key] - except Exception: - pass # might not be an IFD. Might not have populated type - - legacy_ifd = {} - if hasattr(im, "tag"): - legacy_ifd = im.tag.to_v2() - - supplied_tags = {**legacy_ifd, **getattr(im, "tag_v2", {})} - if supplied_tags.get(PLANAR_CONFIGURATION) == 2 and EXTRASAMPLES in supplied_tags: - # If the image used separate component planes, - # then EXTRASAMPLES should be ignored when saving contiguously - if SAMPLESPERPIXEL in supplied_tags: - supplied_tags[SAMPLESPERPIXEL] -= len(supplied_tags[EXTRASAMPLES]) - del supplied_tags[EXTRASAMPLES] - for tag in ( - # IFD offset that may not be correct in the saved image - EXIFIFD, - # Determined by the image format and should not be copied from legacy_ifd. - SAMPLEFORMAT, - ): - if tag in supplied_tags: - del supplied_tags[tag] - - # additions written by Greg Couch, gregc@cgl.ucsf.edu - # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com - if hasattr(im, "tag_v2"): - # preserve tags from original TIFF image file - for key in ( - RESOLUTION_UNIT, - X_RESOLUTION, - Y_RESOLUTION, - IPTC_NAA_CHUNK, - PHOTOSHOP_CHUNK, - XMP, - ): - if key in im.tag_v2: - if key == IPTC_NAA_CHUNK and im.tag_v2.tagtype[key] not in ( - TiffTags.BYTE, - TiffTags.UNDEFINED, - ): - del supplied_tags[key] - else: - ifd[key] = im.tag_v2[key] - ifd.tagtype[key] = im.tag_v2.tagtype[key] - - # preserve ICC profile (should also work when saving other formats - # which support profiles as TIFF) -- 2008-06-06 Florian Hoech - icc = encoderinfo.get("icc_profile", im.info.get("icc_profile")) - if icc: - ifd[ICCPROFILE] = icc - - for key, name in [ - (IMAGEDESCRIPTION, "description"), - (X_RESOLUTION, "resolution"), - (Y_RESOLUTION, "resolution"), - (X_RESOLUTION, "x_resolution"), - (Y_RESOLUTION, "y_resolution"), - (RESOLUTION_UNIT, "resolution_unit"), - (SOFTWARE, "software"), - (DATE_TIME, "date_time"), - (ARTIST, "artist"), - (COPYRIGHT, "copyright"), - ]: - if name in encoderinfo: - ifd[key] = encoderinfo[name] - - dpi = encoderinfo.get("dpi") - if dpi: - ifd[RESOLUTION_UNIT] = 2 - ifd[X_RESOLUTION] = dpi[0] - ifd[Y_RESOLUTION] = dpi[1] - - if bits != (1,): - ifd[BITSPERSAMPLE] = bits - if len(bits) != 1: - ifd[SAMPLESPERPIXEL] = len(bits) - if extra is not None: - ifd[EXTRASAMPLES] = extra - if format != 1: - ifd[SAMPLEFORMAT] = format - - if PHOTOMETRIC_INTERPRETATION not in ifd: - ifd[PHOTOMETRIC_INTERPRETATION] = photo - elif im.mode in ("1", "L") and ifd[PHOTOMETRIC_INTERPRETATION] == 0: - if im.mode == "1": - inverted_im = im.copy() - px = inverted_im.load() - if px is not None: - for y in range(inverted_im.height): - for x in range(inverted_im.width): - px[x, y] = 0 if px[x, y] == 255 else 255 - im = inverted_im - else: - im = ImageOps.invert(im) - - if im.mode in ["P", "PA"]: - lut = im.im.getpalette("RGB", "RGB;L") - colormap = [] - colors = len(lut) // 3 - for i in range(3): - colormap += [v * 256 for v in lut[colors * i : colors * (i + 1)]] - colormap += [0] * (256 - colors) - ifd[COLORMAP] = colormap - # data orientation - w, h = ifd[IMAGEWIDTH], ifd[IMAGELENGTH] - stride = len(bits) * ((w * bits[0] + 7) // 8) - if ROWSPERSTRIP not in ifd: - # aim for given strip size (64 KB by default) when using libtiff writer - if libtiff: - im_strip_size = encoderinfo.get("strip_size", STRIP_SIZE) - rows_per_strip = 1 if stride == 0 else min(im_strip_size // stride, h) - # JPEG encoder expects multiple of 8 rows - if compression == "jpeg": - rows_per_strip = min(((rows_per_strip + 7) // 8) * 8, h) - else: - rows_per_strip = h - if rows_per_strip == 0: - rows_per_strip = 1 - ifd[ROWSPERSTRIP] = rows_per_strip - strip_byte_counts = 1 if stride == 0 else stride * ifd[ROWSPERSTRIP] - strips_per_image = (h + ifd[ROWSPERSTRIP] - 1) // ifd[ROWSPERSTRIP] - if strip_byte_counts >= 2**16: - ifd.tagtype[STRIPBYTECOUNTS] = TiffTags.LONG - ifd[STRIPBYTECOUNTS] = (strip_byte_counts,) * (strips_per_image - 1) + ( - stride * h - strip_byte_counts * (strips_per_image - 1), - ) - ifd[STRIPOFFSETS] = tuple( - range(0, strip_byte_counts * strips_per_image, strip_byte_counts) - ) # this is adjusted by IFD writer - # no compression by default: - ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) - - if im.mode == "YCbCr": - for tag, default_value in { - YCBCRSUBSAMPLING: (1, 1), - REFERENCEBLACKWHITE: (0, 255, 128, 255, 128, 255), - }.items(): - ifd.setdefault(tag, default_value) - - blocklist = [TILEWIDTH, TILELENGTH, TILEOFFSETS, TILEBYTECOUNTS] - if libtiff: - if "quality" in encoderinfo: - quality = encoderinfo["quality"] - if not isinstance(quality, int) or quality < 0 or quality > 100: - msg = "Invalid quality setting" - raise ValueError(msg) - if compression != "jpeg": - msg = "quality setting only supported for 'jpeg' compression" - raise ValueError(msg) - ifd[JPEGQUALITY] = quality - - logger.debug("Saving using libtiff encoder") - logger.debug("Items: %s", sorted(ifd.items())) - _fp = 0 - if hasattr(fp, "fileno"): - try: - fp.seek(0) - _fp = fp.fileno() - except io.UnsupportedOperation: - pass - - # optional types for non core tags - types = {} - # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library - # based on the data in the strip. - # OSUBFILETYPE is deprecated. - # The other tags expect arrays with a certain length (fixed or depending on - # BITSPERSAMPLE, etc), passing arrays with a different length will result in - # segfaults. Block these tags until we add extra validation. - # SUBIFD may also cause a segfault. - blocklist += [ - OSUBFILETYPE, - REFERENCEBLACKWHITE, - STRIPBYTECOUNTS, - STRIPOFFSETS, - TRANSFERFUNCTION, - SUBIFD, - ] - - # bits per sample is a single short in the tiff directory, not a list. - atts: dict[int, Any] = {BITSPERSAMPLE: bits[0]} - # Merge the ones that we have with (optional) more bits from - # the original file, e.g x,y resolution so that we can - # save(load('')) == original file. - for tag, value in itertools.chain(ifd.items(), supplied_tags.items()): - # Libtiff can only process certain core items without adding - # them to the custom dictionary. - # Custom items are supported for int, float, unicode, string and byte - # values. Other types and tuples require a tagtype. - if tag not in TiffTags.LIBTIFF_CORE: - if tag in TiffTags.TAGS_V2_GROUPS: - types[tag] = TiffTags.LONG8 - elif tag in ifd.tagtype: - types[tag] = ifd.tagtype[tag] - elif isinstance(value, (int, float, str, bytes)) or ( - isinstance(value, tuple) - and all(isinstance(v, (int, float, IFDRational)) for v in value) - ): - type = TiffTags.lookup(tag).type - if type: - types[tag] = type - if tag not in atts and tag not in blocklist: - if isinstance(value, str): - atts[tag] = value.encode("ascii", "replace") + b"\0" - elif isinstance(value, IFDRational): - atts[tag] = float(value) - else: - atts[tag] = value - - if SAMPLEFORMAT in atts and len(atts[SAMPLEFORMAT]) == 1: - atts[SAMPLEFORMAT] = atts[SAMPLEFORMAT][0] - - logger.debug("Converted items: %s", sorted(atts.items())) - - # libtiff always expects the bytes in native order. - # we're storing image byte order. So, if the rawmode - # contains I;16, we need to convert from native to image - # byte order. - if im.mode in ("I;16", "I;16B", "I;16L"): - rawmode = "I;16N" - - # Pass tags as sorted list so that the tags are set in a fixed order. - # This is required by libtiff for some tags. For example, the JPEGQUALITY - # pseudo tag requires that the COMPRESS tag was already set. - tags = list(atts.items()) - tags.sort() - a = (rawmode, compression, _fp, filename, tags, types) - encoder = Image._getencoder(im.mode, "libtiff", a, encoderconfig) - encoder.setimage(im.im, (0, 0) + im.size) - while True: - errcode, data = encoder.encode(ImageFile.MAXBLOCK)[1:] - if not _fp: - fp.write(data) - if errcode: - break - if errcode < 0: - msg = f"encoder error {errcode} when writing image file" - raise OSError(msg) - - else: - for tag in blocklist: - del ifd[tag] - offset = ifd.save(fp) - - ImageFile._save( - im, - fp, - [ImageFile._Tile("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))], - ) - - # -- helper for multi-page save -- - if "_debug_multipage" in encoderinfo: - # just to access o32 and o16 (using correct byte order) - setattr(im, "_debug_multipage", ifd) - - -class AppendingTiffWriter(io.BytesIO): - fieldSizes = [ - 0, # None - 1, # byte - 1, # ascii - 2, # short - 4, # long - 8, # rational - 1, # sbyte - 1, # undefined - 2, # sshort - 4, # slong - 8, # srational - 4, # float - 8, # double - 4, # ifd - 2, # unicode - 4, # complex - 8, # long8 - ] - - Tags = { - 273, # StripOffsets - 288, # FreeOffsets - 324, # TileOffsets - 519, # JPEGQTables - 520, # JPEGDCTables - 521, # JPEGACTables - } - - def __init__(self, fn: StrOrBytesPath | IO[bytes], new: bool = False) -> None: - self.f: IO[bytes] - if is_path(fn): - self.name = fn - self.close_fp = True - try: - self.f = open(fn, "w+b" if new else "r+b") - except OSError: - self.f = open(fn, "w+b") - else: - self.f = cast(IO[bytes], fn) - self.close_fp = False - self.beginning = self.f.tell() - self.setup() - - def setup(self) -> None: - # Reset everything. - self.f.seek(self.beginning, os.SEEK_SET) - - self.whereToWriteNewIFDOffset: int | None = None - self.offsetOfNewPage = 0 - - self.IIMM = iimm = self.f.read(4) - self._bigtiff = b"\x2b" in iimm - if not iimm: - # empty file - first page - self.isFirst = True - return - - self.isFirst = False - if iimm not in PREFIXES: - msg = "Invalid TIFF file header" - raise RuntimeError(msg) - - self.setEndian("<" if iimm.startswith(II) else ">") - - if self._bigtiff: - self.f.seek(4, os.SEEK_CUR) - self.skipIFDs() - self.goToEnd() - - def finalize(self) -> None: - if self.isFirst: - return - - # fix offsets - self.f.seek(self.offsetOfNewPage) - - iimm = self.f.read(4) - if not iimm: - # Make it easy to finish a frame without committing to a new one. - return - - if iimm != self.IIMM: - msg = "IIMM of new page doesn't match IIMM of first page" - raise RuntimeError(msg) - - if self._bigtiff: - self.f.seek(4, os.SEEK_CUR) - ifd_offset = self._read(8 if self._bigtiff else 4) - ifd_offset += self.offsetOfNewPage - assert self.whereToWriteNewIFDOffset is not None - self.f.seek(self.whereToWriteNewIFDOffset) - self._write(ifd_offset, 8 if self._bigtiff else 4) - self.f.seek(ifd_offset) - self.fixIFD() - - def newFrame(self) -> None: - # Call this to finish a frame. - self.finalize() - self.setup() - - def __enter__(self) -> AppendingTiffWriter: - return self - - def __exit__(self, *args: object) -> None: - if self.close_fp: - self.close() - - def tell(self) -> int: - return self.f.tell() - self.offsetOfNewPage - - def seek(self, offset: int, whence: int = io.SEEK_SET) -> int: - """ - :param offset: Distance to seek. - :param whence: Whether the distance is relative to the start, - end or current position. - :returns: The resulting position, relative to the start. - """ - if whence == os.SEEK_SET: - offset += self.offsetOfNewPage - - self.f.seek(offset, whence) - return self.tell() - - def goToEnd(self) -> None: - self.f.seek(0, os.SEEK_END) - pos = self.f.tell() - - # pad to 16 byte boundary - pad_bytes = 16 - pos % 16 - if 0 < pad_bytes < 16: - self.f.write(bytes(pad_bytes)) - self.offsetOfNewPage = self.f.tell() - - def setEndian(self, endian: str) -> None: - self.endian = endian - self.longFmt = f"{self.endian}L" - self.shortFmt = f"{self.endian}H" - self.tagFormat = f"{self.endian}HH" + ("Q" if self._bigtiff else "L") - - def skipIFDs(self) -> None: - while True: - ifd_offset = self._read(8 if self._bigtiff else 4) - if ifd_offset == 0: - self.whereToWriteNewIFDOffset = self.f.tell() - ( - 8 if self._bigtiff else 4 - ) - break - - self.f.seek(ifd_offset) - num_tags = self._read(8 if self._bigtiff else 2) - self.f.seek(num_tags * (20 if self._bigtiff else 12), os.SEEK_CUR) - - def write(self, data: Buffer, /) -> int: - return self.f.write(data) - - def _fmt(self, field_size: int) -> str: - try: - return {2: "H", 4: "L", 8: "Q"}[field_size] - except KeyError: - msg = "offset is not supported" - raise RuntimeError(msg) - - def _read(self, field_size: int) -> int: - (value,) = struct.unpack( - self.endian + self._fmt(field_size), self.f.read(field_size) - ) - return value - - def readShort(self) -> int: - return self._read(2) - - def readLong(self) -> int: - return self._read(4) - - @staticmethod - def _verify_bytes_written(bytes_written: int | None, expected: int) -> None: - if bytes_written is not None and bytes_written != expected: - msg = f"wrote only {bytes_written} bytes but wanted {expected}" - raise RuntimeError(msg) - - def _rewriteLast( - self, value: int, field_size: int, new_field_size: int = 0 - ) -> None: - self.f.seek(-field_size, os.SEEK_CUR) - if not new_field_size: - new_field_size = field_size - bytes_written = self.f.write( - struct.pack(self.endian + self._fmt(new_field_size), value) - ) - self._verify_bytes_written(bytes_written, new_field_size) - - def rewriteLastShortToLong(self, value: int) -> None: - self._rewriteLast(value, 2, 4) - - def rewriteLastShort(self, value: int) -> None: - return self._rewriteLast(value, 2) - - def rewriteLastLong(self, value: int) -> None: - return self._rewriteLast(value, 4) - - def _write(self, value: int, field_size: int) -> None: - bytes_written = self.f.write( - struct.pack(self.endian + self._fmt(field_size), value) - ) - self._verify_bytes_written(bytes_written, field_size) - - def writeShort(self, value: int) -> None: - self._write(value, 2) - - def writeLong(self, value: int) -> None: - self._write(value, 4) - - def close(self) -> None: - self.finalize() - if self.close_fp: - self.f.close() - - def fixIFD(self) -> None: - num_tags = self._read(8 if self._bigtiff else 2) - - for i in range(num_tags): - tag, field_type, count = struct.unpack( - self.tagFormat, self.f.read(12 if self._bigtiff else 8) - ) - - field_size = self.fieldSizes[field_type] - total_size = field_size * count - fmt_size = 8 if self._bigtiff else 4 - is_local = total_size <= fmt_size - if not is_local: - offset = self._read(fmt_size) + self.offsetOfNewPage - self._rewriteLast(offset, fmt_size) - - if tag in self.Tags: - cur_pos = self.f.tell() - - logger.debug( - "fixIFD: %s (%d) - type: %s (%d) - type size: %d - count: %d", - TiffTags.lookup(tag).name, - tag, - TYPES.get(field_type, "unknown"), - field_type, - field_size, - count, - ) - - if is_local: - self._fixOffsets(count, field_size) - self.f.seek(cur_pos + fmt_size) - else: - self.f.seek(offset) - self._fixOffsets(count, field_size) - self.f.seek(cur_pos) - - elif is_local: - # skip the locally stored value that is not an offset - self.f.seek(fmt_size, os.SEEK_CUR) - - def _fixOffsets(self, count: int, field_size: int) -> None: - for i in range(count): - offset = self._read(field_size) - offset += self.offsetOfNewPage - - new_field_size = 0 - if self._bigtiff and field_size in (2, 4) and offset >= 2**32: - # offset is now too large - we must convert long to long8 - new_field_size = 8 - elif field_size == 2 and offset >= 2**16: - # offset is now too large - we must convert short to long - new_field_size = 4 - if new_field_size: - if count != 1: - msg = "not implemented" - raise RuntimeError(msg) # XXX TODO - - # simple case - the offset is just one and therefore it is - # local (not referenced with another offset) - self._rewriteLast(offset, field_size, new_field_size) - # Move back past the new offset, past 'count', and before 'field_type' - rewind = -new_field_size - 4 - 2 - self.f.seek(rewind, os.SEEK_CUR) - self.writeShort(new_field_size) # rewrite the type - self.f.seek(2 - rewind, os.SEEK_CUR) - else: - self._rewriteLast(offset, field_size) - - def fixOffsets( - self, count: int, isShort: bool = False, isLong: bool = False - ) -> None: - if isShort: - field_size = 2 - elif isLong: - field_size = 4 - else: - field_size = 0 - return self._fixOffsets(count, field_size) - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - append_images = list(im.encoderinfo.get("append_images", [])) - if not hasattr(im, "n_frames") and not append_images: - return _save(im, fp, filename) - - cur_idx = im.tell() - try: - with AppendingTiffWriter(fp) as tf: - for ims in [im] + append_images: - encoderinfo = ims._attach_default_encoderinfo(im) - if not hasattr(ims, "encoderconfig"): - ims.encoderconfig = () - nfr = getattr(ims, "n_frames", 1) - - for idx in range(nfr): - ims.seek(idx) - ims.load() - _save(ims, tf, filename) - tf.newFrame() - ims.encoderinfo = encoderinfo - finally: - im.seek(cur_idx) - - -# -# -------------------------------------------------------------------- -# Register - -Image.register_open(TiffImageFile.format, TiffImageFile, _accept) -Image.register_save(TiffImageFile.format, _save) -Image.register_save_all(TiffImageFile.format, _save_all) - -Image.register_extensions(TiffImageFile.format, [".tif", ".tiff"]) - -Image.register_mime(TiffImageFile.format, "image/tiff") diff --git a/.venv/lib/python3.12/site-packages/PIL/TiffTags.py b/.venv/lib/python3.12/site-packages/PIL/TiffTags.py deleted file mode 100644 index 613a3b7d..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/TiffTags.py +++ /dev/null @@ -1,566 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# TIFF tags -# -# This module provides clear-text names for various well-known -# TIFF tags. the TIFF codec works just fine without it. -# -# Copyright (c) Secret Labs AB 1999. -# -# See the README file for information on usage and redistribution. -# - -## -# This module provides constants and clear-text names for various -# well-known TIFF tags. -## -from __future__ import annotations - -from typing import NamedTuple - - -class _TagInfo(NamedTuple): - value: int | None - name: str - type: int | None - length: int | None - enum: dict[str, int] - - -class TagInfo(_TagInfo): - __slots__: list[str] = [] - - def __new__( - cls, - value: int | None = None, - name: str = "unknown", - type: int | None = None, - length: int | None = None, - enum: dict[str, int] | None = None, - ) -> TagInfo: - return super().__new__(cls, value, name, type, length, enum or {}) - - def cvt_enum(self, value: str) -> int | str: - # Using get will call hash(value), which can be expensive - # for some types (e.g. Fraction). Since self.enum is rarely - # used, it's usually better to test it first. - return self.enum.get(value, value) if self.enum else value - - -def lookup(tag: int, group: int | None = None) -> TagInfo: - """ - :param tag: Integer tag number - :param group: Which :py:data:`~PIL.TiffTags.TAGS_V2_GROUPS` to look in - - .. versionadded:: 8.3.0 - - :returns: Taginfo namedtuple, From the ``TAGS_V2`` info if possible, - otherwise just populating the value and name from ``TAGS``. - If the tag is not recognized, "unknown" is returned for the name - - """ - - if group is not None: - info = TAGS_V2_GROUPS[group].get(tag) if group in TAGS_V2_GROUPS else None - else: - info = TAGS_V2.get(tag) - return info or TagInfo(tag, TAGS.get(tag, "unknown")) - - -## -# Map tag numbers to tag info. -# -# id: (Name, Type, Length[, enum_values]) -# -# The length here differs from the length in the tiff spec. For -# numbers, the tiff spec is for the number of fields returned. We -# agree here. For string-like types, the tiff spec uses the length of -# field in bytes. In Pillow, we are using the number of expected -# fields, in general 1 for string-like types. - - -BYTE = 1 -ASCII = 2 -SHORT = 3 -LONG = 4 -RATIONAL = 5 -SIGNED_BYTE = 6 -UNDEFINED = 7 -SIGNED_SHORT = 8 -SIGNED_LONG = 9 -SIGNED_RATIONAL = 10 -FLOAT = 11 -DOUBLE = 12 -IFD = 13 -LONG8 = 16 - -_tags_v2: dict[int, tuple[str, int, int] | tuple[str, int, int, dict[str, int]]] = { - 254: ("NewSubfileType", LONG, 1), - 255: ("SubfileType", SHORT, 1), - 256: ("ImageWidth", LONG, 1), - 257: ("ImageLength", LONG, 1), - 258: ("BitsPerSample", SHORT, 0), - 259: ( - "Compression", - SHORT, - 1, - { - "Uncompressed": 1, - "CCITT 1d": 2, - "Group 3 Fax": 3, - "Group 4 Fax": 4, - "LZW": 5, - "JPEG": 6, - "PackBits": 32773, - }, - ), - 262: ( - "PhotometricInterpretation", - SHORT, - 1, - { - "WhiteIsZero": 0, - "BlackIsZero": 1, - "RGB": 2, - "RGB Palette": 3, - "Transparency Mask": 4, - "CMYK": 5, - "YCbCr": 6, - "CieLAB": 8, - "CFA": 32803, # TIFF/EP, Adobe DNG - "LinearRaw": 32892, # Adobe DNG - }, - ), - 263: ("Threshholding", SHORT, 1), - 264: ("CellWidth", SHORT, 1), - 265: ("CellLength", SHORT, 1), - 266: ("FillOrder", SHORT, 1), - 269: ("DocumentName", ASCII, 1), - 270: ("ImageDescription", ASCII, 1), - 271: ("Make", ASCII, 1), - 272: ("Model", ASCII, 1), - 273: ("StripOffsets", LONG, 0), - 274: ("Orientation", SHORT, 1), - 277: ("SamplesPerPixel", SHORT, 1), - 278: ("RowsPerStrip", LONG, 1), - 279: ("StripByteCounts", LONG, 0), - 280: ("MinSampleValue", SHORT, 0), - 281: ("MaxSampleValue", SHORT, 0), - 282: ("XResolution", RATIONAL, 1), - 283: ("YResolution", RATIONAL, 1), - 284: ("PlanarConfiguration", SHORT, 1, {"Contiguous": 1, "Separate": 2}), - 285: ("PageName", ASCII, 1), - 286: ("XPosition", RATIONAL, 1), - 287: ("YPosition", RATIONAL, 1), - 288: ("FreeOffsets", LONG, 1), - 289: ("FreeByteCounts", LONG, 1), - 290: ("GrayResponseUnit", SHORT, 1), - 291: ("GrayResponseCurve", SHORT, 0), - 292: ("T4Options", LONG, 1), - 293: ("T6Options", LONG, 1), - 296: ("ResolutionUnit", SHORT, 1, {"none": 1, "inch": 2, "cm": 3}), - 297: ("PageNumber", SHORT, 2), - 301: ("TransferFunction", SHORT, 0), - 305: ("Software", ASCII, 1), - 306: ("DateTime", ASCII, 1), - 315: ("Artist", ASCII, 1), - 316: ("HostComputer", ASCII, 1), - 317: ("Predictor", SHORT, 1, {"none": 1, "Horizontal Differencing": 2}), - 318: ("WhitePoint", RATIONAL, 2), - 319: ("PrimaryChromaticities", RATIONAL, 6), - 320: ("ColorMap", SHORT, 0), - 321: ("HalftoneHints", SHORT, 2), - 322: ("TileWidth", LONG, 1), - 323: ("TileLength", LONG, 1), - 324: ("TileOffsets", LONG, 0), - 325: ("TileByteCounts", LONG, 0), - 330: ("SubIFDs", LONG, 0), - 332: ("InkSet", SHORT, 1), - 333: ("InkNames", ASCII, 1), - 334: ("NumberOfInks", SHORT, 1), - 336: ("DotRange", SHORT, 0), - 337: ("TargetPrinter", ASCII, 1), - 338: ("ExtraSamples", SHORT, 0), - 339: ("SampleFormat", SHORT, 0), - 340: ("SMinSampleValue", DOUBLE, 0), - 341: ("SMaxSampleValue", DOUBLE, 0), - 342: ("TransferRange", SHORT, 6), - 347: ("JPEGTables", UNDEFINED, 1), - # obsolete JPEG tags - 512: ("JPEGProc", SHORT, 1), - 513: ("JPEGInterchangeFormat", LONG, 1), - 514: ("JPEGInterchangeFormatLength", LONG, 1), - 515: ("JPEGRestartInterval", SHORT, 1), - 517: ("JPEGLosslessPredictors", SHORT, 0), - 518: ("JPEGPointTransforms", SHORT, 0), - 519: ("JPEGQTables", LONG, 0), - 520: ("JPEGDCTables", LONG, 0), - 521: ("JPEGACTables", LONG, 0), - 529: ("YCbCrCoefficients", RATIONAL, 3), - 530: ("YCbCrSubSampling", SHORT, 2), - 531: ("YCbCrPositioning", SHORT, 1), - 532: ("ReferenceBlackWhite", RATIONAL, 6), - 700: ("XMP", BYTE, 0), - # Four private SGI tags - 32995: ("Matteing", SHORT, 1), - 32996: ("DataType", SHORT, 0), - 32997: ("ImageDepth", LONG, 1), - 32998: ("TileDepth", LONG, 1), - 33432: ("Copyright", ASCII, 1), - 33723: ("IptcNaaInfo", UNDEFINED, 1), - 34377: ("PhotoshopInfo", BYTE, 0), - # FIXME add more tags here - 34665: ("ExifIFD", LONG, 1), - 34675: ("ICCProfile", UNDEFINED, 1), - 34853: ("GPSInfoIFD", LONG, 1), - 36864: ("ExifVersion", UNDEFINED, 1), - 37724: ("ImageSourceData", UNDEFINED, 1), - 40965: ("InteroperabilityIFD", LONG, 1), - 41730: ("CFAPattern", UNDEFINED, 1), - # MPInfo - 45056: ("MPFVersion", UNDEFINED, 1), - 45057: ("NumberOfImages", LONG, 1), - 45058: ("MPEntry", UNDEFINED, 1), - 45059: ("ImageUIDList", UNDEFINED, 0), # UNDONE, check - 45060: ("TotalFrames", LONG, 1), - 45313: ("MPIndividualNum", LONG, 1), - 45569: ("PanOrientation", LONG, 1), - 45570: ("PanOverlap_H", RATIONAL, 1), - 45571: ("PanOverlap_V", RATIONAL, 1), - 45572: ("BaseViewpointNum", LONG, 1), - 45573: ("ConvergenceAngle", SIGNED_RATIONAL, 1), - 45574: ("BaselineLength", RATIONAL, 1), - 45575: ("VerticalDivergence", SIGNED_RATIONAL, 1), - 45576: ("AxisDistance_X", SIGNED_RATIONAL, 1), - 45577: ("AxisDistance_Y", SIGNED_RATIONAL, 1), - 45578: ("AxisDistance_Z", SIGNED_RATIONAL, 1), - 45579: ("YawAngle", SIGNED_RATIONAL, 1), - 45580: ("PitchAngle", SIGNED_RATIONAL, 1), - 45581: ("RollAngle", SIGNED_RATIONAL, 1), - 40960: ("FlashPixVersion", UNDEFINED, 1), - 50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}), - 50780: ("BestQualityScale", RATIONAL, 1), - 50838: ("ImageJMetaDataByteCounts", LONG, 0), # Can be more than one - 50839: ("ImageJMetaData", UNDEFINED, 1), # see Issue #2006 -} -_tags_v2_groups = { - # ExifIFD - 34665: { - 36864: ("ExifVersion", UNDEFINED, 1), - 40960: ("FlashPixVersion", UNDEFINED, 1), - 40965: ("InteroperabilityIFD", LONG, 1), - 41730: ("CFAPattern", UNDEFINED, 1), - }, - # GPSInfoIFD - 34853: { - 0: ("GPSVersionID", BYTE, 4), - 1: ("GPSLatitudeRef", ASCII, 2), - 2: ("GPSLatitude", RATIONAL, 3), - 3: ("GPSLongitudeRef", ASCII, 2), - 4: ("GPSLongitude", RATIONAL, 3), - 5: ("GPSAltitudeRef", BYTE, 1), - 6: ("GPSAltitude", RATIONAL, 1), - 7: ("GPSTimeStamp", RATIONAL, 3), - 8: ("GPSSatellites", ASCII, 0), - 9: ("GPSStatus", ASCII, 2), - 10: ("GPSMeasureMode", ASCII, 2), - 11: ("GPSDOP", RATIONAL, 1), - 12: ("GPSSpeedRef", ASCII, 2), - 13: ("GPSSpeed", RATIONAL, 1), - 14: ("GPSTrackRef", ASCII, 2), - 15: ("GPSTrack", RATIONAL, 1), - 16: ("GPSImgDirectionRef", ASCII, 2), - 17: ("GPSImgDirection", RATIONAL, 1), - 18: ("GPSMapDatum", ASCII, 0), - 19: ("GPSDestLatitudeRef", ASCII, 2), - 20: ("GPSDestLatitude", RATIONAL, 3), - 21: ("GPSDestLongitudeRef", ASCII, 2), - 22: ("GPSDestLongitude", RATIONAL, 3), - 23: ("GPSDestBearingRef", ASCII, 2), - 24: ("GPSDestBearing", RATIONAL, 1), - 25: ("GPSDestDistanceRef", ASCII, 2), - 26: ("GPSDestDistance", RATIONAL, 1), - 27: ("GPSProcessingMethod", UNDEFINED, 0), - 28: ("GPSAreaInformation", UNDEFINED, 0), - 29: ("GPSDateStamp", ASCII, 11), - 30: ("GPSDifferential", SHORT, 1), - }, - # InteroperabilityIFD - 40965: {1: ("InteropIndex", ASCII, 1), 2: ("InteropVersion", UNDEFINED, 1)}, -} - -# Legacy Tags structure -# these tags aren't included above, but were in the previous versions -TAGS: dict[int | tuple[int, int], str] = { - 347: "JPEGTables", - 700: "XMP", - # Additional Exif Info - 32932: "Wang Annotation", - 33434: "ExposureTime", - 33437: "FNumber", - 33445: "MD FileTag", - 33446: "MD ScalePixel", - 33447: "MD ColorTable", - 33448: "MD LabName", - 33449: "MD SampleInfo", - 33450: "MD PrepDate", - 33451: "MD PrepTime", - 33452: "MD FileUnits", - 33550: "ModelPixelScaleTag", - 33723: "IptcNaaInfo", - 33918: "INGR Packet Data Tag", - 33919: "INGR Flag Registers", - 33920: "IrasB Transformation Matrix", - 33922: "ModelTiepointTag", - 34264: "ModelTransformationTag", - 34377: "PhotoshopInfo", - 34735: "GeoKeyDirectoryTag", - 34736: "GeoDoubleParamsTag", - 34737: "GeoAsciiParamsTag", - 34850: "ExposureProgram", - 34852: "SpectralSensitivity", - 34855: "ISOSpeedRatings", - 34856: "OECF", - 34864: "SensitivityType", - 34865: "StandardOutputSensitivity", - 34866: "RecommendedExposureIndex", - 34867: "ISOSpeed", - 34868: "ISOSpeedLatitudeyyy", - 34869: "ISOSpeedLatitudezzz", - 34908: "HylaFAX FaxRecvParams", - 34909: "HylaFAX FaxSubAddress", - 34910: "HylaFAX FaxRecvTime", - 36864: "ExifVersion", - 36867: "DateTimeOriginal", - 36868: "DateTimeDigitized", - 37121: "ComponentsConfiguration", - 37122: "CompressedBitsPerPixel", - 37724: "ImageSourceData", - 37377: "ShutterSpeedValue", - 37378: "ApertureValue", - 37379: "BrightnessValue", - 37380: "ExposureBiasValue", - 37381: "MaxApertureValue", - 37382: "SubjectDistance", - 37383: "MeteringMode", - 37384: "LightSource", - 37385: "Flash", - 37386: "FocalLength", - 37396: "SubjectArea", - 37500: "MakerNote", - 37510: "UserComment", - 37520: "SubSec", - 37521: "SubSecTimeOriginal", - 37522: "SubsecTimeDigitized", - 40960: "FlashPixVersion", - 40961: "ColorSpace", - 40962: "PixelXDimension", - 40963: "PixelYDimension", - 40964: "RelatedSoundFile", - 40965: "InteroperabilityIFD", - 41483: "FlashEnergy", - 41484: "SpatialFrequencyResponse", - 41486: "FocalPlaneXResolution", - 41487: "FocalPlaneYResolution", - 41488: "FocalPlaneResolutionUnit", - 41492: "SubjectLocation", - 41493: "ExposureIndex", - 41495: "SensingMethod", - 41728: "FileSource", - 41729: "SceneType", - 41730: "CFAPattern", - 41985: "CustomRendered", - 41986: "ExposureMode", - 41987: "WhiteBalance", - 41988: "DigitalZoomRatio", - 41989: "FocalLengthIn35mmFilm", - 41990: "SceneCaptureType", - 41991: "GainControl", - 41992: "Contrast", - 41993: "Saturation", - 41994: "Sharpness", - 41995: "DeviceSettingDescription", - 41996: "SubjectDistanceRange", - 42016: "ImageUniqueID", - 42032: "CameraOwnerName", - 42033: "BodySerialNumber", - 42034: "LensSpecification", - 42035: "LensMake", - 42036: "LensModel", - 42037: "LensSerialNumber", - 42112: "GDAL_METADATA", - 42113: "GDAL_NODATA", - 42240: "Gamma", - 50215: "Oce Scanjob Description", - 50216: "Oce Application Selector", - 50217: "Oce Identification Number", - 50218: "Oce ImageLogic Characteristics", - # Adobe DNG - 50706: "DNGVersion", - 50707: "DNGBackwardVersion", - 50708: "UniqueCameraModel", - 50709: "LocalizedCameraModel", - 50710: "CFAPlaneColor", - 50711: "CFALayout", - 50712: "LinearizationTable", - 50713: "BlackLevelRepeatDim", - 50714: "BlackLevel", - 50715: "BlackLevelDeltaH", - 50716: "BlackLevelDeltaV", - 50717: "WhiteLevel", - 50718: "DefaultScale", - 50719: "DefaultCropOrigin", - 50720: "DefaultCropSize", - 50721: "ColorMatrix1", - 50722: "ColorMatrix2", - 50723: "CameraCalibration1", - 50724: "CameraCalibration2", - 50725: "ReductionMatrix1", - 50726: "ReductionMatrix2", - 50727: "AnalogBalance", - 50728: "AsShotNeutral", - 50729: "AsShotWhiteXY", - 50730: "BaselineExposure", - 50731: "BaselineNoise", - 50732: "BaselineSharpness", - 50733: "BayerGreenSplit", - 50734: "LinearResponseLimit", - 50735: "CameraSerialNumber", - 50736: "LensInfo", - 50737: "ChromaBlurRadius", - 50738: "AntiAliasStrength", - 50740: "DNGPrivateData", - 50778: "CalibrationIlluminant1", - 50779: "CalibrationIlluminant2", - 50784: "Alias Layer Metadata", -} - -TAGS_V2: dict[int, TagInfo] = {} -TAGS_V2_GROUPS: dict[int, dict[int, TagInfo]] = {} - - -def _populate() -> None: - for k, v in _tags_v2.items(): - # Populate legacy structure. - TAGS[k] = v[0] - if len(v) == 4: - for sk, sv in v[3].items(): - TAGS[(k, sv)] = sk - - TAGS_V2[k] = TagInfo(k, *v) - - for group, tags in _tags_v2_groups.items(): - TAGS_V2_GROUPS[group] = {k: TagInfo(k, *v) for k, v in tags.items()} - - -_populate() -## -# Map type numbers to type names -- defined in ImageFileDirectory. - -TYPES: dict[int, str] = {} - -# -# These tags are handled by default in libtiff, without -# adding to the custom dictionary. From tif_dir.c, searching for -# case TIFFTAG in the _TIFFVSetField function: -# Line: item. -# 148: case TIFFTAG_SUBFILETYPE: -# 151: case TIFFTAG_IMAGEWIDTH: -# 154: case TIFFTAG_IMAGELENGTH: -# 157: case TIFFTAG_BITSPERSAMPLE: -# 181: case TIFFTAG_COMPRESSION: -# 202: case TIFFTAG_PHOTOMETRIC: -# 205: case TIFFTAG_THRESHHOLDING: -# 208: case TIFFTAG_FILLORDER: -# 214: case TIFFTAG_ORIENTATION: -# 221: case TIFFTAG_SAMPLESPERPIXEL: -# 228: case TIFFTAG_ROWSPERSTRIP: -# 238: case TIFFTAG_MINSAMPLEVALUE: -# 241: case TIFFTAG_MAXSAMPLEVALUE: -# 244: case TIFFTAG_SMINSAMPLEVALUE: -# 247: case TIFFTAG_SMAXSAMPLEVALUE: -# 250: case TIFFTAG_XRESOLUTION: -# 256: case TIFFTAG_YRESOLUTION: -# 262: case TIFFTAG_PLANARCONFIG: -# 268: case TIFFTAG_XPOSITION: -# 271: case TIFFTAG_YPOSITION: -# 274: case TIFFTAG_RESOLUTIONUNIT: -# 280: case TIFFTAG_PAGENUMBER: -# 284: case TIFFTAG_HALFTONEHINTS: -# 288: case TIFFTAG_COLORMAP: -# 294: case TIFFTAG_EXTRASAMPLES: -# 298: case TIFFTAG_MATTEING: -# 305: case TIFFTAG_TILEWIDTH: -# 316: case TIFFTAG_TILELENGTH: -# 327: case TIFFTAG_TILEDEPTH: -# 333: case TIFFTAG_DATATYPE: -# 344: case TIFFTAG_SAMPLEFORMAT: -# 361: case TIFFTAG_IMAGEDEPTH: -# 364: case TIFFTAG_SUBIFD: -# 376: case TIFFTAG_YCBCRPOSITIONING: -# 379: case TIFFTAG_YCBCRSUBSAMPLING: -# 383: case TIFFTAG_TRANSFERFUNCTION: -# 389: case TIFFTAG_REFERENCEBLACKWHITE: -# 393: case TIFFTAG_INKNAMES: - -# Following pseudo-tags are also handled by default in libtiff: -# TIFFTAG_JPEGQUALITY 65537 - -# some of these are not in our TAGS_V2 dict and were included from tiff.h - -# This list also exists in encode.c -LIBTIFF_CORE = { - 255, - 256, - 257, - 258, - 259, - 262, - 263, - 266, - 274, - 277, - 278, - 280, - 281, - 340, - 341, - 282, - 283, - 284, - 286, - 287, - 296, - 297, - 321, - 320, - 338, - 32995, - 322, - 323, - 32998, - 32996, - 339, - 32997, - 330, - 531, - 530, - 301, - 532, - 333, - # as above - 269, # this has been in our tests forever, and works - 65537, -} - -LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes -LIBTIFF_CORE.remove(322) # We don't have support for writing tiled images with libtiff -LIBTIFF_CORE.remove(323) # Tiled images - -# Note to advanced users: There may be combinations of these -# parameters and values that when added properly, will work and -# produce valid tiff images that may work in your application. -# It is safe to add and remove tags from this set from Pillow's point -# of view so long as you test against libtiff. diff --git a/.venv/lib/python3.12/site-packages/PIL/WalImageFile.py b/.venv/lib/python3.12/site-packages/PIL/WalImageFile.py deleted file mode 100644 index 07bbf747..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/WalImageFile.py +++ /dev/null @@ -1,129 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# WAL file handling -# -# History: -# 2003-04-23 fl created -# -# Copyright (c) 2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -""" -This reader is based on the specification available from: -https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml -and has been tested with a few sample files found using google. - -.. note:: - This format cannot be automatically recognized, so the reader - is not registered for use with :py:func:`PIL.Image.open()`. - To open a WAL file, use the :py:func:`PIL.WalImageFile.open()` function instead. -""" - -from __future__ import annotations - -from typing import IO - -from . import Image, ImageFile -from ._binary import i32le as i32 -from ._typing import StrOrBytesPath - - -class WalImageFile(ImageFile.ImageFile): - format = "WAL" - format_description = "Quake2 Texture" - - def _open(self) -> None: - self._mode = "P" - - # read header fields - assert self.fp is not None - header = self.fp.read(32 + 24 + 32 + 12) - self._size = i32(header, 32), i32(header, 36) - Image._decompression_bomb_check(self.size) - - # load pixel data - offset = i32(header, 40) - self.fp.seek(offset) - - # strings are null-terminated - self.info["name"] = header[:32].split(b"\0", 1)[0] - if next_name := header[56 : 56 + 32].split(b"\0", 1)[0]: - self.info["next_name"] = next_name - - def load(self) -> Image.core.PixelAccess | None: - if self._im is None: - assert self.fp is not None - self.im = Image.core.new(self.mode, self.size) - self.frombytes(self.fp.read(self.size[0] * self.size[1])) - self.putpalette(quake2palette) - return Image.Image.load(self) - - -def open(filename: StrOrBytesPath | IO[bytes]) -> WalImageFile: - """ - Load texture from a Quake2 WAL texture file. - - By default, a Quake2 standard palette is attached to the texture. - To override the palette, use the :py:func:`PIL.Image.Image.putpalette()` method. - - :param filename: WAL file name, or an opened file handle. - :returns: An image instance. - """ - return WalImageFile(filename) - - -quake2palette = ( - # default palette taken from piffo 0.93 by Hans Häggström - b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" - b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" - b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" - b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" - b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" - b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" - b"\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" - b"\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" - b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" - b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" - b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" - b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" - b"\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" - b"\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" - b"\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" - b"\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" - b"\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" - b"\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" - b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" - b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" - b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" - b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" - b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" - b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" - b"\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" - b"\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" - b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" - b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" - b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" - b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" - b"\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" - b"\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" - b"\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" - b"\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" - b"\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" - b"\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" - b"\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" - b"\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" - b"\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" - b"\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" - b"\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" - b"\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" - b"\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" - b"\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" - b"\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" - b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" - b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" - b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" -) diff --git a/.venv/lib/python3.12/site-packages/PIL/WebPImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/WebPImagePlugin.py deleted file mode 100644 index e20e40d9..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/WebPImagePlugin.py +++ /dev/null @@ -1,317 +0,0 @@ -from __future__ import annotations - -from io import BytesIO - -from . import Image, ImageFile - -try: - from . import _webp - - SUPPORTED = True -except ImportError: - SUPPORTED = False - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import IO, Any - -_VP8_MODES_BY_IDENTIFIER = { - b"VP8 ": "RGB", - b"VP8X": "RGBA", - b"VP8L": "RGBA", # lossless -} - - -def _accept(prefix: bytes) -> bool | str: - is_riff_file_format = prefix.startswith(b"RIFF") - is_webp_file = prefix[8:12] == b"WEBP" - is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER - - if is_riff_file_format and is_webp_file and is_valid_vp8_mode: - if not SUPPORTED: - return ( - "image file could not be identified because WEBP support not installed" - ) - return True - return False - - -class WebPImageFile(ImageFile.ImageFile): - format = "WEBP" - format_description = "WebP image" - __loaded = 0 - __logical_frame = 0 - - def _open(self) -> None: - # Use the newer AnimDecoder API to parse the (possibly) animated file, - # and access muxed chunks like ICC/EXIF/XMP. - assert self.fp is not None - self._decoder = _webp.WebPAnimDecoder(self.fp.read()) - - # Get info from decoder - self._size, self.info["loop"], bgcolor, self.n_frames, self.rawmode = ( - self._decoder.get_info() - ) - self.info["background"] = ( - (bgcolor >> 16) & 0xFF, # R - (bgcolor >> 8) & 0xFF, # G - bgcolor & 0xFF, # B - (bgcolor >> 24) & 0xFF, # A - ) - self.is_animated = self.n_frames > 1 - self._mode = "RGB" if self.rawmode == "RGBX" else self.rawmode - - # Attempt to read ICC / EXIF / XMP chunks from file - for key, chunk_name in { - "icc_profile": "ICCP", - "exif": "EXIF", - "xmp": "XMP ", - }.items(): - if value := self._decoder.get_chunk(chunk_name): - self.info[key] = value - - # Initialize seek state - self._reset(reset=False) - - def _getexif(self) -> dict[int, Any] | None: - if "exif" not in self.info: - return None - return self.getexif()._get_merged_dict() - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - - # Set logical frame to requested position - self.__logical_frame = frame - - def _reset(self, reset: bool = True) -> None: - if reset: - self._decoder.reset() - self.__physical_frame = 0 - self.__loaded = -1 - self.__timestamp = 0 - - def _get_next(self) -> tuple[bytes, int, int]: - # Get next frame - ret = self._decoder.get_next() - self.__physical_frame += 1 - - # Check if an error occurred - if ret is None: - self._reset() # Reset just to be safe - self.seek(0) - msg = "failed to decode next frame in WebP file" - raise EOFError(msg) - - # Compute duration - data, timestamp = ret - duration = timestamp - self.__timestamp - self.__timestamp = timestamp - - # libwebp gives frame end, adjust to start of frame - timestamp -= duration - return data, timestamp, duration - - def _seek(self, frame: int) -> None: - if self.__physical_frame == frame: - return # Nothing to do - if frame < self.__physical_frame: - self._reset() # Rewind to beginning - while self.__physical_frame < frame: - self._get_next() # Advance to the requested frame - - def load(self) -> Image.core.PixelAccess | None: - if self.__loaded != self.__logical_frame: - self._seek(self.__logical_frame) - - # We need to load the image data for this frame - data, self.info["timestamp"], self.info["duration"] = self._get_next() - self.__loaded = self.__logical_frame - - # Set tile - if self.fp and self._exclusive_fp: - self.fp.close() - self.fp = BytesIO(data) - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.rawmode)] - - return super().load() - - def load_seek(self, pos: int) -> None: - pass - - def tell(self) -> int: - return self.__logical_frame - - -def _convert_frame(im: Image.Image) -> Image.Image: - # Make sure image mode is supported - if im.mode not in ("RGBX", "RGBA", "RGB"): - im = im.convert("RGBA" if im.has_transparency_data else "RGB") - return im - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - encoderinfo = im.encoderinfo.copy() - append_images = list(encoderinfo.get("append_images", [])) - - # If total frame count is 1, then save using the legacy API, which - # will preserve non-alpha modes - total = 0 - for ims in [im] + append_images: - total += getattr(ims, "n_frames", 1) - if total == 1: - _save(im, fp, filename) - return - - background: int | tuple[int, ...] = (0, 0, 0, 0) - if "background" in encoderinfo: - background = encoderinfo["background"] - elif "background" in im.info: - background = im.info["background"] - if isinstance(background, int): - # GifImagePlugin stores a global color table index in - # info["background"]. So it must be converted to an RGBA value - palette = im.getpalette() - if palette: - r, g, b = palette[background * 3 : (background + 1) * 3] - background = (r, g, b, 255) - else: - background = (background, background, background, 255) - - duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) - loop = im.encoderinfo.get("loop", 0) - minimize_size = im.encoderinfo.get("minimize_size", False) - kmin = im.encoderinfo.get("kmin", None) - kmax = im.encoderinfo.get("kmax", None) - allow_mixed = im.encoderinfo.get("allow_mixed", False) - verbose = False - lossless = im.encoderinfo.get("lossless", False) - quality = im.encoderinfo.get("quality", 80) - alpha_quality = im.encoderinfo.get("alpha_quality", 100) - method = im.encoderinfo.get("method", 0) - icc_profile = im.encoderinfo.get("icc_profile") or "" - exif = im.encoderinfo.get("exif", "") - if isinstance(exif, Image.Exif): - exif = exif.tobytes() - xmp = im.encoderinfo.get("xmp", "") - if allow_mixed: - lossless = False - - # Sensible keyframe defaults are from gif2webp.c script - if kmin is None: - kmin = 9 if lossless else 3 - if kmax is None: - kmax = 17 if lossless else 5 - - # Validate background color - if ( - not isinstance(background, (list, tuple)) - or len(background) != 4 - or not all(0 <= v < 256 for v in background) - ): - msg = f"Background color is not an RGBA tuple clamped to (0-255): {background}" - raise OSError(msg) - - # Convert to packed uint - bg_r, bg_g, bg_b, bg_a = background - background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0) - - # Setup the WebP animation encoder - enc = _webp.WebPAnimEncoder( - im.size, - background, - loop, - minimize_size, - kmin, - kmax, - allow_mixed, - verbose, - ) - - # Add each frame - frame_idx = 0 - timestamp = 0 - cur_idx = im.tell() - try: - for ims in [im] + append_images: - # Get number of frames in this image - nfr = getattr(ims, "n_frames", 1) - - for idx in range(nfr): - ims.seek(idx) - - frame = _convert_frame(ims) - - # Append the frame to the animation encoder - enc.add( - frame.getim(), - round(timestamp), - lossless, - quality, - alpha_quality, - method, - ) - - # Update timestamp and frame index - if isinstance(duration, (list, tuple)): - timestamp += duration[frame_idx] - else: - timestamp += duration - frame_idx += 1 - - finally: - im.seek(cur_idx) - - # Force encoder to flush frames - enc.add(None, round(timestamp), lossless, quality, alpha_quality, 0) - - # Get the final output from the encoder - data = enc.assemble(icc_profile, exif, xmp) - if data is None: - msg = "cannot write file as WebP (encoder returned None)" - raise OSError(msg) - - fp.write(data) - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - lossless = im.encoderinfo.get("lossless", False) - quality = im.encoderinfo.get("quality", 80) - alpha_quality = im.encoderinfo.get("alpha_quality", 100) - icc_profile = im.encoderinfo.get("icc_profile") or "" - exif = im.encoderinfo.get("exif", b"") - if isinstance(exif, Image.Exif): - exif = exif.tobytes() - if exif.startswith(b"Exif\x00\x00"): - exif = exif[6:] - xmp = im.encoderinfo.get("xmp", "") - method = im.encoderinfo.get("method", 4) - exact = 1 if im.encoderinfo.get("exact") else 0 - - im = _convert_frame(im) - - data = _webp.WebPEncode( - im.getim(), - lossless, - float(quality), - float(alpha_quality), - icc_profile, - method, - exact, - exif, - xmp, - ) - if data is None: - msg = "cannot write file as WebP (encoder returned None)" - raise OSError(msg) - - fp.write(data) - - -Image.register_open(WebPImageFile.format, WebPImageFile, _accept) -if SUPPORTED: - Image.register_save(WebPImageFile.format, _save) - Image.register_save_all(WebPImageFile.format, _save_all) - Image.register_extension(WebPImageFile.format, ".webp") - Image.register_mime(WebPImageFile.format, "image/webp") diff --git a/.venv/lib/python3.12/site-packages/PIL/WmfImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/WmfImagePlugin.py deleted file mode 100644 index f5e24478..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/WmfImagePlugin.py +++ /dev/null @@ -1,183 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# WMF stub codec -# -# history: -# 1996-12-14 fl Created -# 2004-02-22 fl Turned into a stub driver -# 2004-02-23 fl Added EMF support -# -# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -# WMF/EMF reference documentation: -# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf -# http://wvware.sourceforge.net/caolan/index.html -# http://wvware.sourceforge.net/caolan/ora-wmf.html -from __future__ import annotations - -from typing import IO - -from . import Image, ImageFile -from ._binary import i16le as word -from ._binary import si16le as short -from ._binary import si32le as _long - -_handler = None - - -def register_handler(handler: ImageFile.StubHandler | None) -> None: - """ - Install application-specific WMF image handler. - - :param handler: Handler object. - """ - global _handler - _handler = handler - - -if hasattr(Image.core, "drawwmf"): - # install default handler (windows only) - - class WmfHandler(ImageFile.StubHandler): - def open(self, im: ImageFile.StubImageFile) -> None: - self.bbox = im.info["wmf_bbox"] - - def load(self, im: ImageFile.StubImageFile) -> Image.Image: - assert im.fp is not None - im.fp.seek(0) # rewind - return Image.frombytes( - "RGB", - im.size, - Image.core.drawwmf(im.fp.read(), im.size, self.bbox), - "raw", - "BGR", - (im.size[0] * 3 + 3) & -4, - -1, - ) - - register_handler(WmfHandler()) - -# -# -------------------------------------------------------------------- -# Read WMF file - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith((b"\xd7\xcd\xc6\x9a\x00\x00", b"\x01\x00\x00\x00")) - - -## -# Image plugin for Windows metafiles. - - -class WmfStubImageFile(ImageFile.StubImageFile): - format = "WMF" - format_description = "Windows Metafile" - - def _open(self) -> None: - # check placeable header - assert self.fp is not None - s = self.fp.read(44) - - if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"): - # placeable windows metafile - - # get units per inch - inch = word(s, 14) - if inch == 0: - msg = "Invalid inch" - raise ValueError(msg) - self._inch: tuple[float, float] = inch, inch - - # get bounding box - x0 = short(s, 6) - y0 = short(s, 8) - x1 = short(s, 10) - y1 = short(s, 12) - - # normalize size to 72 dots per inch - self.info["dpi"] = 72 - size = ( - (x1 - x0) * self.info["dpi"] // inch, - (y1 - y0) * self.info["dpi"] // inch, - ) - - self.info["wmf_bbox"] = x0, y0, x1, y1 - - # sanity check (standard metafile header) - if s[22:26] != b"\x01\x00\t\x00": - msg = "Unsupported WMF file format" - raise SyntaxError(msg) - - elif s.startswith(b"\x01\x00\x00\x00") and s[40:44] == b" EMF": - # enhanced metafile - - # get bounding box - x0 = _long(s, 8) - y0 = _long(s, 12) - x1 = _long(s, 16) - y1 = _long(s, 20) - - # get frame (in 0.01 millimeter units) - frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36) - - size = x1 - x0, y1 - y0 - - # calculate dots per inch from bbox and frame - xdpi = 2540.0 * (x1 - x0) / (frame[2] - frame[0]) - ydpi = 2540.0 * (y1 - y0) / (frame[3] - frame[1]) - - self.info["wmf_bbox"] = x0, y0, x1, y1 - - if xdpi == ydpi: - self.info["dpi"] = xdpi - else: - self.info["dpi"] = xdpi, ydpi - self._inch = xdpi, ydpi - - else: - msg = "Unsupported file format" - raise SyntaxError(msg) - - self._mode = "RGB" - self._size = size - - def _load(self) -> ImageFile.StubHandler | None: - return _handler - - def load( - self, dpi: float | tuple[float, float] | None = None - ) -> Image.core.PixelAccess | None: - if dpi is not None: - self.info["dpi"] = dpi - x0, y0, x1, y1 = self.info["wmf_bbox"] - if not isinstance(dpi, tuple): - dpi = dpi, dpi - self._size = ( - int((x1 - x0) * dpi[0] / self._inch[0]), - int((y1 - y0) * dpi[1] / self._inch[1]), - ) - return super().load() - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if _handler is None or not hasattr(_handler, "save"): - msg = "WMF save handler not installed" - raise OSError(msg) - _handler.save(im, fp, filename) - - -# -# -------------------------------------------------------------------- -# Registry stuff - - -Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept) -Image.register_save(WmfStubImageFile.format, _save) - -Image.register_extensions(WmfStubImageFile.format, [".wmf", ".emf"]) diff --git a/.venv/lib/python3.12/site-packages/PIL/XVThumbImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/XVThumbImagePlugin.py deleted file mode 100644 index 192c041d..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/XVThumbImagePlugin.py +++ /dev/null @@ -1,83 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# XV Thumbnail file handler by Charles E. "Gene" Cash -# (gcash@magicnet.net) -# -# see xvcolor.c and xvbrowse.c in the sources to John Bradley's XV, -# available from ftp://ftp.cis.upenn.edu/pub/xv/ -# -# history: -# 98-08-15 cec created (b/w only) -# 98-12-09 cec added color palette -# 98-12-28 fl added to PIL (with only a few very minor modifications) -# -# To do: -# FIXME: make save work (this requires quantization support) -# -from __future__ import annotations - -from . import Image, ImageFile, ImagePalette -from ._binary import o8 - -_MAGIC = b"P7 332" - -# standard color palette for thumbnails (RGB332) -PALETTE = b"" -for r in range(8): - for g in range(8): - for b in range(4): - PALETTE = PALETTE + ( - o8((r * 255) // 7) + o8((g * 255) // 7) + o8((b * 255) // 3) - ) - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(_MAGIC) - - -## -# Image plugin for XV thumbnail images. - - -class XVThumbImageFile(ImageFile.ImageFile): - format = "XVThumb" - format_description = "XV thumbnail image" - - def _open(self) -> None: - # check magic - assert self.fp is not None - - if not _accept(self.fp.read(6)): - msg = "not an XV thumbnail file" - raise SyntaxError(msg) - - # Skip to beginning of next line - self.fp.readline() - - # skip info comments - while True: - s = self.fp.readline() - if not s: - msg = "Unexpected EOF reading XV thumbnail file" - raise SyntaxError(msg) - if s[0] != 35: # ie. when not a comment: '#' - break - - # parse header line (already read) - w, h = s.strip().split(maxsplit=2)[:2] - - self._mode = "P" - self._size = int(w), int(h) - - self.palette = ImagePalette.raw("RGB", PALETTE) - - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, self.fp.tell(), self.mode) - ] - - -# -------------------------------------------------------------------- - -Image.register_open(XVThumbImageFile.format, XVThumbImageFile, _accept) diff --git a/.venv/lib/python3.12/site-packages/PIL/XbmImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/XbmImagePlugin.py deleted file mode 100644 index 1e57aa16..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/XbmImagePlugin.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# XBM File handling -# -# History: -# 1995-09-08 fl Created -# 1996-11-01 fl Added save support -# 1997-07-07 fl Made header parser more tolerant -# 1997-07-22 fl Fixed yet another parser bug -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) -# 2001-05-13 fl Added hotspot handling (based on code from Bernhard Herzog) -# 2004-02-24 fl Allow some whitespace before first #define -# -# Copyright (c) 1997-2004 by Secret Labs AB -# Copyright (c) 1996-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re -from typing import IO - -from . import Image, ImageFile - -# XBM header -xbm_head = re.compile( - rb"\s*#define[ \t]+.*_width[ \t]+(?P[0-9]+)[\r\n]+" - b"#define[ \t]+.*_height[ \t]+(?P[0-9]+)[\r\n]+" - b"(?P" - b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" - b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" - b")?" - rb"[\000-\377]*_bits\[]" -) - - -def _accept(prefix: bytes) -> bool: - return prefix.lstrip().startswith(b"#define") - - -## -# Image plugin for X11 bitmaps. - - -class XbmImageFile(ImageFile.ImageFile): - format = "XBM" - format_description = "X11 Bitmap" - - def _open(self) -> None: - assert self.fp is not None - - m = xbm_head.match(self.fp.read(512)) - - if not m: - msg = "not a XBM file" - raise SyntaxError(msg) - - xsize = int(m.group("width")) - ysize = int(m.group("height")) - - if m.group("hotspot"): - self.info["hotspot"] = (int(m.group("xhot")), int(m.group("yhot"))) - - self._mode = "1" - self._size = xsize, ysize - - self.tile = [ImageFile._Tile("xbm", (0, 0) + self.size, m.end())] - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode != "1": - msg = f"cannot write mode {im.mode} as XBM" - raise OSError(msg) - - fp.write(f"#define im_width {im.size[0]}\n".encode("ascii")) - fp.write(f"#define im_height {im.size[1]}\n".encode("ascii")) - - hotspot = im.encoderinfo.get("hotspot") - if hotspot: - fp.write(f"#define im_x_hot {hotspot[0]}\n".encode("ascii")) - fp.write(f"#define im_y_hot {hotspot[1]}\n".encode("ascii")) - - fp.write(b"static char im_bits[] = {\n") - - ImageFile._save(im, fp, [ImageFile._Tile("xbm", (0, 0) + im.size)]) - - fp.write(b"};\n") - - -Image.register_open(XbmImageFile.format, XbmImageFile, _accept) -Image.register_save(XbmImageFile.format, _save) - -Image.register_extension(XbmImageFile.format, ".xbm") - -Image.register_mime(XbmImageFile.format, "image/xbm") diff --git a/.venv/lib/python3.12/site-packages/PIL/XpmImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/XpmImagePlugin.py deleted file mode 100644 index 3be240fb..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/XpmImagePlugin.py +++ /dev/null @@ -1,157 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# XPM File handling -# -# History: -# 1996-12-29 fl Created -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) -# -# Copyright (c) Secret Labs AB 1997-2001. -# Copyright (c) Fredrik Lundh 1996-2001. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re - -from . import Image, ImageFile, ImagePalette -from ._binary import o8 - -# XPM header -xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)') - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"/* XPM */") - - -## -# Image plugin for X11 pixel maps. - - -class XpmImageFile(ImageFile.ImageFile): - format = "XPM" - format_description = "X11 Pixel Map" - - def _open(self) -> None: - assert self.fp is not None - if not _accept(self.fp.read(9)): - msg = "not an XPM file" - raise SyntaxError(msg) - - # skip forward to next string - while True: - line = self.fp.readline() - if not line: - msg = "broken XPM file" - raise SyntaxError(msg) - m = xpm_head.match(line) - if m: - break - - self._size = int(m.group(1)), int(m.group(2)) - - palette_length = int(m.group(3)) - bpp = int(m.group(4)) - - # - # load palette description - - palette = {} - - for _ in range(palette_length): - line = self.fp.readline().rstrip() - - c = line[1 : bpp + 1] - s = line[bpp + 1 : -2].split() - - for i in range(0, len(s), 2): - if s[i] == b"c": - # process colour key - rgb = s[i + 1] - if rgb == b"None": - self.info["transparency"] = c - elif rgb.startswith(b"#"): - rgb_int = int(rgb[1:], 16) - palette[c] = ( - o8((rgb_int >> 16) & 255) - + o8((rgb_int >> 8) & 255) - + o8(rgb_int & 255) - ) - else: - # unknown colour - msg = "cannot read this XPM file" - raise ValueError(msg) - break - - else: - # missing colour key - msg = "cannot read this XPM file" - raise ValueError(msg) - - args: tuple[int, dict[bytes, bytes] | tuple[bytes, ...]] - if palette_length > 256: - self._mode = "RGB" - args = (bpp, palette) - else: - self._mode = "P" - self.palette = ImagePalette.raw("RGB", b"".join(palette.values())) - args = (bpp, tuple(palette.keys())) - - self.tile = [ImageFile._Tile("xpm", (0, 0) + self.size, self.fp.tell(), args)] - - def load_read(self, read_bytes: int) -> bytes: - # - # load all image data in one chunk - - xsize, ysize = self.size - - assert self.fp is not None - s = [self.fp.readline()[1 : xsize + 1].ljust(xsize) for i in range(ysize)] - - return b"".join(s) - - -class XpmDecoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - - data = bytearray() - bpp, palette = self.args - dest_length = self.state.xsize * self.state.ysize - if self.mode == "RGB": - dest_length *= 3 - pixel_header = False - while len(data) < dest_length: - line = self.fd.readline() - if not line: - break - if line.rstrip() == b"/* pixels */" and not pixel_header: - pixel_header = True - continue - line = b'"'.join(line.split(b'"')[1:-1]) - for i in range(0, len(line), bpp): - key = line[i : i + bpp] - if self.mode == "RGB": - data += palette[key] - else: - data += o8(palette.index(key)) - self.set_as_raw(bytes(data)) - return -1, 0 - - -# -# Registry - - -Image.register_open(XpmImageFile.format, XpmImageFile, _accept) -Image.register_decoder("xpm", XpmDecoder) - -Image.register_extension(XpmImageFile.format, ".xpm") - -Image.register_mime(XpmImageFile.format, "image/xpm") diff --git a/.venv/lib/python3.12/site-packages/PIL/__init__.py b/.venv/lib/python3.12/site-packages/PIL/__init__.py deleted file mode 100644 index faf3e76e..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/__init__.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Pillow (Fork of the Python Imaging Library) - -Pillow is the friendly PIL fork by Jeffrey 'Alex' Clark and contributors. - https://github.com/python-pillow/Pillow/ - -Pillow is forked from PIL 1.1.7. - -PIL is the Python Imaging Library by Fredrik Lundh and contributors. -Copyright (c) 1999 by Secret Labs AB. - -Use PIL.__version__ for this Pillow version. - -;-) -""" - -from __future__ import annotations - -from . import _version - -# VERSION was removed in Pillow 6.0.0. -# PILLOW_VERSION was removed in Pillow 9.0.0. -# Use __version__ instead. -__version__ = _version.__version__ -del _version - - -_plugins = [ - "AvifImagePlugin", - "BlpImagePlugin", - "BmpImagePlugin", - "BufrStubImagePlugin", - "CurImagePlugin", - "DcxImagePlugin", - "DdsImagePlugin", - "EpsImagePlugin", - "FitsImagePlugin", - "FliImagePlugin", - "FpxImagePlugin", - "FtexImagePlugin", - "GbrImagePlugin", - "GifImagePlugin", - "GribStubImagePlugin", - "Hdf5StubImagePlugin", - "IcnsImagePlugin", - "IcoImagePlugin", - "ImImagePlugin", - "ImtImagePlugin", - "IptcImagePlugin", - "JpegImagePlugin", - "Jpeg2KImagePlugin", - "McIdasImagePlugin", - "MicImagePlugin", - "MpegImagePlugin", - "MpoImagePlugin", - "MspImagePlugin", - "PalmImagePlugin", - "PcdImagePlugin", - "PcxImagePlugin", - "PdfImagePlugin", - "PixarImagePlugin", - "PngImagePlugin", - "PpmImagePlugin", - "PsdImagePlugin", - "QoiImagePlugin", - "SgiImagePlugin", - "SpiderImagePlugin", - "SunImagePlugin", - "TgaImagePlugin", - "TiffImagePlugin", - "WebPImagePlugin", - "WmfImagePlugin", - "XbmImagePlugin", - "XpmImagePlugin", - "XVThumbImagePlugin", -] - - -class UnidentifiedImageError(OSError): - """ - Raised in :py:meth:`PIL.Image.open` if an image cannot be opened and identified. - - If a PNG image raises this error, setting :data:`.ImageFile.LOAD_TRUNCATED_IMAGES` - to true may allow the image to be opened after all. The setting will ignore missing - data and checksum failures. - """ - - pass diff --git a/.venv/lib/python3.12/site-packages/PIL/__main__.py b/.venv/lib/python3.12/site-packages/PIL/__main__.py deleted file mode 100644 index 043156e8..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/__main__.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import annotations - -import sys - -from .features import pilinfo - -pilinfo(supported_formats="--report" not in sys.argv) diff --git a/.venv/lib/python3.12/site-packages/PIL/_avif.cpython-312-darwin.so b/.venv/lib/python3.12/site-packages/PIL/_avif.cpython-312-darwin.so deleted file mode 100755 index 83ece7e8..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/_avif.cpython-312-darwin.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_avif.pyi b/.venv/lib/python3.12/site-packages/PIL/_avif.pyi deleted file mode 100644 index e27843e5..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_avif.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/.venv/lib/python3.12/site-packages/PIL/_binary.py b/.venv/lib/python3.12/site-packages/PIL/_binary.py deleted file mode 100644 index d3236c17..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_binary.py +++ /dev/null @@ -1,113 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Binary input/output support routines. -# -# Copyright (c) 1997-2003 by Secret Labs AB -# Copyright (c) 1995-2003 by Fredrik Lundh -# Copyright (c) 2012 by Brian Crowell -# -# See the README file for information on usage and redistribution. -# - - -"""Binary input/output support routines.""" - -from __future__ import annotations - -from struct import pack, unpack_from - - -def i8(c: bytes) -> int: - return c[0] - - -def o8(i: int) -> bytes: - return bytes((i & 255,)) - - -# Input, le = little endian, be = big endian -def i16le(c: bytes, o: int = 0) -> int: - """ - Converts a 2-bytes (16 bits) string to an unsigned integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 2-bytes (16 bits) string to a signed integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 2-bytes (16 bits) string to a signed integer, big endian. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(">h", c, o)[0] - - -def i32le(c: bytes, o: int = 0) -> int: - """ - Converts a 4-bytes (32 bits) string to an unsigned integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 4-bytes (32 bits) string to a signed integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 4-bytes (32 bits) string to a signed integer, big endian. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(">i", c, o)[0] - - -def i16be(c: bytes, o: int = 0) -> int: - return unpack_from(">H", c, o)[0] - - -def i32be(c: bytes, o: int = 0) -> int: - return unpack_from(">I", c, o)[0] - - -# Output, le = little endian, be = big endian -def o16le(i: int) -> bytes: - return pack(" bytes: - return pack(" bytes: - return pack(">H", i) - - -def o32be(i: int) -> bytes: - return pack(">I", i) diff --git a/.venv/lib/python3.12/site-packages/PIL/_deprecate.py b/.venv/lib/python3.12/site-packages/PIL/_deprecate.py deleted file mode 100644 index 711c62ab..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_deprecate.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -import warnings - -from . import __version__ - - -def deprecate( - deprecated: str, - when: int | None, - replacement: str | None = None, - *, - action: str | None = None, - plural: bool = False, - stacklevel: int = 3, -) -> None: - """ - Deprecations helper. - - :param deprecated: Name of thing to be deprecated. - :param when: Pillow major version to be removed in. - :param replacement: Name of replacement. - :param action: Instead of "replacement", give a custom call to action - e.g. "Upgrade to new thing". - :param plural: if the deprecated thing is plural, needing "are" instead of "is". - - Usually of the form: - - "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd). - Use [replacement] instead." - - You can leave out the replacement sentence: - - "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd)" - - Or with another call to action: - - "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd). - [action]." - """ - - is_ = "are" if plural else "is" - - if when is None: - removed = "a future version" - elif when <= int(__version__.split(".")[0]): - msg = f"{deprecated} {is_} deprecated and should be removed." - raise RuntimeError(msg) - elif when == 13: - removed = "Pillow 13 (2026-10-15)" - elif when == 14: - removed = "Pillow 14 (2027-10-15)" - else: - msg = f"Unknown removal version: {when}. Update {__name__}?" - raise ValueError(msg) - - if replacement and action: - msg = "Use only one of 'replacement' and 'action'" - raise ValueError(msg) - - if replacement: - action = f". Use {replacement} instead." - elif action: - action = f". {action.rstrip('.')}." - else: - action = "" - - warnings.warn( - f"{deprecated} {is_} deprecated and will be removed in {removed}{action}", - DeprecationWarning, - stacklevel=stacklevel, - ) diff --git a/.venv/lib/python3.12/site-packages/PIL/_imaging.cpython-312-darwin.so b/.venv/lib/python3.12/site-packages/PIL/_imaging.cpython-312-darwin.so deleted file mode 100755 index c19bbf3b..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/_imaging.cpython-312-darwin.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_imaging.pyi b/.venv/lib/python3.12/site-packages/PIL/_imaging.pyi deleted file mode 100644 index 81028a59..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_imaging.pyi +++ /dev/null @@ -1,31 +0,0 @@ -from typing import Any - -class ImagingCore: - def __getitem__(self, index: int) -> float | tuple[int, ...] | None: ... - def __getattr__(self, name: str) -> Any: ... - -class ImagingFont: - def __getattr__(self, name: str) -> Any: ... - -class ImagingDraw: - def __getattr__(self, name: str) -> Any: ... - -class PixelAccess: - def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]: ... - def __setitem__( - self, xy: tuple[int, int], color: float | tuple[int, ...] - ) -> None: ... - -class ImagingDecoder: - def __getattr__(self, name: str) -> Any: ... - -class ImagingEncoder: - def __getattr__(self, name: str) -> Any: ... - -class _Outline: - def close(self) -> None: ... - def __getattr__(self, name: str) -> Any: ... - -def font(image: ImagingCore, glyphdata: bytes) -> ImagingFont: ... -def outline() -> _Outline: ... -def __getattr__(name: str) -> Any: ... diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingcms.cpython-312-darwin.so b/.venv/lib/python3.12/site-packages/PIL/_imagingcms.cpython-312-darwin.so deleted file mode 100755 index 7f2c943d..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/_imagingcms.cpython-312-darwin.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingcms.pyi b/.venv/lib/python3.12/site-packages/PIL/_imagingcms.pyi deleted file mode 100644 index 4fc0d60a..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_imagingcms.pyi +++ /dev/null @@ -1,143 +0,0 @@ -import datetime -import sys -from typing import Literal, SupportsFloat, TypeAlias, TypedDict - -from ._typing import CapsuleType - -littlecms_version: str | None - -_Tuple3f: TypeAlias = tuple[float, float, float] -_Tuple2x3f: TypeAlias = tuple[_Tuple3f, _Tuple3f] -_Tuple3x3f: TypeAlias = tuple[_Tuple3f, _Tuple3f, _Tuple3f] - -class _IccMeasurementCondition(TypedDict): - observer: int - backing: _Tuple3f - geo: str - flare: float - illuminant_type: str - -class _IccViewingCondition(TypedDict): - illuminant: _Tuple3f - surround: _Tuple3f - illuminant_type: str - -class CmsProfile: - @property - def rendering_intent(self) -> int: ... - @property - def creation_date(self) -> datetime.datetime | None: ... - @property - def copyright(self) -> str | None: ... - @property - def target(self) -> str | None: ... - @property - def manufacturer(self) -> str | None: ... - @property - def model(self) -> str | None: ... - @property - def profile_description(self) -> str | None: ... - @property - def screening_description(self) -> str | None: ... - @property - def viewing_condition(self) -> str | None: ... - @property - def version(self) -> float: ... - @property - def icc_version(self) -> int: ... - @property - def attributes(self) -> int: ... - @property - def header_flags(self) -> int: ... - @property - def header_manufacturer(self) -> str: ... - @property - def header_model(self) -> str: ... - @property - def device_class(self) -> str: ... - @property - def connection_space(self) -> str: ... - @property - def xcolor_space(self) -> str: ... - @property - def profile_id(self) -> bytes: ... - @property - def is_matrix_shaper(self) -> bool: ... - @property - def technology(self) -> str | None: ... - @property - def colorimetric_intent(self) -> str | None: ... - @property - def perceptual_rendering_intent_gamut(self) -> str | None: ... - @property - def saturation_rendering_intent_gamut(self) -> str | None: ... - @property - def red_colorant(self) -> _Tuple2x3f | None: ... - @property - def green_colorant(self) -> _Tuple2x3f | None: ... - @property - def blue_colorant(self) -> _Tuple2x3f | None: ... - @property - def red_primary(self) -> _Tuple2x3f | None: ... - @property - def green_primary(self) -> _Tuple2x3f | None: ... - @property - def blue_primary(self) -> _Tuple2x3f | None: ... - @property - def media_white_point_temperature(self) -> float | None: ... - @property - def media_white_point(self) -> _Tuple2x3f | None: ... - @property - def media_black_point(self) -> _Tuple2x3f | None: ... - @property - def luminance(self) -> _Tuple2x3f | None: ... - @property - def chromatic_adaptation(self) -> tuple[_Tuple3x3f, _Tuple3x3f] | None: ... - @property - def chromaticity(self) -> _Tuple3x3f | None: ... - @property - def colorant_table(self) -> list[str] | None: ... - @property - def colorant_table_out(self) -> list[str] | None: ... - @property - def intent_supported(self) -> dict[int, tuple[bool, bool, bool]] | None: ... - @property - def clut(self) -> dict[int, tuple[bool, bool, bool]] | None: ... - @property - def icc_measurement_condition(self) -> _IccMeasurementCondition | None: ... - @property - def icc_viewing_condition(self) -> _IccViewingCondition | None: ... - def is_intent_supported(self, intent: int, direction: int, /) -> int: ... - -class CmsTransform: - def apply(self, id_in: CapsuleType, id_out: CapsuleType) -> int: ... - -def profile_open(profile: str, /) -> CmsProfile: ... -def profile_frombytes(profile: bytes, /) -> CmsProfile: ... -def profile_tobytes(profile: CmsProfile, /) -> bytes: ... -def buildTransform( - input_profile: CmsProfile, - output_profile: CmsProfile, - in_mode: str, - out_mode: str, - rendering_intent: int = 0, - cms_flags: int = 0, - /, -) -> CmsTransform: ... -def buildProofTransform( - input_profile: CmsProfile, - output_profile: CmsProfile, - proof_profile: CmsProfile, - in_mode: str, - out_mode: str, - rendering_intent: int = 0, - proof_intent: int = 0, - cms_flags: int = 0, - /, -) -> CmsTransform: ... -def createProfile( - color_space: Literal["LAB", "XYZ", "sRGB"], color_temp: SupportsFloat = 0.0, / -) -> CmsProfile: ... - -if sys.platform == "win32": - def get_display_profile_win32(handle: int = 0, is_dc: int = 0, /) -> str | None: ... diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingft.cpython-312-darwin.so b/.venv/lib/python3.12/site-packages/PIL/_imagingft.cpython-312-darwin.so deleted file mode 100755 index 52d93149..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/_imagingft.cpython-312-darwin.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingft.pyi b/.venv/lib/python3.12/site-packages/PIL/_imagingft.pyi deleted file mode 100644 index 2136810b..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_imagingft.pyi +++ /dev/null @@ -1,70 +0,0 @@ -from collections.abc import Callable -from typing import Any - -from . import ImageFont, _imaging - -class Font: - @property - def family(self) -> str | None: ... - @property - def style(self) -> str | None: ... - @property - def ascent(self) -> int: ... - @property - def descent(self) -> int: ... - @property - def height(self) -> int: ... - @property - def x_ppem(self) -> int: ... - @property - def y_ppem(self) -> int: ... - @property - def glyphs(self) -> int: ... - def render( - self, - string: str | bytes, - fill: Callable[[int, int], _imaging.ImagingCore], - mode: str, - dir: str | None, - features: list[str] | None, - lang: str | None, - stroke_width: float, - stroke_filled: bool, - anchor: str | None, - foreground_ink_long: int, - start: tuple[float, float], - /, - ) -> tuple[_imaging.ImagingCore, tuple[int, int]]: ... - def getsize( - self, - string: str | bytes | bytearray, - mode: str, - dir: str | None, - features: list[str] | None, - lang: str | None, - anchor: str | None, - /, - ) -> tuple[tuple[int, int], tuple[int, int]]: ... - def getlength( - self, - string: str | bytes, - mode: str, - dir: str | None, - features: list[str] | None, - lang: str | None, - /, - ) -> float: ... - def getvarnames(self) -> list[bytes]: ... - def getvaraxes(self) -> list[ImageFont.Axis]: ... - def setvarname(self, instance_index: int, /) -> None: ... - def setvaraxes(self, axes: list[float], /) -> None: ... - -def getfont( - filename: str | bytes, - size: float, - index: int, - encoding: str, - font_bytes: bytes, - layout_engine: int, -) -> Font: ... -def __getattr__(name: str) -> Any: ... diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingmath.cpython-312-darwin.so b/.venv/lib/python3.12/site-packages/PIL/_imagingmath.cpython-312-darwin.so deleted file mode 100755 index 81d05a04..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/_imagingmath.cpython-312-darwin.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingmath.pyi b/.venv/lib/python3.12/site-packages/PIL/_imagingmath.pyi deleted file mode 100644 index e27843e5..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_imagingmath.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.cpython-312-darwin.so b/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.cpython-312-darwin.so deleted file mode 100755 index 6f6387da..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.cpython-312-darwin.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.pyi b/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.pyi deleted file mode 100644 index e27843e5..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingtk.cpython-312-darwin.so b/.venv/lib/python3.12/site-packages/PIL/_imagingtk.cpython-312-darwin.so deleted file mode 100755 index 5650763e..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/_imagingtk.cpython-312-darwin.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingtk.pyi b/.venv/lib/python3.12/site-packages/PIL/_imagingtk.pyi deleted file mode 100644 index e27843e5..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_imagingtk.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/.venv/lib/python3.12/site-packages/PIL/_tkinter_finder.py b/.venv/lib/python3.12/site-packages/PIL/_tkinter_finder.py deleted file mode 100644 index 9c014300..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_tkinter_finder.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Find compiled module linking to Tcl / Tk libraries""" - -from __future__ import annotations - -import sys -import tkinter - -tk = getattr(tkinter, "_tkinter") - -try: - if hasattr(sys, "pypy_find_executable"): - TKINTER_LIB = tk.tklib_cffi.__file__ - else: - TKINTER_LIB = tk.__file__ -except AttributeError: - # _tkinter may be compiled directly into Python, in which case __file__ is - # not available. load_tkinter_funcs will check the binary first in any case. - TKINTER_LIB = None - -tk_version = str(tkinter.TkVersion) diff --git a/.venv/lib/python3.12/site-packages/PIL/_typing.py b/.venv/lib/python3.12/site-packages/PIL/_typing.py deleted file mode 100644 index a941f898..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_typing.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -import os -import sys -from collections.abc import Sequence -from typing import Any, Protocol, TypeVar - -TYPE_CHECKING = False -if TYPE_CHECKING: - from numbers import _IntegralLike as IntegralLike - - try: - import numpy.typing as npt - - NumpyArray = npt.NDArray[Any] - except ImportError: - pass - -if sys.version_info >= (3, 13): - from types import CapsuleType -else: - CapsuleType = object - -if sys.version_info >= (3, 12): - from collections.abc import Buffer -else: - Buffer = Any - - -_Ink = float | tuple[int, ...] | str - -Coords = Sequence[float] | Sequence[Sequence[float]] - - -_T_co = TypeVar("_T_co", covariant=True) - - -class SupportsRead(Protocol[_T_co]): - def read(self, length: int = ..., /) -> _T_co: ... - - -StrOrBytesPath = str | bytes | os.PathLike[str] | os.PathLike[bytes] - - -__all__ = ["Buffer", "IntegralLike", "StrOrBytesPath", "SupportsRead"] diff --git a/.venv/lib/python3.12/site-packages/PIL/_util.py b/.venv/lib/python3.12/site-packages/PIL/_util.py deleted file mode 100644 index b1fa6a0f..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_util.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -import os - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import Any, NoReturn, TypeGuard - - from ._typing import StrOrBytesPath - - -def is_path(f: Any) -> TypeGuard[StrOrBytesPath]: - return isinstance(f, (bytes, str, os.PathLike)) - - -class DeferredError: - def __init__(self, ex: BaseException): - self.ex = ex - - def __getattr__(self, elt: str) -> NoReturn: - raise self.ex - - @staticmethod - def new(ex: BaseException) -> Any: - """ - Creates an object that raises the wrapped exception ``ex`` when used, - and casts it to :py:obj:`~typing.Any` type. - """ - return DeferredError(ex) diff --git a/.venv/lib/python3.12/site-packages/PIL/_version.py b/.venv/lib/python3.12/site-packages/PIL/_version.py deleted file mode 100644 index 72d11ae9..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_version.py +++ /dev/null @@ -1,4 +0,0 @@ -# Master version for Pillow -from __future__ import annotations - -__version__ = "12.2.0" diff --git a/.venv/lib/python3.12/site-packages/PIL/_webp.cpython-312-darwin.so b/.venv/lib/python3.12/site-packages/PIL/_webp.cpython-312-darwin.so deleted file mode 100755 index a521cea6..00000000 Binary files a/.venv/lib/python3.12/site-packages/PIL/_webp.cpython-312-darwin.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_webp.pyi b/.venv/lib/python3.12/site-packages/PIL/_webp.pyi deleted file mode 100644 index e27843e5..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/_webp.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/.venv/lib/python3.12/site-packages/PIL/features.py b/.venv/lib/python3.12/site-packages/PIL/features.py deleted file mode 100644 index ff32c251..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/features.py +++ /dev/null @@ -1,343 +0,0 @@ -from __future__ import annotations - -import collections -import os -import sys -import warnings -from typing import IO - -import PIL - -from . import Image - -modules = { - "pil": ("PIL._imaging", "PILLOW_VERSION"), - "tkinter": ("PIL._tkinter_finder", "tk_version"), - "freetype2": ("PIL._imagingft", "freetype2_version"), - "littlecms2": ("PIL._imagingcms", "littlecms_version"), - "webp": ("PIL._webp", "webpdecoder_version"), - "avif": ("PIL._avif", "libavif_version"), -} - - -def check_module(feature: str) -> bool: - """ - Checks if a module is available. - - :param feature: The module to check for. - :returns: ``True`` if available, ``False`` otherwise. - :raises ValueError: If the module is not defined in this version of Pillow. - """ - if feature not in modules: - msg = f"Unknown module {feature}" - raise ValueError(msg) - - module, ver = modules[feature] - - try: - __import__(module) - return True - except ModuleNotFoundError: - return False - except ImportError as ex: - warnings.warn(str(ex)) - return False - - -def version_module(feature: str) -> str | None: - """ - :param feature: The module to check for. - :returns: - The loaded version number as a string, or ``None`` if unknown or not available. - :raises ValueError: If the module is not defined in this version of Pillow. - """ - if not check_module(feature): - return None - - module, ver = modules[feature] - - return getattr(__import__(module, fromlist=[ver]), ver) - - -def get_supported_modules() -> list[str]: - """ - :returns: A list of all supported modules. - """ - return [f for f in modules if check_module(f)] - - -codecs = { - "jpg": ("jpeg", "jpeglib"), - "jpg_2000": ("jpeg2k", "jp2klib"), - "zlib": ("zip", "zlib"), - "libtiff": ("libtiff", "libtiff"), -} - - -def check_codec(feature: str) -> bool: - """ - Checks if a codec is available. - - :param feature: The codec to check for. - :returns: ``True`` if available, ``False`` otherwise. - :raises ValueError: If the codec is not defined in this version of Pillow. - """ - if feature not in codecs: - msg = f"Unknown codec {feature}" - raise ValueError(msg) - - codec, lib = codecs[feature] - - return f"{codec}_encoder" in dir(Image.core) - - -def version_codec(feature: str) -> str | None: - """ - :param feature: The codec to check for. - :returns: - The version number as a string, or ``None`` if not available. - Checked at compile time for ``jpg``, run-time otherwise. - :raises ValueError: If the codec is not defined in this version of Pillow. - """ - if not check_codec(feature): - return None - - codec, lib = codecs[feature] - - version = getattr(Image.core, f"{lib}_version") - - if feature == "libtiff": - return version.split("\n")[0].split("Version ")[1] - - return version - - -def get_supported_codecs() -> list[str]: - """ - :returns: A list of all supported codecs. - """ - return [f for f in codecs if check_codec(f)] - - -features: dict[str, tuple[str, str, str | None]] = { - "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"), - "fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"), - "harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"), - "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"), - "mozjpeg": ("PIL._imaging", "HAVE_MOZJPEG", "libjpeg_turbo_version"), - "zlib_ng": ("PIL._imaging", "HAVE_ZLIBNG", "zlib_ng_version"), - "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"), - "xcb": ("PIL._imaging", "HAVE_XCB", None), -} - - -def check_feature(feature: str) -> bool | None: - """ - Checks if a feature is available. - - :param feature: The feature to check for. - :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown. - :raises ValueError: If the feature is not defined in this version of Pillow. - """ - if feature not in features: - msg = f"Unknown feature {feature}" - raise ValueError(msg) - - module, flag, ver = features[feature] - - try: - imported_module = __import__(module, fromlist=["PIL"]) - return getattr(imported_module, flag) - except ModuleNotFoundError: - return None - except ImportError as ex: - warnings.warn(str(ex)) - return None - - -def version_feature(feature: str) -> str | None: - """ - :param feature: The feature to check for. - :returns: The version number as a string, or ``None`` if not available. - :raises ValueError: If the feature is not defined in this version of Pillow. - """ - if not check_feature(feature): - return None - - module, flag, ver = features[feature] - - if ver is None: - return None - - return getattr(__import__(module, fromlist=[ver]), ver) - - -def get_supported_features() -> list[str]: - """ - :returns: A list of all supported features. - """ - return [f for f in features if check_feature(f)] - - -def check(feature: str) -> bool | None: - """ - :param feature: A module, codec, or feature name. - :returns: - ``True`` if the module, codec, or feature is available, - ``False`` or ``None`` otherwise. - """ - - if feature in modules: - return check_module(feature) - if feature in codecs: - return check_codec(feature) - if feature in features: - return check_feature(feature) - warnings.warn(f"Unknown feature '{feature}'.", stacklevel=2) - return False - - -def version(feature: str) -> str | None: - """ - :param feature: - The module, codec, or feature to check for. - :returns: - The version number as a string, or ``None`` if unknown or not available. - """ - if feature in modules: - return version_module(feature) - if feature in codecs: - return version_codec(feature) - if feature in features: - return version_feature(feature) - return None - - -def get_supported() -> list[str]: - """ - :returns: A list of all supported modules, features, and codecs. - """ - - ret = get_supported_modules() - ret.extend(get_supported_features()) - ret.extend(get_supported_codecs()) - return ret - - -def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None: - """ - Prints information about this installation of Pillow. - This function can be called with ``python3 -m PIL``. - It can also be called with ``python3 -m PIL.report`` or ``python3 -m PIL --report`` - to have "supported_formats" set to ``False``, omitting the list of all supported - image file formats. - - :param out: - The output stream to print to. Defaults to ``sys.stdout`` if ``None``. - :param supported_formats: - If ``True``, a list of all supported image file formats will be printed. - """ - - if out is None: - out = sys.stdout - - Image.init() - - print("-" * 68, file=out) - print(f"Pillow {PIL.__version__}", file=out) - py_version_lines = sys.version.splitlines() - print(f"Python {py_version_lines[0].strip()}", file=out) - for py_version in py_version_lines[1:]: - print(f" {py_version.strip()}", file=out) - print("-" * 68, file=out) - print(f"Python executable is {sys.executable or 'unknown'}", file=out) - if sys.prefix != sys.base_prefix: - print(f"Environment Python files loaded from {sys.prefix}", file=out) - print(f"System Python files loaded from {sys.base_prefix}", file=out) - print("-" * 68, file=out) - print( - f"Python Pillow modules loaded from {os.path.dirname(Image.__file__)}", - file=out, - ) - print( - f"Binary Pillow modules loaded from {os.path.dirname(Image.core.__file__)}", - file=out, - ) - print("-" * 68, file=out) - - for name, feature in [ - ("pil", "PIL CORE"), - ("tkinter", "TKINTER"), - ("freetype2", "FREETYPE2"), - ("littlecms2", "LITTLECMS2"), - ("webp", "WEBP"), - ("avif", "AVIF"), - ("jpg", "JPEG"), - ("jpg_2000", "OPENJPEG (JPEG2000)"), - ("zlib", "ZLIB (PNG/ZIP)"), - ("libtiff", "LIBTIFF"), - ("raqm", "RAQM (Bidirectional Text)"), - ("libimagequant", "LIBIMAGEQUANT (Quantization method)"), - ("xcb", "XCB (X protocol)"), - ]: - if check(name): - v: str | None = None - if name == "jpg": - libjpeg_turbo_version = version_feature("libjpeg_turbo") - if libjpeg_turbo_version is not None: - v = "mozjpeg" if check_feature("mozjpeg") else "libjpeg-turbo" - v += " " + libjpeg_turbo_version - if v is None: - v = version(name) - if v is not None: - version_static = name in ("pil", "jpg") - if name == "littlecms2": - # this check is also in src/_imagingcms.c:setup_module() - version_static = tuple(int(x) for x in v.split(".")) < (2, 7) - t = "compiled for" if version_static else "loaded" - if name == "zlib": - zlib_ng_version = version_feature("zlib_ng") - if zlib_ng_version is not None: - v += ", compiled for zlib-ng " + zlib_ng_version - elif name == "raqm": - for f in ("fribidi", "harfbuzz"): - v2 = version_feature(f) - if v2 is not None: - v += f", {f} {v2}" - print("---", feature, "support ok,", t, v, file=out) - else: - print("---", feature, "support ok", file=out) - else: - print("***", feature, "support not installed", file=out) - print("-" * 68, file=out) - - if supported_formats: - extensions = collections.defaultdict(list) - for ext, i in Image.EXTENSION.items(): - extensions[i].append(ext) - - for i in sorted(Image.ID): - line = f"{i}" - if i in Image.MIME: - line = f"{line} {Image.MIME[i]}" - print(line, file=out) - - if i in extensions: - print( - "Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out - ) - - features = [] - if i in Image.OPEN: - features.append("open") - if i in Image.SAVE: - features.append("save") - if i in Image.SAVE_ALL: - features.append("save_all") - if i in Image.DECODERS: - features.append("decode") - if i in Image.ENCODERS: - features.append("encode") - - print("Features: {}".format(", ".join(features)), file=out) - print("-" * 68, file=out) diff --git a/.venv/lib/python3.12/site-packages/PIL/py.typed b/.venv/lib/python3.12/site-packages/PIL/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/PIL/report.py b/.venv/lib/python3.12/site-packages/PIL/report.py deleted file mode 100644 index d2815e84..00000000 --- a/.venv/lib/python3.12/site-packages/PIL/report.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import annotations - -from .features import pilinfo - -pilinfo(supported_formats=False) diff --git a/.venv/lib/python3.12/site-packages/__editable__.debug-0.1.pth b/.venv/lib/python3.12/site-packages/__editable__.debug-0.1.pth deleted file mode 100644 index 51ea9e83..00000000 --- a/.venv/lib/python3.12/site-packages/__editable__.debug-0.1.pth +++ /dev/null @@ -1 +0,0 @@ -import __editable___debug_0_1_finder; __editable___debug_0_1_finder.install() \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/__editable___debug_0_1_finder.py b/.venv/lib/python3.12/site-packages/__editable___debug_0_1_finder.py deleted file mode 100644 index 6928740c..00000000 --- a/.venv/lib/python3.12/site-packages/__editable___debug_0_1_finder.py +++ /dev/null @@ -1,85 +0,0 @@ -from __future__ import annotations -import sys -from importlib.machinery import ModuleSpec, PathFinder -from importlib.machinery import all_suffixes as module_suffixes -from importlib.util import spec_from_file_location -from itertools import chain -from pathlib import Path - -MAPPING: dict[str, str] = {'debug': '/Users/ericzeng/openswarm/debugger/debug', 'debugger_backend': '/Users/ericzeng/openswarm/debugger/debugger_backend', 'swarm_debug': '/Users/ericzeng/openswarm/debugger/swarm_debug'} -NAMESPACES: dict[str, list[str]] = {} -PATH_PLACEHOLDER = '__editable__.debug-0.1.finder' + ".__path_hook__" - - -class _EditableFinder: # MetaPathFinder - @classmethod - def find_spec(cls, fullname: str, path=None, target=None) -> ModuleSpec | None: # type: ignore - # Top-level packages and modules (we know these exist in the FS) - if fullname in MAPPING: - pkg_path = MAPPING[fullname] - return cls._find_spec(fullname, Path(pkg_path)) - - # Handle immediate children modules (required for namespaces to work) - # To avoid problems with case sensitivity in the file system we delegate - # to the importlib.machinery implementation. - parent, _, child = fullname.rpartition(".") - if parent and parent in MAPPING: - return PathFinder.find_spec(fullname, path=[MAPPING[parent]]) - - # Other levels of nesting should be handled automatically by importlib - # using the parent path. - return None - - @classmethod - def _find_spec(cls, fullname: str, candidate_path: Path) -> ModuleSpec | None: - init = candidate_path / "__init__.py" - candidates = (candidate_path.with_suffix(x) for x in module_suffixes()) - for candidate in chain([init], candidates): - if candidate.exists(): - return spec_from_file_location(fullname, candidate) - return None - - -class _EditableNamespaceFinder: # PathEntryFinder - @classmethod - def _path_hook(cls, path) -> type[_EditableNamespaceFinder]: - if path == PATH_PLACEHOLDER: - return cls - raise ImportError - - @classmethod - def _paths(cls, fullname: str) -> list[str]: - paths = NAMESPACES[fullname] - if not paths and fullname in MAPPING: - paths = [MAPPING[fullname]] - # Always add placeholder, for 2 reasons: - # 1. __path__ cannot be empty for the spec to be considered namespace. - # 2. In the case of nested namespaces, we need to force - # import machinery to query _EditableNamespaceFinder again. - return [*paths, PATH_PLACEHOLDER] - - @classmethod - def find_spec(cls, fullname: str, target=None) -> ModuleSpec | None: # type: ignore - if fullname in NAMESPACES: - spec = ModuleSpec(fullname, None, is_package=True) - spec.submodule_search_locations = cls._paths(fullname) - return spec - return None - - @classmethod - def find_module(cls, _fullname) -> None: - return None - - -def install(): - if not any(finder == _EditableFinder for finder in sys.meta_path): - sys.meta_path.append(_EditableFinder) - - if not NAMESPACES: - return - - if not any(hook == _EditableNamespaceFinder._path_hook for hook in sys.path_hooks): - # PathEntryFinder is needed to create NamespaceSpec without private APIS - sys.path_hooks.append(_EditableNamespaceFinder._path_hook) - if PATH_PLACEHOLDER not in sys.path: - sys.path.append(PATH_PLACEHOLDER) # Used just to trigger the path hook diff --git a/.venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-darwin.so b/.venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-darwin.so deleted file mode 100755 index df8f10e0..00000000 Binary files a/.venv/lib/python3.12/site-packages/_cffi_backend.cpython-312-darwin.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/_pytest/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/__init__.py deleted file mode 100644 index 8eb8ec96..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - - -__all__ = ["__version__", "version_tuple"] - -try: - from ._version import version as __version__ - from ._version import version_tuple -except ImportError: # pragma: no cover - # broken installation, we don't even try - # unknown only works because we do poor mans version compare - __version__ = "unknown" - version_tuple = (0, 0, "unknown") diff --git a/.venv/lib/python3.12/site-packages/_pytest/_argcomplete.py b/.venv/lib/python3.12/site-packages/_pytest/_argcomplete.py deleted file mode 100644 index 59426ef9..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_argcomplete.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Allow bash-completion for argparse with argcomplete if installed. - -Needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail -to find the magic string, so _ARGCOMPLETE env. var is never set, and -this does not need special code). - -Function try_argcomplete(parser) should be called directly before -the call to ArgumentParser.parse_args(). - -The filescompleter is what you normally would use on the positional -arguments specification, in order to get "dirname/" after "dirn" -instead of the default "dirname ": - - optparser.add_argument(Config._file_or_dir, nargs='*').completer=filescompleter - -Other, application specific, completers should go in the file -doing the add_argument calls as they need to be specified as .completer -attributes as well. (If argcomplete is not installed, the function the -attribute points to will not be used). - -SPEEDUP -======= - -The generic argcomplete script for bash-completion -(/etc/bash_completion.d/python-argcomplete.sh) -uses a python program to determine startup script generated by pip. -You can speed up completion somewhat by changing this script to include - # PYTHON_ARGCOMPLETE_OK -so the python-argcomplete-check-easy-install-script does not -need to be called to find the entry point of the code and see if that is -marked with PYTHON_ARGCOMPLETE_OK. - -INSTALL/DEBUGGING -================= - -To include this support in another application that has setup.py generated -scripts: - -- Add the line: - # PYTHON_ARGCOMPLETE_OK - near the top of the main python entry point. - -- Include in the file calling parse_args(): - from _argcomplete import try_argcomplete, filescompleter - Call try_argcomplete just before parse_args(), and optionally add - filescompleter to the positional arguments' add_argument(). - -If things do not work right away: - -- Switch on argcomplete debugging with (also helpful when doing custom - completers): - export _ARC_DEBUG=1 - -- Run: - python-argcomplete-check-easy-install-script $(which appname) - echo $? - will echo 0 if the magic line has been found, 1 if not. - -- Sometimes it helps to find early on errors using: - _ARGCOMPLETE=1 _ARC_DEBUG=1 appname - which should throw a KeyError: 'COMPLINE' (which is properly set by the - global argcomplete script). -""" - -from __future__ import annotations - -import argparse -from glob import glob -import os -import sys -from typing import Any - - -class FastFilesCompleter: - """Fast file completer class.""" - - def __init__(self, directories: bool = True) -> None: - self.directories = directories - - def __call__(self, prefix: str, **kwargs: Any) -> list[str]: - # Only called on non option completions. - if os.sep in prefix[1:]: - prefix_dir = len(os.path.dirname(prefix) + os.sep) - else: - prefix_dir = 0 - completion = [] - globbed = [] - if "*" not in prefix and "?" not in prefix: - # We are on unix, otherwise no bash. - if not prefix or prefix[-1] == os.sep: - globbed.extend(glob(prefix + ".*")) - prefix += "*" - globbed.extend(glob(prefix)) - for x in sorted(globbed): - if os.path.isdir(x): - x += "/" - # Append stripping the prefix (like bash, not like compgen). - completion.append(x[prefix_dir:]) - return completion - - -if os.environ.get("_ARGCOMPLETE"): - try: - import argcomplete.completers - except ImportError: - sys.exit(-1) - filescompleter: FastFilesCompleter | None = FastFilesCompleter() - - def try_argcomplete(parser: argparse.ArgumentParser) -> None: - argcomplete.autocomplete(parser, always_complete_options=False) - -else: - - def try_argcomplete(parser: argparse.ArgumentParser) -> None: - pass - - filescompleter = None diff --git a/.venv/lib/python3.12/site-packages/_pytest/_code/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/_code/__init__.py deleted file mode 100644 index 0bfde426..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_code/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Python inspection/code generation API.""" - -from __future__ import annotations - -from .code import Code -from .code import ExceptionInfo -from .code import filter_traceback -from .code import Frame -from .code import getfslineno -from .code import Traceback -from .code import TracebackEntry -from .source import getrawcode -from .source import Source - - -__all__ = [ - "Code", - "ExceptionInfo", - "filter_traceback", - "Frame", - "getfslineno", - "getrawcode", - "Traceback", - "TracebackEntry", - "Source", -] diff --git a/.venv/lib/python3.12/site-packages/_pytest/_code/code.py b/.venv/lib/python3.12/site-packages/_pytest/_code/code.py deleted file mode 100644 index fec627b3..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_code/code.py +++ /dev/null @@ -1,1409 +0,0 @@ -# mypy: allow-untyped-defs -from __future__ import annotations - -import ast -import dataclasses -import inspect -from inspect import CO_VARARGS -from inspect import CO_VARKEYWORDS -from io import StringIO -import os -from pathlib import Path -import re -import sys -import traceback -from traceback import format_exception_only -from types import CodeType -from types import FrameType -from types import TracebackType -from typing import Any -from typing import Callable -from typing import ClassVar -from typing import Final -from typing import final -from typing import Generic -from typing import Iterable -from typing import List -from typing import Literal -from typing import Mapping -from typing import overload -from typing import Pattern -from typing import Sequence -from typing import SupportsIndex -from typing import Tuple -from typing import Type -from typing import TypeVar -from typing import Union - -import pluggy - -import _pytest -from _pytest._code.source import findsource -from _pytest._code.source import getrawcode -from _pytest._code.source import getstatementrange_ast -from _pytest._code.source import Source -from _pytest._io import TerminalWriter -from _pytest._io.saferepr import safeformat -from _pytest._io.saferepr import saferepr -from _pytest.compat import get_real_func -from _pytest.deprecated import check_ispytest -from _pytest.pathlib import absolutepath -from _pytest.pathlib import bestrelpath - - -if sys.version_info < (3, 11): - from exceptiongroup import BaseExceptionGroup - -TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"] - -EXCEPTION_OR_MORE = Union[Type[BaseException], Tuple[Type[BaseException], ...]] - - -class Code: - """Wrapper around Python code objects.""" - - __slots__ = ("raw",) - - def __init__(self, obj: CodeType) -> None: - self.raw = obj - - @classmethod - def from_function(cls, obj: object) -> Code: - return cls(getrawcode(obj)) - - def __eq__(self, other): - return self.raw == other.raw - - # Ignore type because of https://github.com/python/mypy/issues/4266. - __hash__ = None # type: ignore - - @property - def firstlineno(self) -> int: - return self.raw.co_firstlineno - 1 - - @property - def name(self) -> str: - return self.raw.co_name - - @property - def path(self) -> Path | str: - """Return a path object pointing to source code, or an ``str`` in - case of ``OSError`` / non-existing file.""" - if not self.raw.co_filename: - return "" - try: - p = absolutepath(self.raw.co_filename) - # maybe don't try this checking - if not p.exists(): - raise OSError("path check failed.") - return p - except OSError: - # XXX maybe try harder like the weird logic - # in the standard lib [linecache.updatecache] does? - return self.raw.co_filename - - @property - def fullsource(self) -> Source | None: - """Return a _pytest._code.Source object for the full source file of the code.""" - full, _ = findsource(self.raw) - return full - - def source(self) -> Source: - """Return a _pytest._code.Source object for the code object's source only.""" - # return source only for that part of code - return Source(self.raw) - - def getargs(self, var: bool = False) -> tuple[str, ...]: - """Return a tuple with the argument names for the code object. - - If 'var' is set True also return the names of the variable and - keyword arguments when present. - """ - # Handy shortcut for getting args. - raw = self.raw - argcount = raw.co_argcount - if var: - argcount += raw.co_flags & CO_VARARGS - argcount += raw.co_flags & CO_VARKEYWORDS - return raw.co_varnames[:argcount] - - -class Frame: - """Wrapper around a Python frame holding f_locals and f_globals - in which expressions can be evaluated.""" - - __slots__ = ("raw",) - - def __init__(self, frame: FrameType) -> None: - self.raw = frame - - @property - def lineno(self) -> int: - return self.raw.f_lineno - 1 - - @property - def f_globals(self) -> dict[str, Any]: - return self.raw.f_globals - - @property - def f_locals(self) -> dict[str, Any]: - return self.raw.f_locals - - @property - def code(self) -> Code: - return Code(self.raw.f_code) - - @property - def statement(self) -> Source: - """Statement this frame is at.""" - if self.code.fullsource is None: - return Source("") - return self.code.fullsource.getstatement(self.lineno) - - def eval(self, code, **vars): - """Evaluate 'code' in the frame. - - 'vars' are optional additional local variables. - - Returns the result of the evaluation. - """ - f_locals = self.f_locals.copy() - f_locals.update(vars) - return eval(code, self.f_globals, f_locals) - - def repr(self, object: object) -> str: - """Return a 'safe' (non-recursive, one-line) string repr for 'object'.""" - return saferepr(object) - - def getargs(self, var: bool = False): - """Return a list of tuples (name, value) for all arguments. - - If 'var' is set True, also include the variable and keyword arguments - when present. - """ - retval = [] - for arg in self.code.getargs(var): - try: - retval.append((arg, self.f_locals[arg])) - except KeyError: - pass # this can occur when using Psyco - return retval - - -class TracebackEntry: - """A single entry in a Traceback.""" - - __slots__ = ("_rawentry", "_repr_style") - - def __init__( - self, - rawentry: TracebackType, - repr_style: Literal["short", "long"] | None = None, - ) -> None: - self._rawentry: Final = rawentry - self._repr_style: Final = repr_style - - def with_repr_style( - self, repr_style: Literal["short", "long"] | None - ) -> TracebackEntry: - return TracebackEntry(self._rawentry, repr_style) - - @property - def lineno(self) -> int: - return self._rawentry.tb_lineno - 1 - - @property - def frame(self) -> Frame: - return Frame(self._rawentry.tb_frame) - - @property - def relline(self) -> int: - return self.lineno - self.frame.code.firstlineno - - def __repr__(self) -> str: - return "" % (self.frame.code.path, self.lineno + 1) - - @property - def statement(self) -> Source: - """_pytest._code.Source object for the current statement.""" - source = self.frame.code.fullsource - assert source is not None - return source.getstatement(self.lineno) - - @property - def path(self) -> Path | str: - """Path to the source code.""" - return self.frame.code.path - - @property - def locals(self) -> dict[str, Any]: - """Locals of underlying frame.""" - return self.frame.f_locals - - def getfirstlinesource(self) -> int: - return self.frame.code.firstlineno - - def getsource( - self, astcache: dict[str | Path, ast.AST] | None = None - ) -> Source | None: - """Return failing source code.""" - # we use the passed in astcache to not reparse asttrees - # within exception info printing - source = self.frame.code.fullsource - if source is None: - return None - key = astnode = None - if astcache is not None: - key = self.frame.code.path - if key is not None: - astnode = astcache.get(key, None) - start = self.getfirstlinesource() - try: - astnode, _, end = getstatementrange_ast( - self.lineno, source, astnode=astnode - ) - except SyntaxError: - end = self.lineno + 1 - else: - if key is not None and astcache is not None: - astcache[key] = astnode - return source[start:end] - - source = property(getsource) - - def ishidden(self, excinfo: ExceptionInfo[BaseException] | None) -> bool: - """Return True if the current frame has a var __tracebackhide__ - resolving to True. - - If __tracebackhide__ is a callable, it gets called with the - ExceptionInfo instance and can decide whether to hide the traceback. - - Mostly for internal use. - """ - tbh: bool | Callable[[ExceptionInfo[BaseException] | None], bool] = False - for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals): - # in normal cases, f_locals and f_globals are dictionaries - # however via `exec(...)` / `eval(...)` they can be other types - # (even incorrect types!). - # as such, we suppress all exceptions while accessing __tracebackhide__ - try: - tbh = maybe_ns_dct["__tracebackhide__"] - except Exception: - pass - else: - break - if tbh and callable(tbh): - return tbh(excinfo) - return tbh - - def __str__(self) -> str: - name = self.frame.code.name - try: - line = str(self.statement).lstrip() - except KeyboardInterrupt: - raise - except BaseException: - line = "???" - # This output does not quite match Python's repr for traceback entries, - # but changing it to do so would break certain plugins. See - # https://github.com/pytest-dev/pytest/pull/7535/ for details. - return " File %r:%d in %s\n %s\n" % ( - str(self.path), - self.lineno + 1, - name, - line, - ) - - @property - def name(self) -> str: - """co_name of underlying code.""" - return self.frame.code.raw.co_name - - -class Traceback(List[TracebackEntry]): - """Traceback objects encapsulate and offer higher level access to Traceback entries.""" - - def __init__( - self, - tb: TracebackType | Iterable[TracebackEntry], - ) -> None: - """Initialize from given python traceback object and ExceptionInfo.""" - if isinstance(tb, TracebackType): - - def f(cur: TracebackType) -> Iterable[TracebackEntry]: - cur_: TracebackType | None = cur - while cur_ is not None: - yield TracebackEntry(cur_) - cur_ = cur_.tb_next - - super().__init__(f(tb)) - else: - super().__init__(tb) - - def cut( - self, - path: os.PathLike[str] | str | None = None, - lineno: int | None = None, - firstlineno: int | None = None, - excludepath: os.PathLike[str] | None = None, - ) -> Traceback: - """Return a Traceback instance wrapping part of this Traceback. - - By providing any combination of path, lineno and firstlineno, the - first frame to start the to-be-returned traceback is determined. - - This allows cutting the first part of a Traceback instance e.g. - for formatting reasons (removing some uninteresting bits that deal - with handling of the exception/traceback). - """ - path_ = None if path is None else os.fspath(path) - excludepath_ = None if excludepath is None else os.fspath(excludepath) - for x in self: - code = x.frame.code - codepath = code.path - if path is not None and str(codepath) != path_: - continue - if ( - excludepath is not None - and isinstance(codepath, Path) - and excludepath_ in (str(p) for p in codepath.parents) # type: ignore[operator] - ): - continue - if lineno is not None and x.lineno != lineno: - continue - if firstlineno is not None and x.frame.code.firstlineno != firstlineno: - continue - return Traceback(x._rawentry) - return self - - @overload - def __getitem__(self, key: SupportsIndex) -> TracebackEntry: ... - - @overload - def __getitem__(self, key: slice) -> Traceback: ... - - def __getitem__(self, key: SupportsIndex | slice) -> TracebackEntry | Traceback: - if isinstance(key, slice): - return self.__class__(super().__getitem__(key)) - else: - return super().__getitem__(key) - - def filter( - self, - excinfo_or_fn: ExceptionInfo[BaseException] | Callable[[TracebackEntry], bool], - /, - ) -> Traceback: - """Return a Traceback instance with certain items removed. - - If the filter is an `ExceptionInfo`, removes all the ``TracebackEntry``s - which are hidden (see ishidden() above). - - Otherwise, the filter is a function that gets a single argument, a - ``TracebackEntry`` instance, and should return True when the item should - be added to the ``Traceback``, False when not. - """ - if isinstance(excinfo_or_fn, ExceptionInfo): - fn = lambda x: not x.ishidden(excinfo_or_fn) # noqa: E731 - else: - fn = excinfo_or_fn - return Traceback(filter(fn, self)) - - def recursionindex(self) -> int | None: - """Return the index of the frame/TracebackEntry where recursion originates if - appropriate, None if no recursion occurred.""" - cache: dict[tuple[Any, int, int], list[dict[str, Any]]] = {} - for i, entry in enumerate(self): - # id for the code.raw is needed to work around - # the strange metaprogramming in the decorator lib from pypi - # which generates code objects that have hash/value equality - # XXX needs a test - key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno - values = cache.setdefault(key, []) - # Since Python 3.13 f_locals is a proxy, freeze it. - loc = dict(entry.frame.f_locals) - if values: - for otherloc in values: - if otherloc == loc: - return i - values.append(loc) - return None - - -E = TypeVar("E", bound=BaseException, covariant=True) - - -@final -@dataclasses.dataclass -class ExceptionInfo(Generic[E]): - """Wraps sys.exc_info() objects and offers help for navigating the traceback.""" - - _assert_start_repr: ClassVar = "AssertionError('assert " - - _excinfo: tuple[type[E], E, TracebackType] | None - _striptext: str - _traceback: Traceback | None - - def __init__( - self, - excinfo: tuple[type[E], E, TracebackType] | None, - striptext: str = "", - traceback: Traceback | None = None, - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - self._excinfo = excinfo - self._striptext = striptext - self._traceback = traceback - - @classmethod - def from_exception( - cls, - # Ignoring error: "Cannot use a covariant type variable as a parameter". - # This is OK to ignore because this class is (conceptually) readonly. - # See https://github.com/python/mypy/issues/7049. - exception: E, # type: ignore[misc] - exprinfo: str | None = None, - ) -> ExceptionInfo[E]: - """Return an ExceptionInfo for an existing exception. - - The exception must have a non-``None`` ``__traceback__`` attribute, - otherwise this function fails with an assertion error. This means that - the exception must have been raised, or added a traceback with the - :py:meth:`~BaseException.with_traceback()` method. - - :param exprinfo: - A text string helping to determine if we should strip - ``AssertionError`` from the output. Defaults to the exception - message/``__str__()``. - - .. versionadded:: 7.4 - """ - assert exception.__traceback__, ( - "Exceptions passed to ExcInfo.from_exception(...)" - " must have a non-None __traceback__." - ) - exc_info = (type(exception), exception, exception.__traceback__) - return cls.from_exc_info(exc_info, exprinfo) - - @classmethod - def from_exc_info( - cls, - exc_info: tuple[type[E], E, TracebackType], - exprinfo: str | None = None, - ) -> ExceptionInfo[E]: - """Like :func:`from_exception`, but using old-style exc_info tuple.""" - _striptext = "" - if exprinfo is None and isinstance(exc_info[1], AssertionError): - exprinfo = getattr(exc_info[1], "msg", None) - if exprinfo is None: - exprinfo = saferepr(exc_info[1]) - if exprinfo and exprinfo.startswith(cls._assert_start_repr): - _striptext = "AssertionError: " - - return cls(exc_info, _striptext, _ispytest=True) - - @classmethod - def from_current(cls, exprinfo: str | None = None) -> ExceptionInfo[BaseException]: - """Return an ExceptionInfo matching the current traceback. - - .. warning:: - - Experimental API - - :param exprinfo: - A text string helping to determine if we should strip - ``AssertionError`` from the output. Defaults to the exception - message/``__str__()``. - """ - tup = sys.exc_info() - assert tup[0] is not None, "no current exception" - assert tup[1] is not None, "no current exception" - assert tup[2] is not None, "no current exception" - exc_info = (tup[0], tup[1], tup[2]) - return ExceptionInfo.from_exc_info(exc_info, exprinfo) - - @classmethod - def for_later(cls) -> ExceptionInfo[E]: - """Return an unfilled ExceptionInfo.""" - return cls(None, _ispytest=True) - - def fill_unfilled(self, exc_info: tuple[type[E], E, TracebackType]) -> None: - """Fill an unfilled ExceptionInfo created with ``for_later()``.""" - assert self._excinfo is None, "ExceptionInfo was already filled" - self._excinfo = exc_info - - @property - def type(self) -> type[E]: - """The exception class.""" - assert ( - self._excinfo is not None - ), ".type can only be used after the context manager exits" - return self._excinfo[0] - - @property - def value(self) -> E: - """The exception value.""" - assert ( - self._excinfo is not None - ), ".value can only be used after the context manager exits" - return self._excinfo[1] - - @property - def tb(self) -> TracebackType: - """The exception raw traceback.""" - assert ( - self._excinfo is not None - ), ".tb can only be used after the context manager exits" - return self._excinfo[2] - - @property - def typename(self) -> str: - """The type name of the exception.""" - assert ( - self._excinfo is not None - ), ".typename can only be used after the context manager exits" - return self.type.__name__ - - @property - def traceback(self) -> Traceback: - """The traceback.""" - if self._traceback is None: - self._traceback = Traceback(self.tb) - return self._traceback - - @traceback.setter - def traceback(self, value: Traceback) -> None: - self._traceback = value - - def __repr__(self) -> str: - if self._excinfo is None: - return "" - return f"<{self.__class__.__name__} {saferepr(self._excinfo[1])} tblen={len(self.traceback)}>" - - def exconly(self, tryshort: bool = False) -> str: - """Return the exception as a string. - - When 'tryshort' resolves to True, and the exception is an - AssertionError, only the actual exception part of the exception - representation is returned (so 'AssertionError: ' is removed from - the beginning). - """ - lines = format_exception_only(self.type, self.value) - text = "".join(lines) - text = text.rstrip() - if tryshort: - if text.startswith(self._striptext): - text = text[len(self._striptext) :] - return text - - def errisinstance(self, exc: EXCEPTION_OR_MORE) -> bool: - """Return True if the exception is an instance of exc. - - Consider using ``isinstance(excinfo.value, exc)`` instead. - """ - return isinstance(self.value, exc) - - def _getreprcrash(self) -> ReprFileLocation | None: - # Find last non-hidden traceback entry that led to the exception of the - # traceback, or None if all hidden. - for i in range(-1, -len(self.traceback) - 1, -1): - entry = self.traceback[i] - if not entry.ishidden(self): - path, lineno = entry.frame.code.raw.co_filename, entry.lineno - exconly = self.exconly(tryshort=True) - return ReprFileLocation(path, lineno + 1, exconly) - return None - - def getrepr( - self, - showlocals: bool = False, - style: TracebackStyle = "long", - abspath: bool = False, - tbfilter: bool - | Callable[[ExceptionInfo[BaseException]], _pytest._code.code.Traceback] = True, - funcargs: bool = False, - truncate_locals: bool = True, - truncate_args: bool = True, - chain: bool = True, - ) -> ReprExceptionInfo | ExceptionChainRepr: - """Return str()able representation of this exception info. - - :param bool showlocals: - Show locals per traceback entry. - Ignored if ``style=="native"``. - - :param str style: - long|short|line|no|native|value traceback style. - - :param bool abspath: - If paths should be changed to absolute or left unchanged. - - :param tbfilter: - A filter for traceback entries. - - * If false, don't hide any entries. - * If true, hide internal entries and entries that contain a local - variable ``__tracebackhide__ = True``. - * If a callable, delegates the filtering to the callable. - - Ignored if ``style`` is ``"native"``. - - :param bool funcargs: - Show fixtures ("funcargs" for legacy purposes) per traceback entry. - - :param bool truncate_locals: - With ``showlocals==True``, make sure locals can be safely represented as strings. - - :param bool truncate_args: - With ``showargs==True``, make sure args can be safely represented as strings. - - :param bool chain: - If chained exceptions in Python 3 should be shown. - - .. versionchanged:: 3.9 - - Added the ``chain`` parameter. - """ - if style == "native": - return ReprExceptionInfo( - reprtraceback=ReprTracebackNative( - traceback.format_exception( - self.type, - self.value, - self.traceback[0]._rawentry if self.traceback else None, - ) - ), - reprcrash=self._getreprcrash(), - ) - - fmt = FormattedExcinfo( - showlocals=showlocals, - style=style, - abspath=abspath, - tbfilter=tbfilter, - funcargs=funcargs, - truncate_locals=truncate_locals, - truncate_args=truncate_args, - chain=chain, - ) - return fmt.repr_excinfo(self) - - def _stringify_exception(self, exc: BaseException) -> str: - try: - notes = getattr(exc, "__notes__", []) - except KeyError: - # Workaround for https://github.com/python/cpython/issues/98778 on - # Python <= 3.9, and some 3.10 and 3.11 patch versions. - HTTPError = getattr(sys.modules.get("urllib.error", None), "HTTPError", ()) - if sys.version_info < (3, 12) and isinstance(exc, HTTPError): - notes = [] - else: - raise - - return "\n".join( - [ - str(exc), - *notes, - ] - ) - - def match(self, regexp: str | Pattern[str]) -> Literal[True]: - """Check whether the regular expression `regexp` matches the string - representation of the exception using :func:`python:re.search`. - - If it matches `True` is returned, otherwise an `AssertionError` is raised. - """ - __tracebackhide__ = True - value = self._stringify_exception(self.value) - msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}" - if regexp == value: - msg += "\n Did you mean to `re.escape()` the regex?" - assert re.search(regexp, value), msg - # Return True to allow for "assert excinfo.match()". - return True - - def _group_contains( - self, - exc_group: BaseExceptionGroup[BaseException], - expected_exception: EXCEPTION_OR_MORE, - match: str | Pattern[str] | None, - target_depth: int | None = None, - current_depth: int = 1, - ) -> bool: - """Return `True` if a `BaseExceptionGroup` contains a matching exception.""" - if (target_depth is not None) and (current_depth > target_depth): - # already descended past the target depth - return False - for exc in exc_group.exceptions: - if isinstance(exc, BaseExceptionGroup): - if self._group_contains( - exc, expected_exception, match, target_depth, current_depth + 1 - ): - return True - if (target_depth is not None) and (current_depth != target_depth): - # not at the target depth, no match - continue - if not isinstance(exc, expected_exception): - continue - if match is not None: - value = self._stringify_exception(exc) - if not re.search(match, value): - continue - return True - return False - - def group_contains( - self, - expected_exception: EXCEPTION_OR_MORE, - *, - match: str | Pattern[str] | None = None, - depth: int | None = None, - ) -> bool: - """Check whether a captured exception group contains a matching exception. - - :param Type[BaseException] | Tuple[Type[BaseException]] expected_exception: - The expected exception type, or a tuple if one of multiple possible - exception types are expected. - - :param str | Pattern[str] | None match: - If specified, a string containing a regular expression, - or a regular expression object, that is tested against the string - representation of the exception and its `PEP-678 ` `__notes__` - using :func:`re.search`. - - To match a literal string that may contain :ref:`special characters - `, the pattern can first be escaped with :func:`re.escape`. - - :param Optional[int] depth: - If `None`, will search for a matching exception at any nesting depth. - If >= 1, will only match an exception if it's at the specified depth (depth = 1 being - the exceptions contained within the topmost exception group). - - .. versionadded:: 8.0 - """ - msg = "Captured exception is not an instance of `BaseExceptionGroup`" - assert isinstance(self.value, BaseExceptionGroup), msg - msg = "`depth` must be >= 1 if specified" - assert (depth is None) or (depth >= 1), msg - return self._group_contains(self.value, expected_exception, match, depth) - - -@dataclasses.dataclass -class FormattedExcinfo: - """Presenting information about failing Functions and Generators.""" - - # for traceback entries - flow_marker: ClassVar = ">" - fail_marker: ClassVar = "E" - - showlocals: bool = False - style: TracebackStyle = "long" - abspath: bool = True - tbfilter: bool | Callable[[ExceptionInfo[BaseException]], Traceback] = True - funcargs: bool = False - truncate_locals: bool = True - truncate_args: bool = True - chain: bool = True - astcache: dict[str | Path, ast.AST] = dataclasses.field( - default_factory=dict, init=False, repr=False - ) - - def _getindent(self, source: Source) -> int: - # Figure out indent for the given source. - try: - s = str(source.getstatement(len(source) - 1)) - except KeyboardInterrupt: - raise - except BaseException: - try: - s = str(source[-1]) - except KeyboardInterrupt: - raise - except BaseException: - return 0 - return 4 + (len(s) - len(s.lstrip())) - - def _getentrysource(self, entry: TracebackEntry) -> Source | None: - source = entry.getsource(self.astcache) - if source is not None: - source = source.deindent() - return source - - def repr_args(self, entry: TracebackEntry) -> ReprFuncArgs | None: - if self.funcargs: - args = [] - for argname, argvalue in entry.frame.getargs(var=True): - if self.truncate_args: - str_repr = saferepr(argvalue) - else: - str_repr = saferepr(argvalue, maxsize=None) - args.append((argname, str_repr)) - return ReprFuncArgs(args) - return None - - def get_source( - self, - source: Source | None, - line_index: int = -1, - excinfo: ExceptionInfo[BaseException] | None = None, - short: bool = False, - ) -> list[str]: - """Return formatted and marked up source lines.""" - lines = [] - if source is not None and line_index < 0: - line_index += len(source) - if source is None or line_index >= len(source.lines) or line_index < 0: - # `line_index` could still be outside `range(len(source.lines))` if - # we're processing AST with pathological position attributes. - source = Source("???") - line_index = 0 - space_prefix = " " - if short: - lines.append(space_prefix + source.lines[line_index].strip()) - else: - for line in source.lines[:line_index]: - lines.append(space_prefix + line) - lines.append(self.flow_marker + " " + source.lines[line_index]) - for line in source.lines[line_index + 1 :]: - lines.append(space_prefix + line) - if excinfo is not None: - indent = 4 if short else self._getindent(source) - lines.extend(self.get_exconly(excinfo, indent=indent, markall=True)) - return lines - - def get_exconly( - self, - excinfo: ExceptionInfo[BaseException], - indent: int = 4, - markall: bool = False, - ) -> list[str]: - lines = [] - indentstr = " " * indent - # Get the real exception information out. - exlines = excinfo.exconly(tryshort=True).split("\n") - failindent = self.fail_marker + indentstr[1:] - for line in exlines: - lines.append(failindent + line) - if not markall: - failindent = indentstr - return lines - - def repr_locals(self, locals: Mapping[str, object]) -> ReprLocals | None: - if self.showlocals: - lines = [] - keys = [loc for loc in locals if loc[0] != "@"] - keys.sort() - for name in keys: - value = locals[name] - if name == "__builtins__": - lines.append("__builtins__ = ") - else: - # This formatting could all be handled by the - # _repr() function, which is only reprlib.Repr in - # disguise, so is very configurable. - if self.truncate_locals: - str_repr = saferepr(value) - else: - str_repr = safeformat(value) - # if len(str_repr) < 70 or not isinstance(value, (list, tuple, dict)): - lines.append(f"{name:<10} = {str_repr}") - # else: - # self._line("%-10s =\\" % (name,)) - # # XXX - # pprint.pprint(value, stream=self.excinfowriter) - return ReprLocals(lines) - return None - - def repr_traceback_entry( - self, - entry: TracebackEntry | None, - excinfo: ExceptionInfo[BaseException] | None = None, - ) -> ReprEntry: - lines: list[str] = [] - style = ( - entry._repr_style - if entry is not None and entry._repr_style is not None - else self.style - ) - if style in ("short", "long") and entry is not None: - source = self._getentrysource(entry) - if source is None: - source = Source("???") - line_index = 0 - else: - line_index = entry.lineno - entry.getfirstlinesource() - short = style == "short" - reprargs = self.repr_args(entry) if not short else None - s = self.get_source(source, line_index, excinfo, short=short) - lines.extend(s) - if short: - message = f"in {entry.name}" - else: - message = excinfo and excinfo.typename or "" - entry_path = entry.path - path = self._makepath(entry_path) - reprfileloc = ReprFileLocation(path, entry.lineno + 1, message) - localsrepr = self.repr_locals(entry.locals) - return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style) - elif style == "value": - if excinfo: - lines.extend(str(excinfo.value).split("\n")) - return ReprEntry(lines, None, None, None, style) - else: - if excinfo: - lines.extend(self.get_exconly(excinfo, indent=4)) - return ReprEntry(lines, None, None, None, style) - - def _makepath(self, path: Path | str) -> str: - if not self.abspath and isinstance(path, Path): - try: - np = bestrelpath(Path.cwd(), path) - except OSError: - return str(path) - if len(np) < len(str(path)): - return np - return str(path) - - def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> ReprTraceback: - traceback = excinfo.traceback - if callable(self.tbfilter): - traceback = self.tbfilter(excinfo) - elif self.tbfilter: - traceback = traceback.filter(excinfo) - - if isinstance(excinfo.value, RecursionError): - traceback, extraline = self._truncate_recursive_traceback(traceback) - else: - extraline = None - - if not traceback: - if extraline is None: - extraline = "All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames." - entries = [self.repr_traceback_entry(None, excinfo)] - return ReprTraceback(entries, extraline, style=self.style) - - last = traceback[-1] - if self.style == "value": - entries = [self.repr_traceback_entry(last, excinfo)] - return ReprTraceback(entries, None, style=self.style) - - entries = [ - self.repr_traceback_entry(entry, excinfo if last == entry else None) - for entry in traceback - ] - return ReprTraceback(entries, extraline, style=self.style) - - def _truncate_recursive_traceback( - self, traceback: Traceback - ) -> tuple[Traceback, str | None]: - """Truncate the given recursive traceback trying to find the starting - point of the recursion. - - The detection is done by going through each traceback entry and - finding the point in which the locals of the frame are equal to the - locals of a previous frame (see ``recursionindex()``). - - Handle the situation where the recursion process might raise an - exception (for example comparing numpy arrays using equality raises a - TypeError), in which case we do our best to warn the user of the - error and show a limited traceback. - """ - try: - recursionindex = traceback.recursionindex() - except Exception as e: - max_frames = 10 - extraline: str | None = ( - "!!! Recursion error detected, but an error occurred locating the origin of recursion.\n" - " The following exception happened when comparing locals in the stack frame:\n" - f" {type(e).__name__}: {e!s}\n" - f" Displaying first and last {max_frames} stack frames out of {len(traceback)}." - ) - # Type ignored because adding two instances of a List subtype - # currently incorrectly has type List instead of the subtype. - traceback = traceback[:max_frames] + traceback[-max_frames:] # type: ignore - else: - if recursionindex is not None: - extraline = "!!! Recursion detected (same locals & position)" - traceback = traceback[: recursionindex + 1] - else: - extraline = None - - return traceback, extraline - - def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainRepr: - repr_chain: list[tuple[ReprTraceback, ReprFileLocation | None, str | None]] = [] - e: BaseException | None = excinfo.value - excinfo_: ExceptionInfo[BaseException] | None = excinfo - descr = None - seen: set[int] = set() - while e is not None and id(e) not in seen: - seen.add(id(e)) - - if excinfo_: - # Fall back to native traceback as a temporary workaround until - # full support for exception groups added to ExceptionInfo. - # See https://github.com/pytest-dev/pytest/issues/9159 - if isinstance(e, BaseExceptionGroup): - reprtraceback: ReprTracebackNative | ReprTraceback = ( - ReprTracebackNative( - traceback.format_exception( - type(excinfo_.value), - excinfo_.value, - excinfo_.traceback[0]._rawentry, - ) - ) - ) - else: - reprtraceback = self.repr_traceback(excinfo_) - reprcrash = excinfo_._getreprcrash() - else: - # Fallback to native repr if the exception doesn't have a traceback: - # ExceptionInfo objects require a full traceback to work. - reprtraceback = ReprTracebackNative( - traceback.format_exception(type(e), e, None) - ) - reprcrash = None - repr_chain += [(reprtraceback, reprcrash, descr)] - - if e.__cause__ is not None and self.chain: - e = e.__cause__ - excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None - descr = "The above exception was the direct cause of the following exception:" - elif ( - e.__context__ is not None and not e.__suppress_context__ and self.chain - ): - e = e.__context__ - excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None - descr = "During handling of the above exception, another exception occurred:" - else: - e = None - repr_chain.reverse() - return ExceptionChainRepr(repr_chain) - - -@dataclasses.dataclass(eq=False) -class TerminalRepr: - def __str__(self) -> str: - # FYI this is called from pytest-xdist's serialization of exception - # information. - io = StringIO() - tw = TerminalWriter(file=io) - self.toterminal(tw) - return io.getvalue().strip() - - def __repr__(self) -> str: - return f"<{self.__class__} instance at {id(self):0x}>" - - def toterminal(self, tw: TerminalWriter) -> None: - raise NotImplementedError() - - -# This class is abstract -- only subclasses are instantiated. -@dataclasses.dataclass(eq=False) -class ExceptionRepr(TerminalRepr): - # Provided by subclasses. - reprtraceback: ReprTraceback - reprcrash: ReprFileLocation | None - sections: list[tuple[str, str, str]] = dataclasses.field( - init=False, default_factory=list - ) - - def addsection(self, name: str, content: str, sep: str = "-") -> None: - self.sections.append((name, content, sep)) - - def toterminal(self, tw: TerminalWriter) -> None: - for name, content, sep in self.sections: - tw.sep(sep, name) - tw.line(content) - - -@dataclasses.dataclass(eq=False) -class ExceptionChainRepr(ExceptionRepr): - chain: Sequence[tuple[ReprTraceback, ReprFileLocation | None, str | None]] - - def __init__( - self, - chain: Sequence[tuple[ReprTraceback, ReprFileLocation | None, str | None]], - ) -> None: - # reprcrash and reprtraceback of the outermost (the newest) exception - # in the chain. - super().__init__( - reprtraceback=chain[-1][0], - reprcrash=chain[-1][1], - ) - self.chain = chain - - def toterminal(self, tw: TerminalWriter) -> None: - for element in self.chain: - element[0].toterminal(tw) - if element[2] is not None: - tw.line("") - tw.line(element[2], yellow=True) - super().toterminal(tw) - - -@dataclasses.dataclass(eq=False) -class ReprExceptionInfo(ExceptionRepr): - reprtraceback: ReprTraceback - reprcrash: ReprFileLocation | None - - def toterminal(self, tw: TerminalWriter) -> None: - self.reprtraceback.toterminal(tw) - super().toterminal(tw) - - -@dataclasses.dataclass(eq=False) -class ReprTraceback(TerminalRepr): - reprentries: Sequence[ReprEntry | ReprEntryNative] - extraline: str | None - style: TracebackStyle - - entrysep: ClassVar = "_ " - - def toterminal(self, tw: TerminalWriter) -> None: - # The entries might have different styles. - for i, entry in enumerate(self.reprentries): - if entry.style == "long": - tw.line("") - entry.toterminal(tw) - if i < len(self.reprentries) - 1: - next_entry = self.reprentries[i + 1] - if ( - entry.style == "long" - or entry.style == "short" - and next_entry.style == "long" - ): - tw.sep(self.entrysep) - - if self.extraline: - tw.line(self.extraline) - - -class ReprTracebackNative(ReprTraceback): - def __init__(self, tblines: Sequence[str]) -> None: - self.reprentries = [ReprEntryNative(tblines)] - self.extraline = None - self.style = "native" - - -@dataclasses.dataclass(eq=False) -class ReprEntryNative(TerminalRepr): - lines: Sequence[str] - - style: ClassVar[TracebackStyle] = "native" - - def toterminal(self, tw: TerminalWriter) -> None: - tw.write("".join(self.lines)) - - -@dataclasses.dataclass(eq=False) -class ReprEntry(TerminalRepr): - lines: Sequence[str] - reprfuncargs: ReprFuncArgs | None - reprlocals: ReprLocals | None - reprfileloc: ReprFileLocation | None - style: TracebackStyle - - def _write_entry_lines(self, tw: TerminalWriter) -> None: - """Write the source code portions of a list of traceback entries with syntax highlighting. - - Usually entries are lines like these: - - " x = 1" - "> assert x == 2" - "E assert 1 == 2" - - This function takes care of rendering the "source" portions of it (the lines without - the "E" prefix) using syntax highlighting, taking care to not highlighting the ">" - character, as doing so might break line continuations. - """ - if not self.lines: - return - - if self.style == "value": - # Using tw.write instead of tw.line for testing purposes due to TWMock implementation; - # lines written with TWMock.line and TWMock._write_source cannot be distinguished - # from each other, whereas lines written with TWMock.write are marked with TWMock.WRITE - for line in self.lines: - tw.write(line) - tw.write("\n") - return - - # separate indents and source lines that are not failures: we want to - # highlight the code but not the indentation, which may contain markers - # such as "> assert 0" - fail_marker = f"{FormattedExcinfo.fail_marker} " - indent_size = len(fail_marker) - indents: list[str] = [] - source_lines: list[str] = [] - failure_lines: list[str] = [] - for index, line in enumerate(self.lines): - is_failure_line = line.startswith(fail_marker) - if is_failure_line: - # from this point on all lines are considered part of the failure - failure_lines.extend(self.lines[index:]) - break - else: - indents.append(line[:indent_size]) - source_lines.append(line[indent_size:]) - - tw._write_source(source_lines, indents) - - # failure lines are always completely red and bold - for line in failure_lines: - tw.line(line, bold=True, red=True) - - def toterminal(self, tw: TerminalWriter) -> None: - if self.style == "short": - if self.reprfileloc: - self.reprfileloc.toterminal(tw) - self._write_entry_lines(tw) - if self.reprlocals: - self.reprlocals.toterminal(tw, indent=" " * 8) - return - - if self.reprfuncargs: - self.reprfuncargs.toterminal(tw) - - self._write_entry_lines(tw) - - if self.reprlocals: - tw.line("") - self.reprlocals.toterminal(tw) - if self.reprfileloc: - if self.lines: - tw.line("") - self.reprfileloc.toterminal(tw) - - def __str__(self) -> str: - return "{}\n{}\n{}".format( - "\n".join(self.lines), self.reprlocals, self.reprfileloc - ) - - -@dataclasses.dataclass(eq=False) -class ReprFileLocation(TerminalRepr): - path: str - lineno: int - message: str - - def __post_init__(self) -> None: - self.path = str(self.path) - - def toterminal(self, tw: TerminalWriter) -> None: - # Filename and lineno output for each entry, using an output format - # that most editors understand. - msg = self.message - i = msg.find("\n") - if i != -1: - msg = msg[:i] - tw.write(self.path, bold=True, red=True) - tw.line(f":{self.lineno}: {msg}") - - -@dataclasses.dataclass(eq=False) -class ReprLocals(TerminalRepr): - lines: Sequence[str] - - def toterminal(self, tw: TerminalWriter, indent="") -> None: - for line in self.lines: - tw.line(indent + line) - - -@dataclasses.dataclass(eq=False) -class ReprFuncArgs(TerminalRepr): - args: Sequence[tuple[str, object]] - - def toterminal(self, tw: TerminalWriter) -> None: - if self.args: - linesofar = "" - for name, value in self.args: - ns = f"{name} = {value}" - if len(ns) + len(linesofar) + 2 > tw.fullwidth: - if linesofar: - tw.line(linesofar) - linesofar = ns - else: - if linesofar: - linesofar += ", " + ns - else: - linesofar = ns - if linesofar: - tw.line(linesofar) - tw.line("") - - -def getfslineno(obj: object) -> tuple[str | Path, int]: - """Return source location (path, lineno) for the given object. - - If the source cannot be determined return ("", -1). - - The line number is 0-based. - """ - # xxx let decorators etc specify a sane ordering - # NOTE: this used to be done in _pytest.compat.getfslineno, initially added - # in 6ec13a2b9. It ("place_as") appears to be something very custom. - obj = get_real_func(obj) - if hasattr(obj, "place_as"): - obj = obj.place_as - - try: - code = Code.from_function(obj) - except TypeError: - try: - fn = inspect.getsourcefile(obj) or inspect.getfile(obj) # type: ignore[arg-type] - except TypeError: - return "", -1 - - fspath = fn and absolutepath(fn) or "" - lineno = -1 - if fspath: - try: - _, lineno = findsource(obj) - except OSError: - pass - return fspath, lineno - - return code.path, code.firstlineno - - -# Relative paths that we use to filter traceback entries from appearing to the user; -# see filter_traceback. -# note: if we need to add more paths than what we have now we should probably use a list -# for better maintenance. - -_PLUGGY_DIR = Path(pluggy.__file__.rstrip("oc")) -# pluggy is either a package or a single module depending on the version -if _PLUGGY_DIR.name == "__init__.py": - _PLUGGY_DIR = _PLUGGY_DIR.parent -_PYTEST_DIR = Path(_pytest.__file__).parent - - -def filter_traceback(entry: TracebackEntry) -> bool: - """Return True if a TracebackEntry instance should be included in tracebacks. - - We hide traceback entries of: - - * dynamically generated code (no code to show up for it); - * internal traceback from pytest or its internal libraries, py and pluggy. - """ - # entry.path might sometimes return a str object when the entry - # points to dynamically generated code. - # See https://bitbucket.org/pytest-dev/py/issues/71. - raw_filename = entry.frame.code.raw.co_filename - is_generated = "<" in raw_filename and ">" in raw_filename - if is_generated: - return False - - # entry.path might point to a non-existing file, in which case it will - # also return a str object. See #1133. - p = Path(entry.path) - - parents = p.parents - if _PLUGGY_DIR in parents: - return False - if _PYTEST_DIR in parents: - return False - - return True diff --git a/.venv/lib/python3.12/site-packages/_pytest/_code/source.py b/.venv/lib/python3.12/site-packages/_pytest/_code/source.py deleted file mode 100644 index 604aff8b..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_code/source.py +++ /dev/null @@ -1,215 +0,0 @@ -# mypy: allow-untyped-defs -from __future__ import annotations - -import ast -from bisect import bisect_right -import inspect -import textwrap -import tokenize -import types -from typing import Iterable -from typing import Iterator -from typing import overload -import warnings - - -class Source: - """An immutable object holding a source code fragment. - - When using Source(...), the source lines are deindented. - """ - - def __init__(self, obj: object = None) -> None: - if not obj: - self.lines: list[str] = [] - elif isinstance(obj, Source): - self.lines = obj.lines - elif isinstance(obj, (tuple, list)): - self.lines = deindent(x.rstrip("\n") for x in obj) - elif isinstance(obj, str): - self.lines = deindent(obj.split("\n")) - else: - try: - rawcode = getrawcode(obj) - src = inspect.getsource(rawcode) - except TypeError: - src = inspect.getsource(obj) # type: ignore[arg-type] - self.lines = deindent(src.split("\n")) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Source): - return NotImplemented - return self.lines == other.lines - - # Ignore type because of https://github.com/python/mypy/issues/4266. - __hash__ = None # type: ignore - - @overload - def __getitem__(self, key: int) -> str: ... - - @overload - def __getitem__(self, key: slice) -> Source: ... - - def __getitem__(self, key: int | slice) -> str | Source: - if isinstance(key, int): - return self.lines[key] - else: - if key.step not in (None, 1): - raise IndexError("cannot slice a Source with a step") - newsource = Source() - newsource.lines = self.lines[key.start : key.stop] - return newsource - - def __iter__(self) -> Iterator[str]: - return iter(self.lines) - - def __len__(self) -> int: - return len(self.lines) - - def strip(self) -> Source: - """Return new Source object with trailing and leading blank lines removed.""" - start, end = 0, len(self) - while start < end and not self.lines[start].strip(): - start += 1 - while end > start and not self.lines[end - 1].strip(): - end -= 1 - source = Source() - source.lines[:] = self.lines[start:end] - return source - - def indent(self, indent: str = " " * 4) -> Source: - """Return a copy of the source object with all lines indented by the - given indent-string.""" - newsource = Source() - newsource.lines = [(indent + line) for line in self.lines] - return newsource - - def getstatement(self, lineno: int) -> Source: - """Return Source statement which contains the given linenumber - (counted from 0).""" - start, end = self.getstatementrange(lineno) - return self[start:end] - - def getstatementrange(self, lineno: int) -> tuple[int, int]: - """Return (start, end) tuple which spans the minimal statement region - which containing the given lineno.""" - if not (0 <= lineno < len(self)): - raise IndexError("lineno out of range") - ast, start, end = getstatementrange_ast(lineno, self) - return start, end - - def deindent(self) -> Source: - """Return a new Source object deindented.""" - newsource = Source() - newsource.lines[:] = deindent(self.lines) - return newsource - - def __str__(self) -> str: - return "\n".join(self.lines) - - -# -# helper functions -# - - -def findsource(obj) -> tuple[Source | None, int]: - try: - sourcelines, lineno = inspect.findsource(obj) - except Exception: - return None, -1 - source = Source() - source.lines = [line.rstrip() for line in sourcelines] - return source, lineno - - -def getrawcode(obj: object, trycall: bool = True) -> types.CodeType: - """Return code object for given function.""" - try: - return obj.__code__ # type: ignore[attr-defined,no-any-return] - except AttributeError: - pass - if trycall: - call = getattr(obj, "__call__", None) - if call and not isinstance(obj, type): - return getrawcode(call, trycall=False) - raise TypeError(f"could not get code object for {obj!r}") - - -def deindent(lines: Iterable[str]) -> list[str]: - return textwrap.dedent("\n".join(lines)).splitlines() - - -def get_statement_startend2(lineno: int, node: ast.AST) -> tuple[int, int | None]: - # Flatten all statements and except handlers into one lineno-list. - # AST's line numbers start indexing at 1. - values: list[int] = [] - for x in ast.walk(node): - if isinstance(x, (ast.stmt, ast.ExceptHandler)): - # The lineno points to the class/def, so need to include the decorators. - if isinstance(x, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): - for d in x.decorator_list: - values.append(d.lineno - 1) - values.append(x.lineno - 1) - for name in ("finalbody", "orelse"): - val: list[ast.stmt] | None = getattr(x, name, None) - if val: - # Treat the finally/orelse part as its own statement. - values.append(val[0].lineno - 1 - 1) - values.sort() - insert_index = bisect_right(values, lineno) - start = values[insert_index - 1] - if insert_index >= len(values): - end = None - else: - end = values[insert_index] - return start, end - - -def getstatementrange_ast( - lineno: int, - source: Source, - assertion: bool = False, - astnode: ast.AST | None = None, -) -> tuple[ast.AST, int, int]: - if astnode is None: - content = str(source) - # See #4260: - # Don't produce duplicate warnings when compiling source to find AST. - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - astnode = ast.parse(content, "source", "exec") - - start, end = get_statement_startend2(lineno, astnode) - # We need to correct the end: - # - ast-parsing strips comments - # - there might be empty lines - # - we might have lesser indented code blocks at the end - if end is None: - end = len(source.lines) - - if end > start + 1: - # Make sure we don't span differently indented code blocks - # by using the BlockFinder helper used which inspect.getsource() uses itself. - block_finder = inspect.BlockFinder() - # If we start with an indented line, put blockfinder to "started" mode. - block_finder.started = ( - bool(source.lines[start]) and source.lines[start][0].isspace() - ) - it = ((x + "\n") for x in source.lines[start:end]) - try: - for tok in tokenize.generate_tokens(lambda: next(it)): - block_finder.tokeneater(*tok) - except (inspect.EndOfBlock, IndentationError): - end = block_finder.last + start - except Exception: - pass - - # The end might still point to a comment or empty line, correct it. - while end: - line = source.lines[end - 1].lstrip() - if line.startswith("#") or not line: - end -= 1 - else: - break - return astnode, start, end diff --git a/.venv/lib/python3.12/site-packages/_pytest/_io/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/_io/__init__.py deleted file mode 100644 index b0155b18..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_io/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import annotations - -from .terminalwriter import get_terminal_width -from .terminalwriter import TerminalWriter - - -__all__ = [ - "TerminalWriter", - "get_terminal_width", -] diff --git a/.venv/lib/python3.12/site-packages/_pytest/_io/pprint.py b/.venv/lib/python3.12/site-packages/_pytest/_io/pprint.py deleted file mode 100644 index fc29989b..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_io/pprint.py +++ /dev/null @@ -1,673 +0,0 @@ -# mypy: allow-untyped-defs -# This module was imported from the cpython standard library -# (https://github.com/python/cpython/) at commit -# c5140945c723ae6c4b7ee81ff720ac8ea4b52cfd (python3.12). -# -# -# Original Author: Fred L. Drake, Jr. -# fdrake@acm.org -# -# This is a simple little module I wrote to make life easier. I didn't -# see anything quite like it in the library, though I may have overlooked -# something. I wrote this when I was trying to read some heavily nested -# tuples with fairly non-descriptive content. This is modeled very much -# after Lisp/Scheme - style pretty-printing of lists. If you find it -# useful, thank small children who sleep at night. -from __future__ import annotations - -import collections as _collections -import dataclasses as _dataclasses -from io import StringIO as _StringIO -import re -import types as _types -from typing import Any -from typing import Callable -from typing import IO -from typing import Iterator - - -class _safe_key: - """Helper function for key functions when sorting unorderable objects. - - The wrapped-object will fallback to a Py2.x style comparison for - unorderable types (sorting first comparing the type name and then by - the obj ids). Does not work recursively, so dict.items() must have - _safe_key applied to both the key and the value. - - """ - - __slots__ = ["obj"] - - def __init__(self, obj): - self.obj = obj - - def __lt__(self, other): - try: - return self.obj < other.obj - except TypeError: - return (str(type(self.obj)), id(self.obj)) < ( - str(type(other.obj)), - id(other.obj), - ) - - -def _safe_tuple(t): - """Helper function for comparing 2-tuples""" - return _safe_key(t[0]), _safe_key(t[1]) - - -class PrettyPrinter: - def __init__( - self, - indent: int = 4, - width: int = 80, - depth: int | None = None, - ) -> None: - """Handle pretty printing operations onto a stream using a set of - configured parameters. - - indent - Number of spaces to indent for each level of nesting. - - width - Attempted maximum number of columns in the output. - - depth - The maximum depth to print out nested structures. - - """ - if indent < 0: - raise ValueError("indent must be >= 0") - if depth is not None and depth <= 0: - raise ValueError("depth must be > 0") - if not width: - raise ValueError("width must be != 0") - self._depth = depth - self._indent_per_level = indent - self._width = width - - def pformat(self, object: Any) -> str: - sio = _StringIO() - self._format(object, sio, 0, 0, set(), 0) - return sio.getvalue() - - def _format( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - objid = id(object) - if objid in context: - stream.write(_recursion(object)) - return - - p = self._dispatch.get(type(object).__repr__, None) - if p is not None: - context.add(objid) - p(self, object, stream, indent, allowance, context, level + 1) - context.remove(objid) - elif ( - _dataclasses.is_dataclass(object) # type:ignore[unreachable] - and not isinstance(object, type) - and object.__dataclass_params__.repr - and - # Check dataclass has generated repr method. - hasattr(object.__repr__, "__wrapped__") - and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ - ): - context.add(objid) # type:ignore[unreachable] - self._pprint_dataclass( - object, stream, indent, allowance, context, level + 1 - ) - context.remove(objid) - else: - stream.write(self._repr(object, context, level)) - - def _pprint_dataclass( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - cls_name = object.__class__.__name__ - items = [ - (f.name, getattr(object, f.name)) - for f in _dataclasses.fields(object) - if f.repr - ] - stream.write(cls_name + "(") - self._format_namespace_items(items, stream, indent, allowance, context, level) - stream.write(")") - - _dispatch: dict[ - Callable[..., str], - Callable[[PrettyPrinter, Any, IO[str], int, int, set[int], int], None], - ] = {} - - def _pprint_dict( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - write = stream.write - write("{") - items = sorted(object.items(), key=_safe_tuple) - self._format_dict_items(items, stream, indent, allowance, context, level) - write("}") - - _dispatch[dict.__repr__] = _pprint_dict - - def _pprint_ordered_dict( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - if not len(object): - stream.write(repr(object)) - return - cls = object.__class__ - stream.write(cls.__name__ + "(") - self._pprint_dict(object, stream, indent, allowance, context, level) - stream.write(")") - - _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict - - def _pprint_list( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - stream.write("[") - self._format_items(object, stream, indent, allowance, context, level) - stream.write("]") - - _dispatch[list.__repr__] = _pprint_list - - def _pprint_tuple( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - stream.write("(") - self._format_items(object, stream, indent, allowance, context, level) - stream.write(")") - - _dispatch[tuple.__repr__] = _pprint_tuple - - def _pprint_set( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - if not len(object): - stream.write(repr(object)) - return - typ = object.__class__ - if typ is set: - stream.write("{") - endchar = "}" - else: - stream.write(typ.__name__ + "({") - endchar = "})" - object = sorted(object, key=_safe_key) - self._format_items(object, stream, indent, allowance, context, level) - stream.write(endchar) - - _dispatch[set.__repr__] = _pprint_set - _dispatch[frozenset.__repr__] = _pprint_set - - def _pprint_str( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - write = stream.write - if not len(object): - write(repr(object)) - return - chunks = [] - lines = object.splitlines(True) - if level == 1: - indent += 1 - allowance += 1 - max_width1 = max_width = self._width - indent - for i, line in enumerate(lines): - rep = repr(line) - if i == len(lines) - 1: - max_width1 -= allowance - if len(rep) <= max_width1: - chunks.append(rep) - else: - # A list of alternating (non-space, space) strings - parts = re.findall(r"\S*\s*", line) - assert parts - assert not parts[-1] - parts.pop() # drop empty last part - max_width2 = max_width - current = "" - for j, part in enumerate(parts): - candidate = current + part - if j == len(parts) - 1 and i == len(lines) - 1: - max_width2 -= allowance - if len(repr(candidate)) > max_width2: - if current: - chunks.append(repr(current)) - current = part - else: - current = candidate - if current: - chunks.append(repr(current)) - if len(chunks) == 1: - write(rep) - return - if level == 1: - write("(") - for i, rep in enumerate(chunks): - if i > 0: - write("\n" + " " * indent) - write(rep) - if level == 1: - write(")") - - _dispatch[str.__repr__] = _pprint_str - - def _pprint_bytes( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - write = stream.write - if len(object) <= 4: - write(repr(object)) - return - parens = level == 1 - if parens: - indent += 1 - allowance += 1 - write("(") - delim = "" - for rep in _wrap_bytes_repr(object, self._width - indent, allowance): - write(delim) - write(rep) - if not delim: - delim = "\n" + " " * indent - if parens: - write(")") - - _dispatch[bytes.__repr__] = _pprint_bytes - - def _pprint_bytearray( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - write = stream.write - write("bytearray(") - self._pprint_bytes( - bytes(object), stream, indent + 10, allowance + 1, context, level + 1 - ) - write(")") - - _dispatch[bytearray.__repr__] = _pprint_bytearray - - def _pprint_mappingproxy( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - stream.write("mappingproxy(") - self._format(object.copy(), stream, indent, allowance, context, level) - stream.write(")") - - _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy - - def _pprint_simplenamespace( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - if type(object) is _types.SimpleNamespace: - # The SimpleNamespace repr is "namespace" instead of the class - # name, so we do the same here. For subclasses; use the class name. - cls_name = "namespace" - else: - cls_name = object.__class__.__name__ - items = object.__dict__.items() - stream.write(cls_name + "(") - self._format_namespace_items(items, stream, indent, allowance, context, level) - stream.write(")") - - _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace - - def _format_dict_items( - self, - items: list[tuple[Any, Any]], - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - if not items: - return - - write = stream.write - item_indent = indent + self._indent_per_level - delimnl = "\n" + " " * item_indent - for key, ent in items: - write(delimnl) - write(self._repr(key, context, level)) - write(": ") - self._format(ent, stream, item_indent, 1, context, level) - write(",") - - write("\n" + " " * indent) - - def _format_namespace_items( - self, - items: list[tuple[Any, Any]], - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - if not items: - return - - write = stream.write - item_indent = indent + self._indent_per_level - delimnl = "\n" + " " * item_indent - for key, ent in items: - write(delimnl) - write(key) - write("=") - if id(ent) in context: - # Special-case representation of recursion to match standard - # recursive dataclass repr. - write("...") - else: - self._format( - ent, - stream, - item_indent + len(key) + 1, - 1, - context, - level, - ) - - write(",") - - write("\n" + " " * indent) - - def _format_items( - self, - items: list[Any], - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - if not items: - return - - write = stream.write - item_indent = indent + self._indent_per_level - delimnl = "\n" + " " * item_indent - - for item in items: - write(delimnl) - self._format(item, stream, item_indent, 1, context, level) - write(",") - - write("\n" + " " * indent) - - def _repr(self, object: Any, context: set[int], level: int) -> str: - return self._safe_repr(object, context.copy(), self._depth, level) - - def _pprint_default_dict( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - rdf = self._repr(object.default_factory, context, level) - stream.write(f"{object.__class__.__name__}({rdf}, ") - self._pprint_dict(object, stream, indent, allowance, context, level) - stream.write(")") - - _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict - - def _pprint_counter( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - stream.write(object.__class__.__name__ + "(") - - if object: - stream.write("{") - items = object.most_common() - self._format_dict_items(items, stream, indent, allowance, context, level) - stream.write("}") - - stream.write(")") - - _dispatch[_collections.Counter.__repr__] = _pprint_counter - - def _pprint_chain_map( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - if not len(object.maps) or (len(object.maps) == 1 and not len(object.maps[0])): - stream.write(repr(object)) - return - - stream.write(object.__class__.__name__ + "(") - self._format_items(object.maps, stream, indent, allowance, context, level) - stream.write(")") - - _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map - - def _pprint_deque( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - stream.write(object.__class__.__name__ + "(") - if object.maxlen is not None: - stream.write("maxlen=%d, " % object.maxlen) - stream.write("[") - - self._format_items(object, stream, indent, allowance + 1, context, level) - stream.write("])") - - _dispatch[_collections.deque.__repr__] = _pprint_deque - - def _pprint_user_dict( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - self._format(object.data, stream, indent, allowance, context, level - 1) - - _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict - - def _pprint_user_list( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - self._format(object.data, stream, indent, allowance, context, level - 1) - - _dispatch[_collections.UserList.__repr__] = _pprint_user_list - - def _pprint_user_string( - self, - object: Any, - stream: IO[str], - indent: int, - allowance: int, - context: set[int], - level: int, - ) -> None: - self._format(object.data, stream, indent, allowance, context, level - 1) - - _dispatch[_collections.UserString.__repr__] = _pprint_user_string - - def _safe_repr( - self, object: Any, context: set[int], maxlevels: int | None, level: int - ) -> str: - typ = type(object) - if typ in _builtin_scalars: - return repr(object) - - r = getattr(typ, "__repr__", None) - - if issubclass(typ, dict) and r is dict.__repr__: - if not object: - return "{}" - objid = id(object) - if maxlevels and level >= maxlevels: - return "{...}" - if objid in context: - return _recursion(object) - context.add(objid) - components: list[str] = [] - append = components.append - level += 1 - for k, v in sorted(object.items(), key=_safe_tuple): - krepr = self._safe_repr(k, context, maxlevels, level) - vrepr = self._safe_repr(v, context, maxlevels, level) - append(f"{krepr}: {vrepr}") - context.remove(objid) - return "{{{}}}".format(", ".join(components)) - - if (issubclass(typ, list) and r is list.__repr__) or ( - issubclass(typ, tuple) and r is tuple.__repr__ - ): - if issubclass(typ, list): - if not object: - return "[]" - format = "[%s]" - elif len(object) == 1: - format = "(%s,)" - else: - if not object: - return "()" - format = "(%s)" - objid = id(object) - if maxlevels and level >= maxlevels: - return format % "..." - if objid in context: - return _recursion(object) - context.add(objid) - components = [] - append = components.append - level += 1 - for o in object: - orepr = self._safe_repr(o, context, maxlevels, level) - append(orepr) - context.remove(objid) - return format % ", ".join(components) - - return repr(object) - - -_builtin_scalars = frozenset( - {str, bytes, bytearray, float, complex, bool, type(None), int} -) - - -def _recursion(object: Any) -> str: - return f"" - - -def _wrap_bytes_repr(object: Any, width: int, allowance: int) -> Iterator[str]: - current = b"" - last = len(object) // 4 * 4 - for i in range(0, len(object), 4): - part = object[i : i + 4] - candidate = current + part - if i == last: - width -= allowance - if len(repr(candidate)) > width: - if current: - yield repr(current) - current = part - else: - current = candidate - if current: - yield repr(current) diff --git a/.venv/lib/python3.12/site-packages/_pytest/_io/saferepr.py b/.venv/lib/python3.12/site-packages/_pytest/_io/saferepr.py deleted file mode 100644 index cee70e33..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_io/saferepr.py +++ /dev/null @@ -1,130 +0,0 @@ -from __future__ import annotations - -import pprint -import reprlib - - -def _try_repr_or_str(obj: object) -> str: - try: - return repr(obj) - except (KeyboardInterrupt, SystemExit): - raise - except BaseException: - return f'{type(obj).__name__}("{obj}")' - - -def _format_repr_exception(exc: BaseException, obj: object) -> str: - try: - exc_info = _try_repr_or_str(exc) - except (KeyboardInterrupt, SystemExit): - raise - except BaseException as inner_exc: - exc_info = f"unpresentable exception ({_try_repr_or_str(inner_exc)})" - return ( - f"<[{exc_info} raised in repr()] {type(obj).__name__} object at 0x{id(obj):x}>" - ) - - -def _ellipsize(s: str, maxsize: int) -> str: - if len(s) > maxsize: - i = max(0, (maxsize - 3) // 2) - j = max(0, maxsize - 3 - i) - return s[:i] + "..." + s[len(s) - j :] - return s - - -class SafeRepr(reprlib.Repr): - """ - repr.Repr that limits the resulting size of repr() and includes - information on exceptions raised during the call. - """ - - def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None: - """ - :param maxsize: - If not None, will truncate the resulting repr to that specific size, using ellipsis - somewhere in the middle to hide the extra text. - If None, will not impose any size limits on the returning repr. - """ - super().__init__() - # ``maxstring`` is used by the superclass, and needs to be an int; using a - # very large number in case maxsize is None, meaning we want to disable - # truncation. - self.maxstring = maxsize if maxsize is not None else 1_000_000_000 - self.maxsize = maxsize - self.use_ascii = use_ascii - - def repr(self, x: object) -> str: - try: - if self.use_ascii: - s = ascii(x) - else: - s = super().repr(x) - except (KeyboardInterrupt, SystemExit): - raise - except BaseException as exc: - s = _format_repr_exception(exc, x) - if self.maxsize is not None: - s = _ellipsize(s, self.maxsize) - return s - - def repr_instance(self, x: object, level: int) -> str: - try: - s = repr(x) - except (KeyboardInterrupt, SystemExit): - raise - except BaseException as exc: - s = _format_repr_exception(exc, x) - if self.maxsize is not None: - s = _ellipsize(s, self.maxsize) - return s - - -def safeformat(obj: object) -> str: - """Return a pretty printed string for the given object. - - Failing __repr__ functions of user instances will be represented - with a short exception info. - """ - try: - return pprint.pformat(obj) - except Exception as exc: - return _format_repr_exception(exc, obj) - - -# Maximum size of overall repr of objects to display during assertion errors. -DEFAULT_REPR_MAX_SIZE = 240 - - -def saferepr( - obj: object, maxsize: int | None = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False -) -> str: - """Return a size-limited safe repr-string for the given object. - - Failing __repr__ functions of user instances will be represented - with a short exception info and 'saferepr' generally takes - care to never raise exceptions itself. - - This function is a wrapper around the Repr/reprlib functionality of the - stdlib. - """ - return SafeRepr(maxsize, use_ascii).repr(obj) - - -def saferepr_unlimited(obj: object, use_ascii: bool = True) -> str: - """Return an unlimited-size safe repr-string for the given object. - - As with saferepr, failing __repr__ functions of user instances - will be represented with a short exception info. - - This function is a wrapper around simple repr. - - Note: a cleaner solution would be to alter ``saferepr``this way - when maxsize=None, but that might affect some other code. - """ - try: - if use_ascii: - return ascii(obj) - return repr(obj) - except Exception as exc: - return _format_repr_exception(exc, obj) diff --git a/.venv/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py b/.venv/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py deleted file mode 100644 index 70ebd3d0..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py +++ /dev/null @@ -1,274 +0,0 @@ -"""Helper functions for writing to terminals and files.""" - -from __future__ import annotations - -import os -import shutil -import sys -from typing import final -from typing import Literal -from typing import Sequence -from typing import TextIO -from typing import TYPE_CHECKING - -from ..compat import assert_never -from .wcwidth import wcswidth - - -if TYPE_CHECKING: - from pygments.formatter import Formatter - from pygments.lexer import Lexer - - -# This code was initially copied from py 1.8.1, file _io/terminalwriter.py. - - -def get_terminal_width() -> int: - width, _ = shutil.get_terminal_size(fallback=(80, 24)) - - # The Windows get_terminal_size may be bogus, let's sanify a bit. - if width < 40: - width = 80 - - return width - - -def should_do_markup(file: TextIO) -> bool: - if os.environ.get("PY_COLORS") == "1": - return True - if os.environ.get("PY_COLORS") == "0": - return False - if os.environ.get("NO_COLOR"): - return False - if os.environ.get("FORCE_COLOR"): - return True - return ( - hasattr(file, "isatty") and file.isatty() and os.environ.get("TERM") != "dumb" - ) - - -@final -class TerminalWriter: - _esctable = dict( - black=30, - red=31, - green=32, - yellow=33, - blue=34, - purple=35, - cyan=36, - white=37, - Black=40, - Red=41, - Green=42, - Yellow=43, - Blue=44, - Purple=45, - Cyan=46, - White=47, - bold=1, - light=2, - blink=5, - invert=7, - ) - - def __init__(self, file: TextIO | None = None) -> None: - if file is None: - file = sys.stdout - if hasattr(file, "isatty") and file.isatty() and sys.platform == "win32": - try: - import colorama - except ImportError: - pass - else: - file = colorama.AnsiToWin32(file).stream - assert file is not None - self._file = file - self.hasmarkup = should_do_markup(file) - self._current_line = "" - self._terminal_width: int | None = None - self.code_highlight = True - - @property - def fullwidth(self) -> int: - if self._terminal_width is not None: - return self._terminal_width - return get_terminal_width() - - @fullwidth.setter - def fullwidth(self, value: int) -> None: - self._terminal_width = value - - @property - def width_of_current_line(self) -> int: - """Return an estimate of the width so far in the current line.""" - return wcswidth(self._current_line) - - def markup(self, text: str, **markup: bool) -> str: - for name in markup: - if name not in self._esctable: - raise ValueError(f"unknown markup: {name!r}") - if self.hasmarkup: - esc = [self._esctable[name] for name, on in markup.items() if on] - if esc: - text = "".join(f"\x1b[{cod}m" for cod in esc) + text + "\x1b[0m" - return text - - def sep( - self, - sepchar: str, - title: str | None = None, - fullwidth: int | None = None, - **markup: bool, - ) -> None: - if fullwidth is None: - fullwidth = self.fullwidth - # The goal is to have the line be as long as possible - # under the condition that len(line) <= fullwidth. - if sys.platform == "win32": - # If we print in the last column on windows we are on a - # new line but there is no way to verify/neutralize this - # (we may not know the exact line width). - # So let's be defensive to avoid empty lines in the output. - fullwidth -= 1 - if title is not None: - # we want 2 + 2*len(fill) + len(title) <= fullwidth - # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth - # 2*len(sepchar)*N <= fullwidth - len(title) - 2 - # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) - N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1) - fill = sepchar * N - line = f"{fill} {title} {fill}" - else: - # we want len(sepchar)*N <= fullwidth - # i.e. N <= fullwidth // len(sepchar) - line = sepchar * (fullwidth // len(sepchar)) - # In some situations there is room for an extra sepchar at the right, - # in particular if we consider that with a sepchar like "_ " the - # trailing space is not important at the end of the line. - if len(line) + len(sepchar.rstrip()) <= fullwidth: - line += sepchar.rstrip() - - self.line(line, **markup) - - def write(self, msg: str, *, flush: bool = False, **markup: bool) -> None: - if msg: - current_line = msg.rsplit("\n", 1)[-1] - if "\n" in msg: - self._current_line = current_line - else: - self._current_line += current_line - - msg = self.markup(msg, **markup) - - try: - self._file.write(msg) - except UnicodeEncodeError: - # Some environments don't support printing general Unicode - # strings, due to misconfiguration or otherwise; in that case, - # print the string escaped to ASCII. - # When the Unicode situation improves we should consider - # letting the error propagate instead of masking it (see #7475 - # for one brief attempt). - msg = msg.encode("unicode-escape").decode("ascii") - self._file.write(msg) - - if flush: - self.flush() - - def line(self, s: str = "", **markup: bool) -> None: - self.write(s, **markup) - self.write("\n") - - def flush(self) -> None: - self._file.flush() - - def _write_source(self, lines: Sequence[str], indents: Sequence[str] = ()) -> None: - """Write lines of source code possibly highlighted. - - Keeping this private for now because the API is clunky. We should discuss how - to evolve the terminal writer so we can have more precise color support, for example - being able to write part of a line in one color and the rest in another, and so on. - """ - if indents and len(indents) != len(lines): - raise ValueError( - f"indents size ({len(indents)}) should have same size as lines ({len(lines)})" - ) - if not indents: - indents = [""] * len(lines) - source = "\n".join(lines) - new_lines = self._highlight(source).splitlines() - for indent, new_line in zip(indents, new_lines): - self.line(indent + new_line) - - def _get_pygments_lexer(self, lexer: Literal["python", "diff"]) -> Lexer | None: - try: - if lexer == "python": - from pygments.lexers.python import PythonLexer - - return PythonLexer() - elif lexer == "diff": - from pygments.lexers.diff import DiffLexer - - return DiffLexer() - else: - assert_never(lexer) - except ModuleNotFoundError: - return None - - def _get_pygments_formatter(self) -> Formatter | None: - try: - import pygments.util - except ModuleNotFoundError: - return None - - from _pytest.config.exceptions import UsageError - - theme = os.getenv("PYTEST_THEME") - theme_mode = os.getenv("PYTEST_THEME_MODE", "dark") - - try: - from pygments.formatters.terminal import TerminalFormatter - - return TerminalFormatter(bg=theme_mode, style=theme) - - except pygments.util.ClassNotFound as e: - raise UsageError( - f"PYTEST_THEME environment variable has an invalid value: '{theme}'. " - "Hint: See available pygments styles with `pygmentize -L styles`." - ) from e - except pygments.util.OptionError as e: - raise UsageError( - f"PYTEST_THEME_MODE environment variable has an invalid value: '{theme_mode}'. " - "The allowed values are 'dark' (default) and 'light'." - ) from e - - def _highlight( - self, source: str, lexer: Literal["diff", "python"] = "python" - ) -> str: - """Highlight the given source if we have markup support.""" - if not source or not self.hasmarkup or not self.code_highlight: - return source - - pygments_lexer = self._get_pygments_lexer(lexer) - if pygments_lexer is None: - return source - - pygments_formatter = self._get_pygments_formatter() - if pygments_formatter is None: - return source - - from pygments import highlight - - highlighted: str = highlight(source, pygments_lexer, pygments_formatter) - # pygments terminal formatter may add a newline when there wasn't one. - # We don't want this, remove. - if highlighted[-1] == "\n" and source[-1] != "\n": - highlighted = highlighted[:-1] - - # Some lexers will not set the initial color explicitly - # which may lead to the previous color being propagated to the - # start of the expression, so reset first. - highlighted = "\x1b[0m" + highlighted - - return highlighted diff --git a/.venv/lib/python3.12/site-packages/_pytest/_io/wcwidth.py b/.venv/lib/python3.12/site-packages/_pytest/_io/wcwidth.py deleted file mode 100644 index 23886ff1..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_io/wcwidth.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations - -from functools import lru_cache -import unicodedata - - -@lru_cache(100) -def wcwidth(c: str) -> int: - """Determine how many columns are needed to display a character in a terminal. - - Returns -1 if the character is not printable. - Returns 0, 1 or 2 for other characters. - """ - o = ord(c) - - # ASCII fast path. - if 0x20 <= o < 0x07F: - return 1 - - # Some Cf/Zp/Zl characters which should be zero-width. - if ( - o == 0x0000 - or 0x200B <= o <= 0x200F - or 0x2028 <= o <= 0x202E - or 0x2060 <= o <= 0x2063 - ): - return 0 - - category = unicodedata.category(c) - - # Control characters. - if category == "Cc": - return -1 - - # Combining characters with zero width. - if category in ("Me", "Mn"): - return 0 - - # Full/Wide east asian characters. - if unicodedata.east_asian_width(c) in ("F", "W"): - return 2 - - return 1 - - -def wcswidth(s: str) -> int: - """Determine how many columns are needed to display a string in a terminal. - - Returns -1 if the string contains non-printable characters. - """ - width = 0 - for c in unicodedata.normalize("NFC", s): - wc = wcwidth(c) - if wc < 0: - return -1 - width += wc - return width diff --git a/.venv/lib/python3.12/site-packages/_pytest/_py/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/_py/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/_pytest/_py/error.py b/.venv/lib/python3.12/site-packages/_pytest/_py/error.py deleted file mode 100644 index ab3a4ed3..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_py/error.py +++ /dev/null @@ -1,111 +0,0 @@ -"""create errno-specific classes for IO or os calls.""" - -from __future__ import annotations - -import errno -import os -import sys -from typing import Callable -from typing import TYPE_CHECKING -from typing import TypeVar - - -if TYPE_CHECKING: - from typing_extensions import ParamSpec - - P = ParamSpec("P") - -R = TypeVar("R") - - -class Error(EnvironmentError): - def __repr__(self) -> str: - return "{}.{} {!r}: {} ".format( - self.__class__.__module__, - self.__class__.__name__, - self.__class__.__doc__, - " ".join(map(str, self.args)), - # repr(self.args) - ) - - def __str__(self) -> str: - s = "[{}]: {}".format( - self.__class__.__doc__, - " ".join(map(str, self.args)), - ) - return s - - -_winerrnomap = { - 2: errno.ENOENT, - 3: errno.ENOENT, - 17: errno.EEXIST, - 18: errno.EXDEV, - 13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailable - 22: errno.ENOTDIR, - 20: errno.ENOTDIR, - 267: errno.ENOTDIR, - 5: errno.EACCES, # anything better? -} - - -class ErrorMaker: - """lazily provides Exception classes for each possible POSIX errno - (as defined per the 'errno' module). All such instances - subclass EnvironmentError. - """ - - _errno2class: dict[int, type[Error]] = {} - - def __getattr__(self, name: str) -> type[Error]: - if name[0] == "_": - raise AttributeError(name) - eno = getattr(errno, name) - cls = self._geterrnoclass(eno) - setattr(self, name, cls) - return cls - - def _geterrnoclass(self, eno: int) -> type[Error]: - try: - return self._errno2class[eno] - except KeyError: - clsname = errno.errorcode.get(eno, "UnknownErrno%d" % (eno,)) - errorcls = type( - clsname, - (Error,), - {"__module__": "py.error", "__doc__": os.strerror(eno)}, - ) - self._errno2class[eno] = errorcls - return errorcls - - def checked_call( - self, func: Callable[P, R], *args: P.args, **kwargs: P.kwargs - ) -> R: - """Call a function and raise an errno-exception if applicable.""" - __tracebackhide__ = True - try: - return func(*args, **kwargs) - except Error: - raise - except OSError as value: - if not hasattr(value, "errno"): - raise - errno = value.errno - if sys.platform == "win32": - try: - cls = self._geterrnoclass(_winerrnomap[errno]) - except KeyError: - raise value - else: - # we are not on Windows, or we got a proper OSError - cls = self._geterrnoclass(errno) - - raise cls(f"{func.__name__}{args!r}") - - -_error_maker = ErrorMaker() -checked_call = _error_maker.checked_call - - -def __getattr__(attr: str) -> type[Error]: - return getattr(_error_maker, attr) # type: ignore[no-any-return] diff --git a/.venv/lib/python3.12/site-packages/_pytest/_py/path.py b/.venv/lib/python3.12/site-packages/_pytest/_py/path.py deleted file mode 100644 index c7ab1182..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_py/path.py +++ /dev/null @@ -1,1475 +0,0 @@ -# mypy: allow-untyped-defs -"""local path implementation.""" - -from __future__ import annotations - -import atexit -from contextlib import contextmanager -import fnmatch -import importlib.util -import io -import os -from os.path import abspath -from os.path import dirname -from os.path import exists -from os.path import isabs -from os.path import isdir -from os.path import isfile -from os.path import islink -from os.path import normpath -import posixpath -from stat import S_ISDIR -from stat import S_ISLNK -from stat import S_ISREG -import sys -from typing import Any -from typing import Callable -from typing import cast -from typing import Literal -from typing import overload -from typing import TYPE_CHECKING -import uuid -import warnings - -from . import error - - -# Moved from local.py. -iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt") - - -class Checkers: - _depend_on_existence = "exists", "link", "dir", "file" - - def __init__(self, path): - self.path = path - - def dotfile(self): - return self.path.basename.startswith(".") - - def ext(self, arg): - if not arg.startswith("."): - arg = "." + arg - return self.path.ext == arg - - def basename(self, arg): - return self.path.basename == arg - - def basestarts(self, arg): - return self.path.basename.startswith(arg) - - def relto(self, arg): - return self.path.relto(arg) - - def fnmatch(self, arg): - return self.path.fnmatch(arg) - - def endswith(self, arg): - return str(self.path).endswith(arg) - - def _evaluate(self, kw): - from .._code.source import getrawcode - - for name, value in kw.items(): - invert = False - meth = None - try: - meth = getattr(self, name) - except AttributeError: - if name[:3] == "not": - invert = True - try: - meth = getattr(self, name[3:]) - except AttributeError: - pass - if meth is None: - raise TypeError(f"no {name!r} checker available for {self.path!r}") - try: - if getrawcode(meth).co_argcount > 1: - if (not meth(value)) ^ invert: - return False - else: - if bool(value) ^ bool(meth()) ^ invert: - return False - except (error.ENOENT, error.ENOTDIR, error.EBUSY): - # EBUSY feels not entirely correct, - # but its kind of necessary since ENOMEDIUM - # is not accessible in python - for name in self._depend_on_existence: - if name in kw: - if kw.get(name): - return False - name = "not" + name - if name in kw: - if not kw.get(name): - return False - return True - - _statcache: Stat - - def _stat(self) -> Stat: - try: - return self._statcache - except AttributeError: - try: - self._statcache = self.path.stat() - except error.ELOOP: - self._statcache = self.path.lstat() - return self._statcache - - def dir(self): - return S_ISDIR(self._stat().mode) - - def file(self): - return S_ISREG(self._stat().mode) - - def exists(self): - return self._stat() - - def link(self): - st = self.path.lstat() - return S_ISLNK(st.mode) - - -class NeverRaised(Exception): - pass - - -class Visitor: - def __init__(self, fil, rec, ignore, bf, sort): - if isinstance(fil, str): - fil = FNMatcher(fil) - if isinstance(rec, str): - self.rec: Callable[[LocalPath], bool] = FNMatcher(rec) - elif not hasattr(rec, "__call__") and rec: - self.rec = lambda path: True - else: - self.rec = rec - self.fil = fil - self.ignore = ignore - self.breadthfirst = bf - self.optsort = cast(Callable[[Any], Any], sorted) if sort else (lambda x: x) - - def gen(self, path): - try: - entries = path.listdir() - except self.ignore: - return - rec = self.rec - dirs = self.optsort( - [p for p in entries if p.check(dir=1) and (rec is None or rec(p))] - ) - if not self.breadthfirst: - for subdir in dirs: - yield from self.gen(subdir) - for p in self.optsort(entries): - if self.fil is None or self.fil(p): - yield p - if self.breadthfirst: - for subdir in dirs: - yield from self.gen(subdir) - - -class FNMatcher: - def __init__(self, pattern): - self.pattern = pattern - - def __call__(self, path): - pattern = self.pattern - - if ( - pattern.find(path.sep) == -1 - and iswin32 - and pattern.find(posixpath.sep) != -1 - ): - # Running on Windows, the pattern has no Windows path separators, - # and the pattern has one or more Posix path separators. Replace - # the Posix path separators with the Windows path separator. - pattern = pattern.replace(posixpath.sep, path.sep) - - if pattern.find(path.sep) == -1: - name = path.basename - else: - name = str(path) # path.strpath # XXX svn? - if not os.path.isabs(pattern): - pattern = "*" + path.sep + pattern - return fnmatch.fnmatch(name, pattern) - - -def map_as_list(func, iter): - return list(map(func, iter)) - - -class Stat: - if TYPE_CHECKING: - - @property - def size(self) -> int: ... - - @property - def mtime(self) -> float: ... - - def __getattr__(self, name: str) -> Any: - return getattr(self._osstatresult, "st_" + name) - - def __init__(self, path, osstatresult): - self.path = path - self._osstatresult = osstatresult - - @property - def owner(self): - if iswin32: - raise NotImplementedError("XXX win32") - import pwd - - entry = error.checked_call(pwd.getpwuid, self.uid) # type:ignore[attr-defined,unused-ignore] - return entry[0] - - @property - def group(self): - """Return group name of file.""" - if iswin32: - raise NotImplementedError("XXX win32") - import grp - - entry = error.checked_call(grp.getgrgid, self.gid) # type:ignore[attr-defined,unused-ignore] - return entry[0] - - def isdir(self): - return S_ISDIR(self._osstatresult.st_mode) - - def isfile(self): - return S_ISREG(self._osstatresult.st_mode) - - def islink(self): - self.path.lstat() - return S_ISLNK(self._osstatresult.st_mode) - - -def getuserid(user): - import pwd - - if not isinstance(user, int): - user = pwd.getpwnam(user)[2] # type:ignore[attr-defined,unused-ignore] - return user - - -def getgroupid(group): - import grp - - if not isinstance(group, int): - group = grp.getgrnam(group)[2] # type:ignore[attr-defined,unused-ignore] - return group - - -class LocalPath: - """Object oriented interface to os.path and other local filesystem - related information. - """ - - class ImportMismatchError(ImportError): - """raised on pyimport() if there is a mismatch of __file__'s""" - - sep = os.sep - - def __init__(self, path=None, expanduser=False): - """Initialize and return a local Path instance. - - Path can be relative to the current directory. - If path is None it defaults to the current working directory. - If expanduser is True, tilde-expansion is performed. - Note that Path instances always carry an absolute path. - Note also that passing in a local path object will simply return - the exact same path object. Use new() to get a new copy. - """ - if path is None: - self.strpath = error.checked_call(os.getcwd) - else: - try: - path = os.fspath(path) - except TypeError: - raise ValueError( - "can only pass None, Path instances " - "or non-empty strings to LocalPath" - ) - if expanduser: - path = os.path.expanduser(path) - self.strpath = abspath(path) - - if sys.platform != "win32": - - def chown(self, user, group, rec=0): - """Change ownership to the given user and group. - user and group may be specified by a number or - by a name. if rec is True change ownership - recursively. - """ - uid = getuserid(user) - gid = getgroupid(group) - if rec: - for x in self.visit(rec=lambda x: x.check(link=0)): - if x.check(link=0): - error.checked_call(os.chown, str(x), uid, gid) - error.checked_call(os.chown, str(self), uid, gid) - - def readlink(self) -> str: - """Return value of a symbolic link.""" - # https://github.com/python/mypy/issues/12278 - return error.checked_call(os.readlink, self.strpath) # type: ignore[arg-type,return-value,unused-ignore] - - def mklinkto(self, oldname): - """Posix style hard link to another name.""" - error.checked_call(os.link, str(oldname), str(self)) - - def mksymlinkto(self, value, absolute=1): - """Create a symbolic link with the given value (pointing to another name).""" - if absolute: - error.checked_call(os.symlink, str(value), self.strpath) - else: - base = self.common(value) - # with posix local paths '/' is always a common base - relsource = self.__class__(value).relto(base) - reldest = self.relto(base) - n = reldest.count(self.sep) - target = self.sep.join(("..",) * n + (relsource,)) - error.checked_call(os.symlink, target, self.strpath) - - def __div__(self, other): - return self.join(os.fspath(other)) - - __truediv__ = __div__ # py3k - - @property - def basename(self): - """Basename part of path.""" - return self._getbyspec("basename")[0] - - @property - def dirname(self): - """Dirname part of path.""" - return self._getbyspec("dirname")[0] - - @property - def purebasename(self): - """Pure base name of the path.""" - return self._getbyspec("purebasename")[0] - - @property - def ext(self): - """Extension of the path (including the '.').""" - return self._getbyspec("ext")[0] - - def read_binary(self): - """Read and return a bytestring from reading the path.""" - with self.open("rb") as f: - return f.read() - - def read_text(self, encoding): - """Read and return a Unicode string from reading the path.""" - with self.open("r", encoding=encoding) as f: - return f.read() - - def read(self, mode="r"): - """Read and return a bytestring from reading the path.""" - with self.open(mode) as f: - return f.read() - - def readlines(self, cr=1): - """Read and return a list of lines from the path. if cr is False, the - newline will be removed from the end of each line.""" - mode = "r" - - if not cr: - content = self.read(mode) - return content.split("\n") - else: - f = self.open(mode) - try: - return f.readlines() - finally: - f.close() - - def load(self): - """(deprecated) return object unpickled from self.read()""" - f = self.open("rb") - try: - import pickle - - return error.checked_call(pickle.load, f) - finally: - f.close() - - def move(self, target): - """Move this path to target.""" - if target.relto(self): - raise error.EINVAL(target, "cannot move path into a subdirectory of itself") - try: - self.rename(target) - except error.EXDEV: # invalid cross-device link - self.copy(target) - self.remove() - - def fnmatch(self, pattern): - """Return true if the basename/fullname matches the glob-'pattern'. - - valid pattern characters:: - - * matches everything - ? matches any single character - [seq] matches any character in seq - [!seq] matches any char not in seq - - If the pattern contains a path-separator then the full path - is used for pattern matching and a '*' is prepended to the - pattern. - - if the pattern doesn't contain a path-separator the pattern - is only matched against the basename. - """ - return FNMatcher(pattern)(self) - - def relto(self, relpath): - """Return a string which is the relative part of the path - to the given 'relpath'. - """ - if not isinstance(relpath, (str, LocalPath)): - raise TypeError(f"{relpath!r}: not a string or path object") - strrelpath = str(relpath) - if strrelpath and strrelpath[-1] != self.sep: - strrelpath += self.sep - # assert strrelpath[-1] == self.sep - # assert strrelpath[-2] != self.sep - strself = self.strpath - if sys.platform == "win32" or getattr(os, "_name", None) == "nt": - if os.path.normcase(strself).startswith(os.path.normcase(strrelpath)): - return strself[len(strrelpath) :] - elif strself.startswith(strrelpath): - return strself[len(strrelpath) :] - return "" - - def ensure_dir(self, *args): - """Ensure the path joined with args is a directory.""" - return self.ensure(*args, dir=True) - - def bestrelpath(self, dest): - """Return a string which is a relative path from self - (assumed to be a directory) to dest such that - self.join(bestrelpath) == dest and if not such - path can be determined return dest. - """ - try: - if self == dest: - return os.curdir - base = self.common(dest) - if not base: # can be the case on windows - return str(dest) - self2base = self.relto(base) - reldest = dest.relto(base) - if self2base: - n = self2base.count(self.sep) + 1 - else: - n = 0 - lst = [os.pardir] * n - if reldest: - lst.append(reldest) - target = dest.sep.join(lst) - return target - except AttributeError: - return str(dest) - - def exists(self): - return self.check() - - def isdir(self): - return self.check(dir=1) - - def isfile(self): - return self.check(file=1) - - def parts(self, reverse=False): - """Return a root-first list of all ancestor directories - plus the path itself. - """ - current = self - lst = [self] - while 1: - last = current - current = current.dirpath() - if last == current: - break - lst.append(current) - if not reverse: - lst.reverse() - return lst - - def common(self, other): - """Return the common part shared with the other path - or None if there is no common part. - """ - last = None - for x, y in zip(self.parts(), other.parts()): - if x != y: - return last - last = x - return last - - def __add__(self, other): - """Return new path object with 'other' added to the basename""" - return self.new(basename=self.basename + str(other)) - - def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False): - """Yields all paths below the current one - - fil is a filter (glob pattern or callable), if not matching the - path will not be yielded, defaulting to None (everything is - returned) - - rec is a filter (glob pattern or callable) that controls whether - a node is descended, defaulting to None - - ignore is an Exception class that is ignoredwhen calling dirlist() - on any of the paths (by default, all exceptions are reported) - - bf if True will cause a breadthfirst search instead of the - default depthfirst. Default: False - - sort if True will sort entries within each directory level. - """ - yield from Visitor(fil, rec, ignore, bf, sort).gen(self) - - def _sortlist(self, res, sort): - if sort: - if hasattr(sort, "__call__"): - warnings.warn( - DeprecationWarning( - "listdir(sort=callable) is deprecated and breaks on python3" - ), - stacklevel=3, - ) - res.sort(sort) - else: - res.sort() - - def __fspath__(self): - return self.strpath - - def __hash__(self): - s = self.strpath - if iswin32: - s = s.lower() - return hash(s) - - def __eq__(self, other): - s1 = os.fspath(self) - try: - s2 = os.fspath(other) - except TypeError: - return False - if iswin32: - s1 = s1.lower() - try: - s2 = s2.lower() - except AttributeError: - return False - return s1 == s2 - - def __ne__(self, other): - return not (self == other) - - def __lt__(self, other): - return os.fspath(self) < os.fspath(other) - - def __gt__(self, other): - return os.fspath(self) > os.fspath(other) - - def samefile(self, other): - """Return True if 'other' references the same file as 'self'.""" - other = os.fspath(other) - if not isabs(other): - other = abspath(other) - if self == other: - return True - if not hasattr(os.path, "samefile"): - return False - return error.checked_call(os.path.samefile, self.strpath, other) - - def remove(self, rec=1, ignore_errors=False): - """Remove a file or directory (or a directory tree if rec=1). - if ignore_errors is True, errors while removing directories will - be ignored. - """ - if self.check(dir=1, link=0): - if rec: - # force remove of readonly files on windows - if iswin32: - self.chmod(0o700, rec=1) - import shutil - - error.checked_call( - shutil.rmtree, self.strpath, ignore_errors=ignore_errors - ) - else: - error.checked_call(os.rmdir, self.strpath) - else: - if iswin32: - self.chmod(0o700) - error.checked_call(os.remove, self.strpath) - - def computehash(self, hashtype="md5", chunksize=524288): - """Return hexdigest of hashvalue for this file.""" - try: - try: - import hashlib as mod - except ImportError: - if hashtype == "sha1": - hashtype = "sha" - mod = __import__(hashtype) - hash = getattr(mod, hashtype)() - except (AttributeError, ImportError): - raise ValueError(f"Don't know how to compute {hashtype!r} hash") - f = self.open("rb") - try: - while 1: - buf = f.read(chunksize) - if not buf: - return hash.hexdigest() - hash.update(buf) - finally: - f.close() - - def new(self, **kw): - """Create a modified version of this path. - the following keyword arguments modify various path parts:: - - a:/some/path/to/a/file.ext - xx drive - xxxxxxxxxxxxxxxxx dirname - xxxxxxxx basename - xxxx purebasename - xxx ext - """ - obj = object.__new__(self.__class__) - if not kw: - obj.strpath = self.strpath - return obj - drive, dirname, basename, purebasename, ext = self._getbyspec( - "drive,dirname,basename,purebasename,ext" - ) - if "basename" in kw: - if "purebasename" in kw or "ext" in kw: - raise ValueError(f"invalid specification {kw!r}") - else: - pb = kw.setdefault("purebasename", purebasename) - try: - ext = kw["ext"] - except KeyError: - pass - else: - if ext and not ext.startswith("."): - ext = "." + ext - kw["basename"] = pb + ext - - if "dirname" in kw and not kw["dirname"]: - kw["dirname"] = drive - else: - kw.setdefault("dirname", dirname) - kw.setdefault("sep", self.sep) - obj.strpath = normpath("{dirname}{sep}{basename}".format(**kw)) - return obj - - def _getbyspec(self, spec: str) -> list[str]: - """See new for what 'spec' can be.""" - res = [] - parts = self.strpath.split(self.sep) - - args = filter(None, spec.split(",")) - for name in args: - if name == "drive": - res.append(parts[0]) - elif name == "dirname": - res.append(self.sep.join(parts[:-1])) - else: - basename = parts[-1] - if name == "basename": - res.append(basename) - else: - i = basename.rfind(".") - if i == -1: - purebasename, ext = basename, "" - else: - purebasename, ext = basename[:i], basename[i:] - if name == "purebasename": - res.append(purebasename) - elif name == "ext": - res.append(ext) - else: - raise ValueError(f"invalid part specification {name!r}") - return res - - def dirpath(self, *args, **kwargs): - """Return the directory path joined with any given path arguments.""" - if not kwargs: - path = object.__new__(self.__class__) - path.strpath = dirname(self.strpath) - if args: - path = path.join(*args) - return path - return self.new(basename="").join(*args, **kwargs) - - def join(self, *args: os.PathLike[str], abs: bool = False) -> LocalPath: - """Return a new path by appending all 'args' as path - components. if abs=1 is used restart from root if any - of the args is an absolute path. - """ - sep = self.sep - strargs = [os.fspath(arg) for arg in args] - strpath = self.strpath - if abs: - newargs: list[str] = [] - for arg in reversed(strargs): - if isabs(arg): - strpath = arg - strargs = newargs - break - newargs.insert(0, arg) - # special case for when we have e.g. strpath == "/" - actual_sep = "" if strpath.endswith(sep) else sep - for arg in strargs: - arg = arg.strip(sep) - if iswin32: - # allow unix style paths even on windows. - arg = arg.strip("/") - arg = arg.replace("/", sep) - strpath = strpath + actual_sep + arg - actual_sep = sep - obj = object.__new__(self.__class__) - obj.strpath = normpath(strpath) - return obj - - def open(self, mode="r", ensure=False, encoding=None): - """Return an opened file with the given mode. - - If ensure is True, create parent directories if needed. - """ - if ensure: - self.dirpath().ensure(dir=1) - if encoding: - return error.checked_call( - io.open, - self.strpath, - mode, - encoding=encoding, - ) - return error.checked_call(open, self.strpath, mode) - - def _fastjoin(self, name): - child = object.__new__(self.__class__) - child.strpath = self.strpath + self.sep + name - return child - - def islink(self): - return islink(self.strpath) - - def check(self, **kw): - """Check a path for existence and properties. - - Without arguments, return True if the path exists, otherwise False. - - valid checkers:: - - file = 1 # is a file - file = 0 # is not a file (may not even exist) - dir = 1 # is a dir - link = 1 # is a link - exists = 1 # exists - - You can specify multiple checker definitions, for example:: - - path.check(file=1, link=1) # a link pointing to a file - """ - if not kw: - return exists(self.strpath) - if len(kw) == 1: - if "dir" in kw: - return not kw["dir"] ^ isdir(self.strpath) - if "file" in kw: - return not kw["file"] ^ isfile(self.strpath) - if not kw: - kw = {"exists": 1} - return Checkers(self)._evaluate(kw) - - _patternchars = set("*?[" + os.sep) - - def listdir(self, fil=None, sort=None): - """List directory contents, possibly filter by the given fil func - and possibly sorted. - """ - if fil is None and sort is None: - names = error.checked_call(os.listdir, self.strpath) - return map_as_list(self._fastjoin, names) - if isinstance(fil, str): - if not self._patternchars.intersection(fil): - child = self._fastjoin(fil) - if exists(child.strpath): - return [child] - return [] - fil = FNMatcher(fil) - names = error.checked_call(os.listdir, self.strpath) - res = [] - for name in names: - child = self._fastjoin(name) - if fil is None or fil(child): - res.append(child) - self._sortlist(res, sort) - return res - - def size(self) -> int: - """Return size of the underlying file object""" - return self.stat().size - - def mtime(self) -> float: - """Return last modification time of the path.""" - return self.stat().mtime - - def copy(self, target, mode=False, stat=False): - """Copy path to target. - - If mode is True, will copy permission from path to target. - If stat is True, copy permission, last modification - time, last access time, and flags from path to target. - """ - if self.check(file=1): - if target.check(dir=1): - target = target.join(self.basename) - assert self != target - copychunked(self, target) - if mode: - copymode(self.strpath, target.strpath) - if stat: - copystat(self, target) - else: - - def rec(p): - return p.check(link=0) - - for x in self.visit(rec=rec): - relpath = x.relto(self) - newx = target.join(relpath) - newx.dirpath().ensure(dir=1) - if x.check(link=1): - newx.mksymlinkto(x.readlink()) - continue - elif x.check(file=1): - copychunked(x, newx) - elif x.check(dir=1): - newx.ensure(dir=1) - if mode: - copymode(x.strpath, newx.strpath) - if stat: - copystat(x, newx) - - def rename(self, target): - """Rename this path to target.""" - target = os.fspath(target) - return error.checked_call(os.rename, self.strpath, target) - - def dump(self, obj, bin=1): - """Pickle object into path location""" - f = self.open("wb") - import pickle - - try: - error.checked_call(pickle.dump, obj, f, bin) - finally: - f.close() - - def mkdir(self, *args): - """Create & return the directory joined with args.""" - p = self.join(*args) - error.checked_call(os.mkdir, os.fspath(p)) - return p - - def write_binary(self, data, ensure=False): - """Write binary data into path. If ensure is True create - missing parent directories. - """ - if ensure: - self.dirpath().ensure(dir=1) - with self.open("wb") as f: - f.write(data) - - def write_text(self, data, encoding, ensure=False): - """Write text data into path using the specified encoding. - If ensure is True create missing parent directories. - """ - if ensure: - self.dirpath().ensure(dir=1) - with self.open("w", encoding=encoding) as f: - f.write(data) - - def write(self, data, mode="w", ensure=False): - """Write data into path. If ensure is True create - missing parent directories. - """ - if ensure: - self.dirpath().ensure(dir=1) - if "b" in mode: - if not isinstance(data, bytes): - raise ValueError("can only process bytes") - else: - if not isinstance(data, str): - if not isinstance(data, bytes): - data = str(data) - else: - data = data.decode(sys.getdefaultencoding()) - f = self.open(mode) - try: - f.write(data) - finally: - f.close() - - def _ensuredirs(self): - parent = self.dirpath() - if parent == self: - return self - if parent.check(dir=0): - parent._ensuredirs() - if self.check(dir=0): - try: - self.mkdir() - except error.EEXIST: - # race condition: file/dir created by another thread/process. - # complain if it is not a dir - if self.check(dir=0): - raise - return self - - def ensure(self, *args, **kwargs): - """Ensure that an args-joined path exists (by default as - a file). if you specify a keyword argument 'dir=True' - then the path is forced to be a directory path. - """ - p = self.join(*args) - if kwargs.get("dir", 0): - return p._ensuredirs() - else: - p.dirpath()._ensuredirs() - if not p.check(file=1): - p.open("wb").close() - return p - - @overload - def stat(self, raising: Literal[True] = ...) -> Stat: ... - - @overload - def stat(self, raising: Literal[False]) -> Stat | None: ... - - def stat(self, raising: bool = True) -> Stat | None: - """Return an os.stat() tuple.""" - if raising: - return Stat(self, error.checked_call(os.stat, self.strpath)) - try: - return Stat(self, os.stat(self.strpath)) - except KeyboardInterrupt: - raise - except Exception: - return None - - def lstat(self) -> Stat: - """Return an os.lstat() tuple.""" - return Stat(self, error.checked_call(os.lstat, self.strpath)) - - def setmtime(self, mtime=None): - """Set modification time for the given path. if 'mtime' is None - (the default) then the file's mtime is set to current time. - - Note that the resolution for 'mtime' is platform dependent. - """ - if mtime is None: - return error.checked_call(os.utime, self.strpath, mtime) - try: - return error.checked_call(os.utime, self.strpath, (-1, mtime)) - except error.EINVAL: - return error.checked_call(os.utime, self.strpath, (self.atime(), mtime)) - - def chdir(self): - """Change directory to self and return old current directory""" - try: - old = self.__class__() - except error.ENOENT: - old = None - error.checked_call(os.chdir, self.strpath) - return old - - @contextmanager - def as_cwd(self): - """ - Return a context manager, which changes to the path's dir during the - managed "with" context. - On __enter__ it returns the old dir, which might be ``None``. - """ - old = self.chdir() - try: - yield old - finally: - if old is not None: - old.chdir() - - def realpath(self): - """Return a new path which contains no symbolic links.""" - return self.__class__(os.path.realpath(self.strpath)) - - def atime(self): - """Return last access time of the path.""" - return self.stat().atime - - def __repr__(self): - return f"local({self.strpath!r})" - - def __str__(self): - """Return string representation of the Path.""" - return self.strpath - - def chmod(self, mode, rec=0): - """Change permissions to the given mode. If mode is an - integer it directly encodes the os-specific modes. - if rec is True perform recursively. - """ - if not isinstance(mode, int): - raise TypeError(f"mode {mode!r} must be an integer") - if rec: - for x in self.visit(rec=rec): - error.checked_call(os.chmod, str(x), mode) - error.checked_call(os.chmod, self.strpath, mode) - - def pypkgpath(self): - """Return the Python package path by looking for the last - directory upwards which still contains an __init__.py. - Return None if a pkgpath cannot be determined. - """ - pkgpath = None - for parent in self.parts(reverse=True): - if parent.isdir(): - if not parent.join("__init__.py").exists(): - break - if not isimportable(parent.basename): - break - pkgpath = parent - return pkgpath - - def _ensuresyspath(self, ensuremode, path): - if ensuremode: - s = str(path) - if ensuremode == "append": - if s not in sys.path: - sys.path.append(s) - else: - if s != sys.path[0]: - sys.path.insert(0, s) - - def pyimport(self, modname=None, ensuresyspath=True): - """Return path as an imported python module. - - If modname is None, look for the containing package - and construct an according module name. - The module will be put/looked up in sys.modules. - if ensuresyspath is True then the root dir for importing - the file (taking __init__.py files into account) will - be prepended to sys.path if it isn't there already. - If ensuresyspath=="append" the root dir will be appended - if it isn't already contained in sys.path. - if ensuresyspath is False no modification of syspath happens. - - Special value of ensuresyspath=="importlib" is intended - purely for using in pytest, it is capable only of importing - separate .py files outside packages, e.g. for test suite - without any __init__.py file. It effectively allows having - same-named test modules in different places and offers - mild opt-in via this option. Note that it works only in - recent versions of python. - """ - if not self.check(): - raise error.ENOENT(self) - - if ensuresyspath == "importlib": - if modname is None: - modname = self.purebasename - spec = importlib.util.spec_from_file_location(modname, str(self)) - if spec is None or spec.loader is None: - raise ImportError(f"Can't find module {modname} at location {self!s}") - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - return mod - - pkgpath = None - if modname is None: - pkgpath = self.pypkgpath() - if pkgpath is not None: - pkgroot = pkgpath.dirpath() - names = self.new(ext="").relto(pkgroot).split(self.sep) - if names[-1] == "__init__": - names.pop() - modname = ".".join(names) - else: - pkgroot = self.dirpath() - modname = self.purebasename - - self._ensuresyspath(ensuresyspath, pkgroot) - __import__(modname) - mod = sys.modules[modname] - if self.basename == "__init__.py": - return mod # we don't check anything as we might - # be in a namespace package ... too icky to check - modfile = mod.__file__ - assert modfile is not None - if modfile[-4:] in (".pyc", ".pyo"): - modfile = modfile[:-1] - elif modfile.endswith("$py.class"): - modfile = modfile[:-9] + ".py" - if modfile.endswith(os.sep + "__init__.py"): - if self.basename != "__init__.py": - modfile = modfile[:-12] - try: - issame = self.samefile(modfile) - except error.ENOENT: - issame = False - if not issame: - ignore = os.getenv("PY_IGNORE_IMPORTMISMATCH") - if ignore != "1": - raise self.ImportMismatchError(modname, modfile, self) - return mod - else: - try: - return sys.modules[modname] - except KeyError: - # we have a custom modname, do a pseudo-import - import types - - mod = types.ModuleType(modname) - mod.__file__ = str(self) - sys.modules[modname] = mod - try: - with open(str(self), "rb") as f: - exec(f.read(), mod.__dict__) - except BaseException: - del sys.modules[modname] - raise - return mod - - def sysexec(self, *argv: os.PathLike[str], **popen_opts: Any) -> str: - """Return stdout text from executing a system child process, - where the 'self' path points to executable. - The process is directly invoked and not through a system shell. - """ - from subprocess import PIPE - from subprocess import Popen - - popen_opts.pop("stdout", None) - popen_opts.pop("stderr", None) - proc = Popen( - [str(self)] + [str(arg) for arg in argv], - **popen_opts, - stdout=PIPE, - stderr=PIPE, - ) - stdout: str | bytes - stdout, stderr = proc.communicate() - ret = proc.wait() - if isinstance(stdout, bytes): - stdout = stdout.decode(sys.getdefaultencoding()) - if ret != 0: - if isinstance(stderr, bytes): - stderr = stderr.decode(sys.getdefaultencoding()) - raise RuntimeError( - ret, - ret, - str(self), - stdout, - stderr, - ) - return stdout - - @classmethod - def sysfind(cls, name, checker=None, paths=None): - """Return a path object found by looking at the systems - underlying PATH specification. If the checker is not None - it will be invoked to filter matching paths. If a binary - cannot be found, None is returned - Note: This is probably not working on plain win32 systems - but may work on cygwin. - """ - if isabs(name): - p = local(name) - if p.check(file=1): - return p - else: - if paths is None: - if iswin32: - paths = os.environ["Path"].split(";") - if "" not in paths and "." not in paths: - paths.append(".") - try: - systemroot = os.environ["SYSTEMROOT"] - except KeyError: - pass - else: - paths = [ - path.replace("%SystemRoot%", systemroot) for path in paths - ] - else: - paths = os.environ["PATH"].split(":") - tryadd = [] - if iswin32: - tryadd += os.environ["PATHEXT"].split(os.pathsep) - tryadd.append("") - - for x in paths: - for addext in tryadd: - p = local(x).join(name, abs=True) + addext - try: - if p.check(file=1): - if checker: - if not checker(p): - continue - return p - except error.EACCES: - pass - return None - - @classmethod - def _gethomedir(cls): - try: - x = os.environ["HOME"] - except KeyError: - try: - x = os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"] - except KeyError: - return None - return cls(x) - - # """ - # special class constructors for local filesystem paths - # """ - @classmethod - def get_temproot(cls): - """Return the system's temporary directory - (where tempfiles are usually created in) - """ - import tempfile - - return local(tempfile.gettempdir()) - - @classmethod - def mkdtemp(cls, rootdir=None): - """Return a Path object pointing to a fresh new temporary directory - (which we created ourselves). - """ - import tempfile - - if rootdir is None: - rootdir = cls.get_temproot() - path = error.checked_call(tempfile.mkdtemp, dir=str(rootdir)) - return cls(path) - - @classmethod - def make_numbered_dir( - cls, prefix="session-", rootdir=None, keep=3, lock_timeout=172800 - ): # two days - """Return unique directory with a number greater than the current - maximum one. The number is assumed to start directly after prefix. - if keep is true directories with a number less than (maxnum-keep) - will be removed. If .lock files are used (lock_timeout non-zero), - algorithm is multi-process safe. - """ - if rootdir is None: - rootdir = cls.get_temproot() - - nprefix = prefix.lower() - - def parse_num(path): - """Parse the number out of a path (if it matches the prefix)""" - nbasename = path.basename.lower() - if nbasename.startswith(nprefix): - try: - return int(nbasename[len(nprefix) :]) - except ValueError: - pass - - def create_lockfile(path): - """Exclusively create lockfile. Throws when failed""" - mypid = os.getpid() - lockfile = path.join(".lock") - if hasattr(lockfile, "mksymlinkto"): - lockfile.mksymlinkto(str(mypid)) - else: - fd = error.checked_call( - os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644 - ) - with os.fdopen(fd, "w") as f: - f.write(str(mypid)) - return lockfile - - def atexit_remove_lockfile(lockfile): - """Ensure lockfile is removed at process exit""" - mypid = os.getpid() - - def try_remove_lockfile(): - # in a fork() situation, only the last process should - # remove the .lock, otherwise the other processes run the - # risk of seeing their temporary dir disappear. For now - # we remove the .lock in the parent only (i.e. we assume - # that the children finish before the parent). - if os.getpid() != mypid: - return - try: - lockfile.remove() - except error.Error: - pass - - atexit.register(try_remove_lockfile) - - # compute the maximum number currently in use with the prefix - lastmax = None - while True: - maxnum = -1 - for path in rootdir.listdir(): - num = parse_num(path) - if num is not None: - maxnum = max(maxnum, num) - - # make the new directory - try: - udir = rootdir.mkdir(prefix + str(maxnum + 1)) - if lock_timeout: - lockfile = create_lockfile(udir) - atexit_remove_lockfile(lockfile) - except (error.EEXIST, error.ENOENT, error.EBUSY): - # race condition (1): another thread/process created the dir - # in the meantime - try again - # race condition (2): another thread/process spuriously acquired - # lock treating empty directory as candidate - # for removal - try again - # race condition (3): another thread/process tried to create the lock at - # the same time (happened in Python 3.3 on Windows) - # https://ci.appveyor.com/project/pytestbot/py/build/1.0.21/job/ffi85j4c0lqwsfwa - if lastmax == maxnum: - raise - lastmax = maxnum - continue - break - - def get_mtime(path): - """Read file modification time""" - try: - return path.lstat().mtime - except error.Error: - pass - - garbage_prefix = prefix + "garbage-" - - def is_garbage(path): - """Check if path denotes directory scheduled for removal""" - bn = path.basename - return bn.startswith(garbage_prefix) - - # prune old directories - udir_time = get_mtime(udir) - if keep and udir_time: - for path in rootdir.listdir(): - num = parse_num(path) - if num is not None and num <= (maxnum - keep): - try: - # try acquiring lock to remove directory as exclusive user - if lock_timeout: - create_lockfile(path) - except (error.EEXIST, error.ENOENT, error.EBUSY): - path_time = get_mtime(path) - if not path_time: - # assume directory doesn't exist now - continue - if abs(udir_time - path_time) < lock_timeout: - # assume directory with lockfile exists - # and lock timeout hasn't expired yet - continue - - # path dir locked for exclusive use - # and scheduled for removal to avoid another thread/process - # treating it as a new directory or removal candidate - garbage_path = rootdir.join(garbage_prefix + str(uuid.uuid4())) - try: - path.rename(garbage_path) - garbage_path.remove(rec=1) - except KeyboardInterrupt: - raise - except Exception: # this might be error.Error, WindowsError ... - pass - if is_garbage(path): - try: - path.remove(rec=1) - except KeyboardInterrupt: - raise - except Exception: # this might be error.Error, WindowsError ... - pass - - # make link... - try: - username = os.environ["USER"] # linux, et al - except KeyError: - try: - username = os.environ["USERNAME"] # windows - except KeyError: - username = "current" - - src = str(udir) - dest = src[: src.rfind("-")] + "-" + username - try: - os.unlink(dest) - except OSError: - pass - try: - os.symlink(src, dest) - except (OSError, AttributeError, NotImplementedError): - pass - - return udir - - -def copymode(src, dest): - """Copy permission from src to dst.""" - import shutil - - shutil.copymode(src, dest) - - -def copystat(src, dest): - """Copy permission, last modification time, - last access time, and flags from src to dst.""" - import shutil - - shutil.copystat(str(src), str(dest)) - - -def copychunked(src, dest): - chunksize = 524288 # half a meg of bytes - fsrc = src.open("rb") - try: - fdest = dest.open("wb") - try: - while 1: - buf = fsrc.read(chunksize) - if not buf: - break - fdest.write(buf) - finally: - fdest.close() - finally: - fsrc.close() - - -def isimportable(name): - if name and (name[0].isalpha() or name[0] == "_"): - name = name.replace("_", "") - return not name or name.isalnum() - - -local = LocalPath diff --git a/.venv/lib/python3.12/site-packages/_pytest/_version.py b/.venv/lib/python3.12/site-packages/_pytest/_version.py deleted file mode 100644 index e88f96c7..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/_version.py +++ /dev/null @@ -1,16 +0,0 @@ -# file generated by setuptools_scm -# don't change, don't track in version control -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import Tuple, Union - VERSION_TUPLE = Tuple[Union[int, str], ...] -else: - VERSION_TUPLE = object - -version: str -__version__: str -__version_tuple__: VERSION_TUPLE -version_tuple: VERSION_TUPLE - -__version__ = version = '8.3.4' -__version_tuple__ = version_tuple = (8, 3, 4) diff --git a/.venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py deleted file mode 100644 index f2f1d029..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/assertion/__init__.py +++ /dev/null @@ -1,192 +0,0 @@ -# mypy: allow-untyped-defs -"""Support for presenting detailed information in failing assertions.""" - -from __future__ import annotations - -import sys -from typing import Any -from typing import Generator -from typing import TYPE_CHECKING - -from _pytest.assertion import rewrite -from _pytest.assertion import truncate -from _pytest.assertion import util -from _pytest.assertion.rewrite import assertstate_key -from _pytest.config import Config -from _pytest.config import hookimpl -from _pytest.config.argparsing import Parser -from _pytest.nodes import Item - - -if TYPE_CHECKING: - from _pytest.main import Session - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("debugconfig") - group.addoption( - "--assert", - action="store", - dest="assertmode", - choices=("rewrite", "plain"), - default="rewrite", - metavar="MODE", - help=( - "Control assertion debugging tools.\n" - "'plain' performs no assertion debugging.\n" - "'rewrite' (the default) rewrites assert statements in test modules" - " on import to provide assert expression information." - ), - ) - parser.addini( - "enable_assertion_pass_hook", - type="bool", - default=False, - help="Enables the pytest_assertion_pass hook. " - "Make sure to delete any previously generated pyc cache files.", - ) - Config._add_verbosity_ini( - parser, - Config.VERBOSITY_ASSERTIONS, - help=( - "Specify a verbosity level for assertions, overriding the main level. " - "Higher levels will provide more detailed explanation when an assertion fails." - ), - ) - - -def register_assert_rewrite(*names: str) -> None: - """Register one or more module names to be rewritten on import. - - This function will make sure that this module or all modules inside - the package will get their assert statements rewritten. - Thus you should make sure to call this before the module is - actually imported, usually in your __init__.py if you are a plugin - using a package. - - :param names: The module names to register. - """ - for name in names: - if not isinstance(name, str): - msg = "expected module names as *args, got {0} instead" # type: ignore[unreachable] - raise TypeError(msg.format(repr(names))) - for hook in sys.meta_path: - if isinstance(hook, rewrite.AssertionRewritingHook): - importhook = hook - break - else: - # TODO(typing): Add a protocol for mark_rewrite() and use it - # for importhook and for PytestPluginManager.rewrite_hook. - importhook = DummyRewriteHook() # type: ignore - importhook.mark_rewrite(*names) - - -class DummyRewriteHook: - """A no-op import hook for when rewriting is disabled.""" - - def mark_rewrite(self, *names: str) -> None: - pass - - -class AssertionState: - """State for the assertion plugin.""" - - def __init__(self, config: Config, mode) -> None: - self.mode = mode - self.trace = config.trace.root.get("assertion") - self.hook: rewrite.AssertionRewritingHook | None = None - - -def install_importhook(config: Config) -> rewrite.AssertionRewritingHook: - """Try to install the rewrite hook, raise SystemError if it fails.""" - config.stash[assertstate_key] = AssertionState(config, "rewrite") - config.stash[assertstate_key].hook = hook = rewrite.AssertionRewritingHook(config) - sys.meta_path.insert(0, hook) - config.stash[assertstate_key].trace("installed rewrite import hook") - - def undo() -> None: - hook = config.stash[assertstate_key].hook - if hook is not None and hook in sys.meta_path: - sys.meta_path.remove(hook) - - config.add_cleanup(undo) - return hook - - -def pytest_collection(session: Session) -> None: - # This hook is only called when test modules are collected - # so for example not in the managing process of pytest-xdist - # (which does not collect test modules). - assertstate = session.config.stash.get(assertstate_key, None) - if assertstate: - if assertstate.hook is not None: - assertstate.hook.set_session(session) - - -@hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: - """Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks. - - The rewrite module will use util._reprcompare if it exists to use custom - reporting via the pytest_assertrepr_compare hook. This sets up this custom - comparison for the test. - """ - ihook = item.ihook - - def callbinrepr(op, left: object, right: object) -> str | None: - """Call the pytest_assertrepr_compare hook and prepare the result. - - This uses the first result from the hook and then ensures the - following: - * Overly verbose explanations are truncated unless configured otherwise - (eg. if running in verbose mode). - * Embedded newlines are escaped to help util.format_explanation() - later. - * If the rewrite mode is used embedded %-characters are replaced - to protect later % formatting. - - The result can be formatted by util.format_explanation() for - pretty printing. - """ - hook_result = ihook.pytest_assertrepr_compare( - config=item.config, op=op, left=left, right=right - ) - for new_expl in hook_result: - if new_expl: - new_expl = truncate.truncate_if_required(new_expl, item) - new_expl = [line.replace("\n", "\\n") for line in new_expl] - res = "\n~".join(new_expl) - if item.config.getvalue("assertmode") == "rewrite": - res = res.replace("%", "%%") - return res - return None - - saved_assert_hooks = util._reprcompare, util._assertion_pass - util._reprcompare = callbinrepr - util._config = item.config - - if ihook.pytest_assertion_pass.get_hookimpls(): - - def call_assertion_pass_hook(lineno: int, orig: str, expl: str) -> None: - ihook.pytest_assertion_pass(item=item, lineno=lineno, orig=orig, expl=expl) - - util._assertion_pass = call_assertion_pass_hook - - try: - return (yield) - finally: - util._reprcompare, util._assertion_pass = saved_assert_hooks - util._config = None - - -def pytest_sessionfinish(session: Session) -> None: - assertstate = session.config.stash.get(assertstate_key, None) - if assertstate: - if assertstate.hook is not None: - assertstate.hook.set_session(None) - - -def pytest_assertrepr_compare( - config: Config, op: str, left: Any, right: Any -) -> list[str] | None: - return util.assertrepr_compare(config=config, op=op, left=left, right=right) diff --git a/.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py b/.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py deleted file mode 100644 index 37c09b03..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/assertion/rewrite.py +++ /dev/null @@ -1,1211 +0,0 @@ -"""Rewrite assertion AST to produce nice error messages.""" - -from __future__ import annotations - -import ast -from collections import defaultdict -import errno -import functools -import importlib.abc -import importlib.machinery -import importlib.util -import io -import itertools -import marshal -import os -from pathlib import Path -from pathlib import PurePath -import struct -import sys -import tokenize -import types -from typing import Callable -from typing import IO -from typing import Iterable -from typing import Iterator -from typing import Sequence -from typing import TYPE_CHECKING - -from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE -from _pytest._io.saferepr import saferepr -from _pytest._version import version -from _pytest.assertion import util -from _pytest.config import Config -from _pytest.main import Session -from _pytest.pathlib import absolutepath -from _pytest.pathlib import fnmatch_ex -from _pytest.stash import StashKey - - -# fmt: off -from _pytest.assertion.util import format_explanation as _format_explanation # noqa:F401, isort:skip -# fmt:on - -if TYPE_CHECKING: - from _pytest.assertion import AssertionState - - -class Sentinel: - pass - - -assertstate_key = StashKey["AssertionState"]() - -# pytest caches rewritten pycs in pycache dirs -PYTEST_TAG = f"{sys.implementation.cache_tag}-pytest-{version}" -PYC_EXT = ".py" + (__debug__ and "c" or "o") -PYC_TAIL = "." + PYTEST_TAG + PYC_EXT - -# Special marker that denotes we have just left a scope definition -_SCOPE_END_MARKER = Sentinel() - - -class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader): - """PEP302/PEP451 import hook which rewrites asserts.""" - - def __init__(self, config: Config) -> None: - self.config = config - try: - self.fnpats = config.getini("python_files") - except ValueError: - self.fnpats = ["test_*.py", "*_test.py"] - self.session: Session | None = None - self._rewritten_names: dict[str, Path] = {} - self._must_rewrite: set[str] = set() - # flag to guard against trying to rewrite a pyc file while we are already writing another pyc file, - # which might result in infinite recursion (#3506) - self._writing_pyc = False - self._basenames_to_check_rewrite = {"conftest"} - self._marked_for_rewrite_cache: dict[str, bool] = {} - self._session_paths_checked = False - - def set_session(self, session: Session | None) -> None: - self.session = session - self._session_paths_checked = False - - # Indirection so we can mock calls to find_spec originated from the hook during testing - _find_spec = importlib.machinery.PathFinder.find_spec - - def find_spec( - self, - name: str, - path: Sequence[str | bytes] | None = None, - target: types.ModuleType | None = None, - ) -> importlib.machinery.ModuleSpec | None: - if self._writing_pyc: - return None - state = self.config.stash[assertstate_key] - if self._early_rewrite_bailout(name, state): - return None - state.trace(f"find_module called for: {name}") - - # Type ignored because mypy is confused about the `self` binding here. - spec = self._find_spec(name, path) # type: ignore - - if spec is None and path is not None: - # With --import-mode=importlib, PathFinder cannot find spec without modifying `sys.path`, - # causing inability to assert rewriting (#12659). - # At this point, try using the file path to find the module spec. - for _path_str in path: - spec = importlib.util.spec_from_file_location(name, _path_str) - if spec is not None: - break - - if ( - # the import machinery could not find a file to import - spec is None - # this is a namespace package (without `__init__.py`) - # there's nothing to rewrite there - or spec.origin is None - # we can only rewrite source files - or not isinstance(spec.loader, importlib.machinery.SourceFileLoader) - # if the file doesn't exist, we can't rewrite it - or not os.path.exists(spec.origin) - ): - return None - else: - fn = spec.origin - - if not self._should_rewrite(name, fn, state): - return None - - return importlib.util.spec_from_file_location( - name, - fn, - loader=self, - submodule_search_locations=spec.submodule_search_locations, - ) - - def create_module( - self, spec: importlib.machinery.ModuleSpec - ) -> types.ModuleType | None: - return None # default behaviour is fine - - def exec_module(self, module: types.ModuleType) -> None: - assert module.__spec__ is not None - assert module.__spec__.origin is not None - fn = Path(module.__spec__.origin) - state = self.config.stash[assertstate_key] - - self._rewritten_names[module.__name__] = fn - - # The requested module looks like a test file, so rewrite it. This is - # the most magical part of the process: load the source, rewrite the - # asserts, and load the rewritten source. We also cache the rewritten - # module code in a special pyc. We must be aware of the possibility of - # concurrent pytest processes rewriting and loading pycs. To avoid - # tricky race conditions, we maintain the following invariant: The - # cached pyc is always a complete, valid pyc. Operations on it must be - # atomic. POSIX's atomic rename comes in handy. - write = not sys.dont_write_bytecode - cache_dir = get_cache_dir(fn) - if write: - ok = try_makedirs(cache_dir) - if not ok: - write = False - state.trace(f"read only directory: {cache_dir}") - - cache_name = fn.name[:-3] + PYC_TAIL - pyc = cache_dir / cache_name - # Notice that even if we're in a read-only directory, I'm going - # to check for a cached pyc. This may not be optimal... - co = _read_pyc(fn, pyc, state.trace) - if co is None: - state.trace(f"rewriting {fn!r}") - source_stat, co = _rewrite_test(fn, self.config) - if write: - self._writing_pyc = True - try: - _write_pyc(state, co, source_stat, pyc) - finally: - self._writing_pyc = False - else: - state.trace(f"found cached rewritten pyc for {fn}") - exec(co, module.__dict__) - - def _early_rewrite_bailout(self, name: str, state: AssertionState) -> bool: - """A fast way to get out of rewriting modules. - - Profiling has shown that the call to PathFinder.find_spec (inside of - the find_spec from this class) is a major slowdown, so, this method - tries to filter what we're sure won't be rewritten before getting to - it. - """ - if self.session is not None and not self._session_paths_checked: - self._session_paths_checked = True - for initial_path in self.session._initialpaths: - # Make something as c:/projects/my_project/path.py -> - # ['c:', 'projects', 'my_project', 'path.py'] - parts = str(initial_path).split(os.sep) - # add 'path' to basenames to be checked. - self._basenames_to_check_rewrite.add(os.path.splitext(parts[-1])[0]) - - # Note: conftest already by default in _basenames_to_check_rewrite. - parts = name.split(".") - if parts[-1] in self._basenames_to_check_rewrite: - return False - - # For matching the name it must be as if it was a filename. - path = PurePath(*parts).with_suffix(".py") - - for pat in self.fnpats: - # if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based - # on the name alone because we need to match against the full path - if os.path.dirname(pat): - return False - if fnmatch_ex(pat, path): - return False - - if self._is_marked_for_rewrite(name, state): - return False - - state.trace(f"early skip of rewriting module: {name}") - return True - - def _should_rewrite(self, name: str, fn: str, state: AssertionState) -> bool: - # always rewrite conftest files - if os.path.basename(fn) == "conftest.py": - state.trace(f"rewriting conftest file: {fn!r}") - return True - - if self.session is not None: - if self.session.isinitpath(absolutepath(fn)): - state.trace(f"matched test file (was specified on cmdline): {fn!r}") - return True - - # modules not passed explicitly on the command line are only - # rewritten if they match the naming convention for test files - fn_path = PurePath(fn) - for pat in self.fnpats: - if fnmatch_ex(pat, fn_path): - state.trace(f"matched test file {fn!r}") - return True - - return self._is_marked_for_rewrite(name, state) - - def _is_marked_for_rewrite(self, name: str, state: AssertionState) -> bool: - try: - return self._marked_for_rewrite_cache[name] - except KeyError: - for marked in self._must_rewrite: - if name == marked or name.startswith(marked + "."): - state.trace(f"matched marked file {name!r} (from {marked!r})") - self._marked_for_rewrite_cache[name] = True - return True - - self._marked_for_rewrite_cache[name] = False - return False - - def mark_rewrite(self, *names: str) -> None: - """Mark import names as needing to be rewritten. - - The named module or package as well as any nested modules will - be rewritten on import. - """ - already_imported = ( - set(names).intersection(sys.modules).difference(self._rewritten_names) - ) - for name in already_imported: - mod = sys.modules[name] - if not AssertionRewriter.is_rewrite_disabled( - mod.__doc__ or "" - ) and not isinstance(mod.__loader__, type(self)): - self._warn_already_imported(name) - self._must_rewrite.update(names) - self._marked_for_rewrite_cache.clear() - - def _warn_already_imported(self, name: str) -> None: - from _pytest.warning_types import PytestAssertRewriteWarning - - self.config.issue_config_time_warning( - PytestAssertRewriteWarning( - f"Module already imported so cannot be rewritten: {name}" - ), - stacklevel=5, - ) - - def get_data(self, pathname: str | bytes) -> bytes: - """Optional PEP302 get_data API.""" - with open(pathname, "rb") as f: - return f.read() - - if sys.version_info >= (3, 10): - if sys.version_info >= (3, 12): - from importlib.resources.abc import TraversableResources - else: - from importlib.abc import TraversableResources - - def get_resource_reader(self, name: str) -> TraversableResources: - if sys.version_info < (3, 11): - from importlib.readers import FileReader - else: - from importlib.resources.readers import FileReader - - return FileReader(types.SimpleNamespace(path=self._rewritten_names[name])) - - -def _write_pyc_fp( - fp: IO[bytes], source_stat: os.stat_result, co: types.CodeType -) -> None: - # Technically, we don't have to have the same pyc format as - # (C)Python, since these "pycs" should never be seen by builtin - # import. However, there's little reason to deviate. - fp.write(importlib.util.MAGIC_NUMBER) - # https://www.python.org/dev/peps/pep-0552/ - flags = b"\x00\x00\x00\x00" - fp.write(flags) - # as of now, bytecode header expects 32-bit numbers for size and mtime (#4903) - mtime = int(source_stat.st_mtime) & 0xFFFFFFFF - size = source_stat.st_size & 0xFFFFFFFF - # " bool: - proc_pyc = f"{pyc}.{os.getpid()}" - try: - with open(proc_pyc, "wb") as fp: - _write_pyc_fp(fp, source_stat, co) - except OSError as e: - state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}") - return False - - try: - os.replace(proc_pyc, pyc) - except OSError as e: - state.trace(f"error writing pyc file at {pyc}: {e}") - # we ignore any failure to write the cache file - # there are many reasons, permission-denied, pycache dir being a - # file etc. - return False - return True - - -def _rewrite_test(fn: Path, config: Config) -> tuple[os.stat_result, types.CodeType]: - """Read and rewrite *fn* and return the code object.""" - stat = os.stat(fn) - source = fn.read_bytes() - strfn = str(fn) - tree = ast.parse(source, filename=strfn) - rewrite_asserts(tree, source, strfn, config) - co = compile(tree, strfn, "exec", dont_inherit=True) - return stat, co - - -def _read_pyc( - source: Path, pyc: Path, trace: Callable[[str], None] = lambda x: None -) -> types.CodeType | None: - """Possibly read a pytest pyc containing rewritten code. - - Return rewritten code if successful or None if not. - """ - try: - fp = open(pyc, "rb") - except OSError: - return None - with fp: - try: - stat_result = os.stat(source) - mtime = int(stat_result.st_mtime) - size = stat_result.st_size - data = fp.read(16) - except OSError as e: - trace(f"_read_pyc({source}): OSError {e}") - return None - # Check for invalid or out of date pyc file. - if len(data) != (16): - trace(f"_read_pyc({source}): invalid pyc (too short)") - return None - if data[:4] != importlib.util.MAGIC_NUMBER: - trace(f"_read_pyc({source}): invalid pyc (bad magic number)") - return None - if data[4:8] != b"\x00\x00\x00\x00": - trace(f"_read_pyc({source}): invalid pyc (unsupported flags)") - return None - mtime_data = data[8:12] - if int.from_bytes(mtime_data, "little") != mtime & 0xFFFFFFFF: - trace(f"_read_pyc({source}): out of date") - return None - size_data = data[12:16] - if int.from_bytes(size_data, "little") != size & 0xFFFFFFFF: - trace(f"_read_pyc({source}): invalid pyc (incorrect size)") - return None - try: - co = marshal.load(fp) - except Exception as e: - trace(f"_read_pyc({source}): marshal.load error {e}") - return None - if not isinstance(co, types.CodeType): - trace(f"_read_pyc({source}): not a code object") - return None - return co - - -def rewrite_asserts( - mod: ast.Module, - source: bytes, - module_path: str | None = None, - config: Config | None = None, -) -> None: - """Rewrite the assert statements in mod.""" - AssertionRewriter(module_path, config, source).run(mod) - - -def _saferepr(obj: object) -> str: - r"""Get a safe repr of an object for assertion error messages. - - The assertion formatting (util.format_explanation()) requires - newlines to be escaped since they are a special character for it. - Normally assertion.util.format_explanation() does this but for a - custom repr it is possible to contain one of the special escape - sequences, especially '\n{' and '\n}' are likely to be present in - JSON reprs. - """ - if isinstance(obj, types.MethodType): - # for bound methods, skip redundant information - return obj.__name__ - - maxsize = _get_maxsize_for_saferepr(util._config) - return saferepr(obj, maxsize=maxsize).replace("\n", "\\n") - - -def _get_maxsize_for_saferepr(config: Config | None) -> int | None: - """Get `maxsize` configuration for saferepr based on the given config object.""" - if config is None: - verbosity = 0 - else: - verbosity = config.get_verbosity(Config.VERBOSITY_ASSERTIONS) - if verbosity >= 2: - return None - if verbosity >= 1: - return DEFAULT_REPR_MAX_SIZE * 10 - return DEFAULT_REPR_MAX_SIZE - - -def _format_assertmsg(obj: object) -> str: - r"""Format the custom assertion message given. - - For strings this simply replaces newlines with '\n~' so that - util.format_explanation() will preserve them instead of escaping - newlines. For other objects saferepr() is used first. - """ - # reprlib appears to have a bug which means that if a string - # contains a newline it gets escaped, however if an object has a - # .__repr__() which contains newlines it does not get escaped. - # However in either case we want to preserve the newline. - replaces = [("\n", "\n~"), ("%", "%%")] - if not isinstance(obj, str): - obj = saferepr(obj, _get_maxsize_for_saferepr(util._config)) - replaces.append(("\\n", "\n~")) - - for r1, r2 in replaces: - obj = obj.replace(r1, r2) - - return obj - - -def _should_repr_global_name(obj: object) -> bool: - if callable(obj): - return False - - try: - return not hasattr(obj, "__name__") - except Exception: - return True - - -def _format_boolop(explanations: Iterable[str], is_or: bool) -> str: - explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")" - return explanation.replace("%", "%%") - - -def _call_reprcompare( - ops: Sequence[str], - results: Sequence[bool], - expls: Sequence[str], - each_obj: Sequence[object], -) -> str: - for i, res, expl in zip(range(len(ops)), results, expls): - try: - done = not res - except Exception: - done = True - if done: - break - if util._reprcompare is not None: - custom = util._reprcompare(ops[i], each_obj[i], each_obj[i + 1]) - if custom is not None: - return custom - return expl - - -def _call_assertion_pass(lineno: int, orig: str, expl: str) -> None: - if util._assertion_pass is not None: - util._assertion_pass(lineno, orig, expl) - - -def _check_if_assertion_pass_impl() -> bool: - """Check if any plugins implement the pytest_assertion_pass hook - in order not to generate explanation unnecessarily (might be expensive).""" - return True if util._assertion_pass else False - - -UNARY_MAP = {ast.Not: "not %s", ast.Invert: "~%s", ast.USub: "-%s", ast.UAdd: "+%s"} - -BINOP_MAP = { - ast.BitOr: "|", - ast.BitXor: "^", - ast.BitAnd: "&", - ast.LShift: "<<", - ast.RShift: ">>", - ast.Add: "+", - ast.Sub: "-", - ast.Mult: "*", - ast.Div: "/", - ast.FloorDiv: "//", - ast.Mod: "%%", # escaped for string formatting - ast.Eq: "==", - ast.NotEq: "!=", - ast.Lt: "<", - ast.LtE: "<=", - ast.Gt: ">", - ast.GtE: ">=", - ast.Pow: "**", - ast.Is: "is", - ast.IsNot: "is not", - ast.In: "in", - ast.NotIn: "not in", - ast.MatMult: "@", -} - - -def traverse_node(node: ast.AST) -> Iterator[ast.AST]: - """Recursively yield node and all its children in depth-first order.""" - yield node - for child in ast.iter_child_nodes(node): - yield from traverse_node(child) - - -@functools.lru_cache(maxsize=1) -def _get_assertion_exprs(src: bytes) -> dict[int, str]: - """Return a mapping from {lineno: "assertion test expression"}.""" - ret: dict[int, str] = {} - - depth = 0 - lines: list[str] = [] - assert_lineno: int | None = None - seen_lines: set[int] = set() - - def _write_and_reset() -> None: - nonlocal depth, lines, assert_lineno, seen_lines - assert assert_lineno is not None - ret[assert_lineno] = "".join(lines).rstrip().rstrip("\\") - depth = 0 - lines = [] - assert_lineno = None - seen_lines = set() - - tokens = tokenize.tokenize(io.BytesIO(src).readline) - for tp, source, (lineno, offset), _, line in tokens: - if tp == tokenize.NAME and source == "assert": - assert_lineno = lineno - elif assert_lineno is not None: - # keep track of depth for the assert-message `,` lookup - if tp == tokenize.OP and source in "([{": - depth += 1 - elif tp == tokenize.OP and source in ")]}": - depth -= 1 - - if not lines: - lines.append(line[offset:]) - seen_lines.add(lineno) - # a non-nested comma separates the expression from the message - elif depth == 0 and tp == tokenize.OP and source == ",": - # one line assert with message - if lineno in seen_lines and len(lines) == 1: - offset_in_trimmed = offset + len(lines[-1]) - len(line) - lines[-1] = lines[-1][:offset_in_trimmed] - # multi-line assert with message - elif lineno in seen_lines: - lines[-1] = lines[-1][:offset] - # multi line assert with escaped newline before message - else: - lines.append(line[:offset]) - _write_and_reset() - elif tp in {tokenize.NEWLINE, tokenize.ENDMARKER}: - _write_and_reset() - elif lines and lineno not in seen_lines: - lines.append(line) - seen_lines.add(lineno) - - return ret - - -class AssertionRewriter(ast.NodeVisitor): - """Assertion rewriting implementation. - - The main entrypoint is to call .run() with an ast.Module instance, - this will then find all the assert statements and rewrite them to - provide intermediate values and a detailed assertion error. See - http://pybites.blogspot.be/2011/07/behind-scenes-of-pytests-new-assertion.html - for an overview of how this works. - - The entry point here is .run() which will iterate over all the - statements in an ast.Module and for each ast.Assert statement it - finds call .visit() with it. Then .visit_Assert() takes over and - is responsible for creating new ast statements to replace the - original assert statement: it rewrites the test of an assertion - to provide intermediate values and replace it with an if statement - which raises an assertion error with a detailed explanation in - case the expression is false and calls pytest_assertion_pass hook - if expression is true. - - For this .visit_Assert() uses the visitor pattern to visit all the - AST nodes of the ast.Assert.test field, each visit call returning - an AST node and the corresponding explanation string. During this - state is kept in several instance attributes: - - :statements: All the AST statements which will replace the assert - statement. - - :variables: This is populated by .variable() with each variable - used by the statements so that they can all be set to None at - the end of the statements. - - :variable_counter: Counter to create new unique variables needed - by statements. Variables are created using .variable() and - have the form of "@py_assert0". - - :expl_stmts: The AST statements which will be executed to get - data from the assertion. This is the code which will construct - the detailed assertion message that is used in the AssertionError - or for the pytest_assertion_pass hook. - - :explanation_specifiers: A dict filled by .explanation_param() - with %-formatting placeholders and their corresponding - expressions to use in the building of an assertion message. - This is used by .pop_format_context() to build a message. - - :stack: A stack of the explanation_specifiers dicts maintained by - .push_format_context() and .pop_format_context() which allows - to build another %-formatted string while already building one. - - :scope: A tuple containing the current scope used for variables_overwrite. - - :variables_overwrite: A dict filled with references to variables - that change value within an assert. This happens when a variable is - reassigned with the walrus operator - - This state, except the variables_overwrite, is reset on every new assert - statement visited and used by the other visitors. - """ - - def __init__( - self, module_path: str | None, config: Config | None, source: bytes - ) -> None: - super().__init__() - self.module_path = module_path - self.config = config - if config is not None: - self.enable_assertion_pass_hook = config.getini( - "enable_assertion_pass_hook" - ) - else: - self.enable_assertion_pass_hook = False - self.source = source - self.scope: tuple[ast.AST, ...] = () - self.variables_overwrite: defaultdict[tuple[ast.AST, ...], dict[str, str]] = ( - defaultdict(dict) - ) - - def run(self, mod: ast.Module) -> None: - """Find all assert statements in *mod* and rewrite them.""" - if not mod.body: - # Nothing to do. - return - - # We'll insert some special imports at the top of the module, but after any - # docstrings and __future__ imports, so first figure out where that is. - doc = getattr(mod, "docstring", None) - expect_docstring = doc is None - if doc is not None and self.is_rewrite_disabled(doc): - return - pos = 0 - item = None - for item in mod.body: - if ( - expect_docstring - and isinstance(item, ast.Expr) - and isinstance(item.value, ast.Constant) - and isinstance(item.value.value, str) - ): - doc = item.value.value - if self.is_rewrite_disabled(doc): - return - expect_docstring = False - elif ( - isinstance(item, ast.ImportFrom) - and item.level == 0 - and item.module == "__future__" - ): - pass - else: - break - pos += 1 - # Special case: for a decorated function, set the lineno to that of the - # first decorator, not the `def`. Issue #4984. - if isinstance(item, ast.FunctionDef) and item.decorator_list: - lineno = item.decorator_list[0].lineno - else: - lineno = item.lineno - # Now actually insert the special imports. - if sys.version_info >= (3, 10): - aliases = [ - ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0), - ast.alias( - "_pytest.assertion.rewrite", - "@pytest_ar", - lineno=lineno, - col_offset=0, - ), - ] - else: - aliases = [ - ast.alias("builtins", "@py_builtins"), - ast.alias("_pytest.assertion.rewrite", "@pytest_ar"), - ] - imports = [ - ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases - ] - mod.body[pos:pos] = imports - - # Collect asserts. - self.scope = (mod,) - nodes: list[ast.AST | Sentinel] = [mod] - while nodes: - node = nodes.pop() - if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): - self.scope = tuple((*self.scope, node)) - nodes.append(_SCOPE_END_MARKER) - if node == _SCOPE_END_MARKER: - self.scope = self.scope[:-1] - continue - assert isinstance(node, ast.AST) - for name, field in ast.iter_fields(node): - if isinstance(field, list): - new: list[ast.AST] = [] - for i, child in enumerate(field): - if isinstance(child, ast.Assert): - # Transform assert. - new.extend(self.visit(child)) - else: - new.append(child) - if isinstance(child, ast.AST): - nodes.append(child) - setattr(node, name, new) - elif ( - isinstance(field, ast.AST) - # Don't recurse into expressions as they can't contain - # asserts. - and not isinstance(field, ast.expr) - ): - nodes.append(field) - - @staticmethod - def is_rewrite_disabled(docstring: str) -> bool: - return "PYTEST_DONT_REWRITE" in docstring - - def variable(self) -> str: - """Get a new variable.""" - # Use a character invalid in python identifiers to avoid clashing. - name = "@py_assert" + str(next(self.variable_counter)) - self.variables.append(name) - return name - - def assign(self, expr: ast.expr) -> ast.Name: - """Give *expr* a name.""" - name = self.variable() - self.statements.append(ast.Assign([ast.Name(name, ast.Store())], expr)) - return ast.copy_location(ast.Name(name, ast.Load()), expr) - - def display(self, expr: ast.expr) -> ast.expr: - """Call saferepr on the expression.""" - return self.helper("_saferepr", expr) - - def helper(self, name: str, *args: ast.expr) -> ast.expr: - """Call a helper in this module.""" - py_name = ast.Name("@pytest_ar", ast.Load()) - attr = ast.Attribute(py_name, name, ast.Load()) - return ast.Call(attr, list(args), []) - - def builtin(self, name: str) -> ast.Attribute: - """Return the builtin called *name*.""" - builtin_name = ast.Name("@py_builtins", ast.Load()) - return ast.Attribute(builtin_name, name, ast.Load()) - - def explanation_param(self, expr: ast.expr) -> str: - """Return a new named %-formatting placeholder for expr. - - This creates a %-formatting placeholder for expr in the - current formatting context, e.g. ``%(py0)s``. The placeholder - and expr are placed in the current format context so that it - can be used on the next call to .pop_format_context(). - """ - specifier = "py" + str(next(self.variable_counter)) - self.explanation_specifiers[specifier] = expr - return "%(" + specifier + ")s" - - def push_format_context(self) -> None: - """Create a new formatting context. - - The format context is used for when an explanation wants to - have a variable value formatted in the assertion message. In - this case the value required can be added using - .explanation_param(). Finally .pop_format_context() is used - to format a string of %-formatted values as added by - .explanation_param(). - """ - self.explanation_specifiers: dict[str, ast.expr] = {} - self.stack.append(self.explanation_specifiers) - - def pop_format_context(self, expl_expr: ast.expr) -> ast.Name: - """Format the %-formatted string with current format context. - - The expl_expr should be an str ast.expr instance constructed from - the %-placeholders created by .explanation_param(). This will - add the required code to format said string to .expl_stmts and - return the ast.Name instance of the formatted string. - """ - current = self.stack.pop() - if self.stack: - self.explanation_specifiers = self.stack[-1] - keys: list[ast.expr | None] = [ast.Constant(key) for key in current.keys()] - format_dict = ast.Dict(keys, list(current.values())) - form = ast.BinOp(expl_expr, ast.Mod(), format_dict) - name = "@py_format" + str(next(self.variable_counter)) - if self.enable_assertion_pass_hook: - self.format_variables.append(name) - self.expl_stmts.append(ast.Assign([ast.Name(name, ast.Store())], form)) - return ast.Name(name, ast.Load()) - - def generic_visit(self, node: ast.AST) -> tuple[ast.Name, str]: - """Handle expressions we don't have custom code for.""" - assert isinstance(node, ast.expr) - res = self.assign(node) - return res, self.explanation_param(self.display(res)) - - def visit_Assert(self, assert_: ast.Assert) -> list[ast.stmt]: - """Return the AST statements to replace the ast.Assert instance. - - This rewrites the test of an assertion to provide - intermediate values and replace it with an if statement which - raises an assertion error with a detailed explanation in case - the expression is false. - """ - if isinstance(assert_.test, ast.Tuple) and len(assert_.test.elts) >= 1: - import warnings - - from _pytest.warning_types import PytestAssertRewriteWarning - - # TODO: This assert should not be needed. - assert self.module_path is not None - warnings.warn_explicit( - PytestAssertRewriteWarning( - "assertion is always true, perhaps remove parentheses?" - ), - category=None, - filename=self.module_path, - lineno=assert_.lineno, - ) - - self.statements: list[ast.stmt] = [] - self.variables: list[str] = [] - self.variable_counter = itertools.count() - - if self.enable_assertion_pass_hook: - self.format_variables: list[str] = [] - - self.stack: list[dict[str, ast.expr]] = [] - self.expl_stmts: list[ast.stmt] = [] - self.push_format_context() - # Rewrite assert into a bunch of statements. - top_condition, explanation = self.visit(assert_.test) - - negation = ast.UnaryOp(ast.Not(), top_condition) - - if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook - msg = self.pop_format_context(ast.Constant(explanation)) - - # Failed - if assert_.msg: - assertmsg = self.helper("_format_assertmsg", assert_.msg) - gluestr = "\n>assert " - else: - assertmsg = ast.Constant("") - gluestr = "assert " - err_explanation = ast.BinOp(ast.Constant(gluestr), ast.Add(), msg) - err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation) - err_name = ast.Name("AssertionError", ast.Load()) - fmt = self.helper("_format_explanation", err_msg) - exc = ast.Call(err_name, [fmt], []) - raise_ = ast.Raise(exc, None) - statements_fail = [] - statements_fail.extend(self.expl_stmts) - statements_fail.append(raise_) - - # Passed - fmt_pass = self.helper("_format_explanation", msg) - orig = _get_assertion_exprs(self.source)[assert_.lineno] - hook_call_pass = ast.Expr( - self.helper( - "_call_assertion_pass", - ast.Constant(assert_.lineno), - ast.Constant(orig), - fmt_pass, - ) - ) - # If any hooks implement assert_pass hook - hook_impl_test = ast.If( - self.helper("_check_if_assertion_pass_impl"), - [*self.expl_stmts, hook_call_pass], - [], - ) - statements_pass: list[ast.stmt] = [hook_impl_test] - - # Test for assertion condition - main_test = ast.If(negation, statements_fail, statements_pass) - self.statements.append(main_test) - if self.format_variables: - variables: list[ast.expr] = [ - ast.Name(name, ast.Store()) for name in self.format_variables - ] - clear_format = ast.Assign(variables, ast.Constant(None)) - self.statements.append(clear_format) - - else: # Original assertion rewriting - # Create failure message. - body = self.expl_stmts - self.statements.append(ast.If(negation, body, [])) - if assert_.msg: - assertmsg = self.helper("_format_assertmsg", assert_.msg) - explanation = "\n>assert " + explanation - else: - assertmsg = ast.Constant("") - explanation = "assert " + explanation - template = ast.BinOp(assertmsg, ast.Add(), ast.Constant(explanation)) - msg = self.pop_format_context(template) - fmt = self.helper("_format_explanation", msg) - err_name = ast.Name("AssertionError", ast.Load()) - exc = ast.Call(err_name, [fmt], []) - raise_ = ast.Raise(exc, None) - - body.append(raise_) - - # Clear temporary variables by setting them to None. - if self.variables: - variables = [ast.Name(name, ast.Store()) for name in self.variables] - clear = ast.Assign(variables, ast.Constant(None)) - self.statements.append(clear) - # Fix locations (line numbers/column offsets). - for stmt in self.statements: - for node in traverse_node(stmt): - if getattr(node, "lineno", None) is None: - # apply the assertion location to all generated ast nodes without source location - # and preserve the location of existing nodes or generated nodes with an correct location. - ast.copy_location(node, assert_) - return self.statements - - def visit_NamedExpr(self, name: ast.NamedExpr) -> tuple[ast.NamedExpr, str]: - # This method handles the 'walrus operator' repr of the target - # name if it's a local variable or _should_repr_global_name() - # thinks it's acceptable. - locs = ast.Call(self.builtin("locals"), [], []) - target_id = name.target.id - inlocs = ast.Compare(ast.Constant(target_id), [ast.In()], [locs]) - dorepr = self.helper("_should_repr_global_name", name) - test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) - expr = ast.IfExp(test, self.display(name), ast.Constant(target_id)) - return name, self.explanation_param(expr) - - def visit_Name(self, name: ast.Name) -> tuple[ast.Name, str]: - # Display the repr of the name if it's a local variable or - # _should_repr_global_name() thinks it's acceptable. - locs = ast.Call(self.builtin("locals"), [], []) - inlocs = ast.Compare(ast.Constant(name.id), [ast.In()], [locs]) - dorepr = self.helper("_should_repr_global_name", name) - test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) - expr = ast.IfExp(test, self.display(name), ast.Constant(name.id)) - return name, self.explanation_param(expr) - - def visit_BoolOp(self, boolop: ast.BoolOp) -> tuple[ast.Name, str]: - res_var = self.variable() - expl_list = self.assign(ast.List([], ast.Load())) - app = ast.Attribute(expl_list, "append", ast.Load()) - is_or = int(isinstance(boolop.op, ast.Or)) - body = save = self.statements - fail_save = self.expl_stmts - levels = len(boolop.values) - 1 - self.push_format_context() - # Process each operand, short-circuiting if needed. - for i, v in enumerate(boolop.values): - if i: - fail_inner: list[ast.stmt] = [] - # cond is set in a prior loop iteration below - self.expl_stmts.append(ast.If(cond, fail_inner, [])) # noqa: F821 - self.expl_stmts = fail_inner - # Check if the left operand is a ast.NamedExpr and the value has already been visited - if ( - isinstance(v, ast.Compare) - and isinstance(v.left, ast.NamedExpr) - and v.left.target.id - in [ - ast_expr.id - for ast_expr in boolop.values[:i] - if hasattr(ast_expr, "id") - ] - ): - pytest_temp = self.variable() - self.variables_overwrite[self.scope][v.left.target.id] = v.left # type:ignore[assignment] - v.left.target.id = pytest_temp - self.push_format_context() - res, expl = self.visit(v) - body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) - expl_format = self.pop_format_context(ast.Constant(expl)) - call = ast.Call(app, [expl_format], []) - self.expl_stmts.append(ast.Expr(call)) - if i < levels: - cond: ast.expr = res - if is_or: - cond = ast.UnaryOp(ast.Not(), cond) - inner: list[ast.stmt] = [] - self.statements.append(ast.If(cond, inner, [])) - self.statements = body = inner - self.statements = save - self.expl_stmts = fail_save - expl_template = self.helper("_format_boolop", expl_list, ast.Constant(is_or)) - expl = self.pop_format_context(expl_template) - return ast.Name(res_var, ast.Load()), self.explanation_param(expl) - - def visit_UnaryOp(self, unary: ast.UnaryOp) -> tuple[ast.Name, str]: - pattern = UNARY_MAP[unary.op.__class__] - operand_res, operand_expl = self.visit(unary.operand) - res = self.assign(ast.copy_location(ast.UnaryOp(unary.op, operand_res), unary)) - return res, pattern % (operand_expl,) - - def visit_BinOp(self, binop: ast.BinOp) -> tuple[ast.Name, str]: - symbol = BINOP_MAP[binop.op.__class__] - left_expr, left_expl = self.visit(binop.left) - right_expr, right_expl = self.visit(binop.right) - explanation = f"({left_expl} {symbol} {right_expl})" - res = self.assign( - ast.copy_location(ast.BinOp(left_expr, binop.op, right_expr), binop) - ) - return res, explanation - - def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: - new_func, func_expl = self.visit(call.func) - arg_expls = [] - new_args = [] - new_kwargs = [] - for arg in call.args: - if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get( - self.scope, {} - ): - arg = self.variables_overwrite[self.scope][arg.id] # type:ignore[assignment] - res, expl = self.visit(arg) - arg_expls.append(expl) - new_args.append(res) - for keyword in call.keywords: - if isinstance( - keyword.value, ast.Name - ) and keyword.value.id in self.variables_overwrite.get(self.scope, {}): - keyword.value = self.variables_overwrite[self.scope][keyword.value.id] # type:ignore[assignment] - res, expl = self.visit(keyword.value) - new_kwargs.append(ast.keyword(keyword.arg, res)) - if keyword.arg: - arg_expls.append(keyword.arg + "=" + expl) - else: # **args have `arg` keywords with an .arg of None - arg_expls.append("**" + expl) - - expl = "{}({})".format(func_expl, ", ".join(arg_expls)) - new_call = ast.copy_location(ast.Call(new_func, new_args, new_kwargs), call) - res = self.assign(new_call) - res_expl = self.explanation_param(self.display(res)) - outer_expl = f"{res_expl}\n{{{res_expl} = {expl}\n}}" - return res, outer_expl - - def visit_Starred(self, starred: ast.Starred) -> tuple[ast.Starred, str]: - # A Starred node can appear in a function call. - res, expl = self.visit(starred.value) - new_starred = ast.Starred(res, starred.ctx) - return new_starred, "*" + expl - - def visit_Attribute(self, attr: ast.Attribute) -> tuple[ast.Name, str]: - if not isinstance(attr.ctx, ast.Load): - return self.generic_visit(attr) - value, value_expl = self.visit(attr.value) - res = self.assign( - ast.copy_location(ast.Attribute(value, attr.attr, ast.Load()), attr) - ) - res_expl = self.explanation_param(self.display(res)) - pat = "%s\n{%s = %s.%s\n}" - expl = pat % (res_expl, res_expl, value_expl, attr.attr) - return res, expl - - def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: - self.push_format_context() - # We first check if we have overwritten a variable in the previous assert - if isinstance( - comp.left, ast.Name - ) and comp.left.id in self.variables_overwrite.get(self.scope, {}): - comp.left = self.variables_overwrite[self.scope][comp.left.id] # type:ignore[assignment] - if isinstance(comp.left, ast.NamedExpr): - self.variables_overwrite[self.scope][comp.left.target.id] = comp.left # type:ignore[assignment] - left_res, left_expl = self.visit(comp.left) - if isinstance(comp.left, (ast.Compare, ast.BoolOp)): - left_expl = f"({left_expl})" - res_variables = [self.variable() for i in range(len(comp.ops))] - load_names: list[ast.expr] = [ast.Name(v, ast.Load()) for v in res_variables] - store_names = [ast.Name(v, ast.Store()) for v in res_variables] - it = zip(range(len(comp.ops)), comp.ops, comp.comparators) - expls: list[ast.expr] = [] - syms: list[ast.expr] = [] - results = [left_res] - for i, op, next_operand in it: - if ( - isinstance(next_operand, ast.NamedExpr) - and isinstance(left_res, ast.Name) - and next_operand.target.id == left_res.id - ): - next_operand.target.id = self.variable() - self.variables_overwrite[self.scope][left_res.id] = next_operand # type:ignore[assignment] - next_res, next_expl = self.visit(next_operand) - if isinstance(next_operand, (ast.Compare, ast.BoolOp)): - next_expl = f"({next_expl})" - results.append(next_res) - sym = BINOP_MAP[op.__class__] - syms.append(ast.Constant(sym)) - expl = f"{left_expl} {sym} {next_expl}" - expls.append(ast.Constant(expl)) - res_expr = ast.copy_location(ast.Compare(left_res, [op], [next_res]), comp) - self.statements.append(ast.Assign([store_names[i]], res_expr)) - left_res, left_expl = next_res, next_expl - # Use pytest.assertion.util._reprcompare if that's available. - expl_call = self.helper( - "_call_reprcompare", - ast.Tuple(syms, ast.Load()), - ast.Tuple(load_names, ast.Load()), - ast.Tuple(expls, ast.Load()), - ast.Tuple(results, ast.Load()), - ) - if len(comp.ops) > 1: - res: ast.expr = ast.BoolOp(ast.And(), load_names) - else: - res = load_names[0] - - return res, self.explanation_param(self.pop_format_context(expl_call)) - - -def try_makedirs(cache_dir: Path) -> bool: - """Attempt to create the given directory and sub-directories exist. - - Returns True if successful or if it already exists. - """ - try: - os.makedirs(cache_dir, exist_ok=True) - except (FileNotFoundError, NotADirectoryError, FileExistsError): - # One of the path components was not a directory: - # - we're in a zip file - # - it is a file - return False - except PermissionError: - return False - except OSError as e: - # as of now, EROFS doesn't have an equivalent OSError-subclass - # - # squashfuse_ll returns ENOSYS "OSError: [Errno 38] Function not - # implemented" for a read-only error - if e.errno in {errno.EROFS, errno.ENOSYS}: - return False - raise - return True - - -def get_cache_dir(file_path: Path) -> Path: - """Return the cache directory to write .pyc files for the given .py file path.""" - if sys.pycache_prefix: - # given: - # prefix = '/tmp/pycs' - # path = '/home/user/proj/test_app.py' - # we want: - # '/tmp/pycs/home/user/proj' - return Path(sys.pycache_prefix) / Path(*file_path.parts[1:-1]) - else: - # classic pycache directory - return file_path.parent / "__pycache__" diff --git a/.venv/lib/python3.12/site-packages/_pytest/assertion/truncate.py b/.venv/lib/python3.12/site-packages/_pytest/assertion/truncate.py deleted file mode 100644 index b67f02cc..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/assertion/truncate.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Utilities for truncating assertion output. - -Current default behaviour is to truncate assertion explanations at -terminal lines, unless running with an assertions verbosity level of at least 2 or running on CI. -""" - -from __future__ import annotations - -from _pytest.assertion import util -from _pytest.config import Config -from _pytest.nodes import Item - - -DEFAULT_MAX_LINES = 8 -DEFAULT_MAX_CHARS = 8 * 80 -USAGE_MSG = "use '-vv' to show" - - -def truncate_if_required( - explanation: list[str], item: Item, max_length: int | None = None -) -> list[str]: - """Truncate this assertion explanation if the given test item is eligible.""" - if _should_truncate_item(item): - return _truncate_explanation(explanation) - return explanation - - -def _should_truncate_item(item: Item) -> bool: - """Whether or not this test item is eligible for truncation.""" - verbose = item.config.get_verbosity(Config.VERBOSITY_ASSERTIONS) - return verbose < 2 and not util.running_on_ci() - - -def _truncate_explanation( - input_lines: list[str], - max_lines: int | None = None, - max_chars: int | None = None, -) -> list[str]: - """Truncate given list of strings that makes up the assertion explanation. - - Truncates to either 8 lines, or 640 characters - whichever the input reaches - first, taking the truncation explanation into account. The remaining lines - will be replaced by a usage message. - """ - if max_lines is None: - max_lines = DEFAULT_MAX_LINES - if max_chars is None: - max_chars = DEFAULT_MAX_CHARS - - # Check if truncation required - input_char_count = len("".join(input_lines)) - # The length of the truncation explanation depends on the number of lines - # removed but is at least 68 characters: - # The real value is - # 64 (for the base message: - # '...\n...Full output truncated (1 line hidden), use '-vv' to show")' - # ) - # + 1 (for plural) - # + int(math.log10(len(input_lines) - max_lines)) (number of hidden line, at least 1) - # + 3 for the '...' added to the truncated line - # But if there's more than 100 lines it's very likely that we're going to - # truncate, so we don't need the exact value using log10. - tolerable_max_chars = ( - max_chars + 70 # 64 + 1 (for plural) + 2 (for '99') + 3 for '...' - ) - # The truncation explanation add two lines to the output - tolerable_max_lines = max_lines + 2 - if ( - len(input_lines) <= tolerable_max_lines - and input_char_count <= tolerable_max_chars - ): - return input_lines - # Truncate first to max_lines, and then truncate to max_chars if necessary - truncated_explanation = input_lines[:max_lines] - truncated_char = True - # We reevaluate the need to truncate chars following removal of some lines - if len("".join(truncated_explanation)) > tolerable_max_chars: - truncated_explanation = _truncate_by_char_count( - truncated_explanation, max_chars - ) - else: - truncated_char = False - - truncated_line_count = len(input_lines) - len(truncated_explanation) - if truncated_explanation[-1]: - # Add ellipsis and take into account part-truncated final line - truncated_explanation[-1] = truncated_explanation[-1] + "..." - if truncated_char: - # It's possible that we did not remove any char from this line - truncated_line_count += 1 - else: - # Add proper ellipsis when we were able to fit a full line exactly - truncated_explanation[-1] = "..." - return [ - *truncated_explanation, - "", - f"...Full output truncated ({truncated_line_count} line" - f"{'' if truncated_line_count == 1 else 's'} hidden), {USAGE_MSG}", - ] - - -def _truncate_by_char_count(input_lines: list[str], max_chars: int) -> list[str]: - # Find point at which input length exceeds total allowed length - iterated_char_count = 0 - for iterated_index, input_line in enumerate(input_lines): - if iterated_char_count + len(input_line) > max_chars: - break - iterated_char_count += len(input_line) - - # Create truncated explanation with modified final line - truncated_result = input_lines[:iterated_index] - final_line = input_lines[iterated_index] - if final_line: - final_line_truncate_point = max_chars - iterated_char_count - final_line = final_line[:final_line_truncate_point] - truncated_result.append(final_line) - return truncated_result diff --git a/.venv/lib/python3.12/site-packages/_pytest/assertion/util.py b/.venv/lib/python3.12/site-packages/_pytest/assertion/util.py deleted file mode 100644 index 4dc1af4a..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/assertion/util.py +++ /dev/null @@ -1,609 +0,0 @@ -# mypy: allow-untyped-defs -"""Utilities for assertion debugging.""" - -from __future__ import annotations - -import collections.abc -import os -import pprint -from typing import AbstractSet -from typing import Any -from typing import Callable -from typing import Iterable -from typing import Literal -from typing import Mapping -from typing import Protocol -from typing import Sequence -from unicodedata import normalize - -from _pytest import outcomes -import _pytest._code -from _pytest._io.pprint import PrettyPrinter -from _pytest._io.saferepr import saferepr -from _pytest._io.saferepr import saferepr_unlimited -from _pytest.config import Config - - -# The _reprcompare attribute on the util module is used by the new assertion -# interpretation code and assertion rewriter to detect this plugin was -# loaded and in turn call the hooks defined here as part of the -# DebugInterpreter. -_reprcompare: Callable[[str, object, object], str | None] | None = None - -# Works similarly as _reprcompare attribute. Is populated with the hook call -# when pytest_runtest_setup is called. -_assertion_pass: Callable[[int, str, str], None] | None = None - -# Config object which is assigned during pytest_runtest_protocol. -_config: Config | None = None - - -class _HighlightFunc(Protocol): - def __call__(self, source: str, lexer: Literal["diff", "python"] = "python") -> str: - """Apply highlighting to the given source.""" - - -def format_explanation(explanation: str) -> str: - r"""Format an explanation. - - Normally all embedded newlines are escaped, however there are - three exceptions: \n{, \n} and \n~. The first two are intended - cover nested explanations, see function and attribute explanations - for examples (.visit_Call(), visit_Attribute()). The last one is - for when one explanation needs to span multiple lines, e.g. when - displaying diffs. - """ - lines = _split_explanation(explanation) - result = _format_lines(lines) - return "\n".join(result) - - -def _split_explanation(explanation: str) -> list[str]: - r"""Return a list of individual lines in the explanation. - - This will return a list of lines split on '\n{', '\n}' and '\n~'. - Any other newlines will be escaped and appear in the line as the - literal '\n' characters. - """ - raw_lines = (explanation or "").split("\n") - lines = [raw_lines[0]] - for values in raw_lines[1:]: - if values and values[0] in ["{", "}", "~", ">"]: - lines.append(values) - else: - lines[-1] += "\\n" + values - return lines - - -def _format_lines(lines: Sequence[str]) -> list[str]: - """Format the individual lines. - - This will replace the '{', '}' and '~' characters of our mini formatting - language with the proper 'where ...', 'and ...' and ' + ...' text, taking - care of indentation along the way. - - Return a list of formatted lines. - """ - result = list(lines[:1]) - stack = [0] - stackcnt = [0] - for line in lines[1:]: - if line.startswith("{"): - if stackcnt[-1]: - s = "and " - else: - s = "where " - stack.append(len(result)) - stackcnt[-1] += 1 - stackcnt.append(0) - result.append(" +" + " " * (len(stack) - 1) + s + line[1:]) - elif line.startswith("}"): - stack.pop() - stackcnt.pop() - result[stack[-1]] += line[1:] - else: - assert line[0] in ["~", ">"] - stack[-1] += 1 - indent = len(stack) if line.startswith("~") else len(stack) - 1 - result.append(" " * indent + line[1:]) - assert len(stack) == 1 - return result - - -def issequence(x: Any) -> bool: - return isinstance(x, collections.abc.Sequence) and not isinstance(x, str) - - -def istext(x: Any) -> bool: - return isinstance(x, str) - - -def isdict(x: Any) -> bool: - return isinstance(x, dict) - - -def isset(x: Any) -> bool: - return isinstance(x, (set, frozenset)) - - -def isnamedtuple(obj: Any) -> bool: - return isinstance(obj, tuple) and getattr(obj, "_fields", None) is not None - - -def isdatacls(obj: Any) -> bool: - return getattr(obj, "__dataclass_fields__", None) is not None - - -def isattrs(obj: Any) -> bool: - return getattr(obj, "__attrs_attrs__", None) is not None - - -def isiterable(obj: Any) -> bool: - try: - iter(obj) - return not istext(obj) - except Exception: - return False - - -def has_default_eq( - obj: object, -) -> bool: - """Check if an instance of an object contains the default eq - - First, we check if the object's __eq__ attribute has __code__, - if so, we check the equally of the method code filename (__code__.co_filename) - to the default one generated by the dataclass and attr module - for dataclasses the default co_filename is , for attrs class, the __eq__ should contain "attrs eq generated" - """ - # inspired from https://github.com/willmcgugan/rich/blob/07d51ffc1aee6f16bd2e5a25b4e82850fb9ed778/rich/pretty.py#L68 - if hasattr(obj.__eq__, "__code__") and hasattr(obj.__eq__.__code__, "co_filename"): - code_filename = obj.__eq__.__code__.co_filename - - if isattrs(obj): - return "attrs generated eq" in code_filename - - return code_filename == "" # data class - return True - - -def assertrepr_compare( - config, op: str, left: Any, right: Any, use_ascii: bool = False -) -> list[str] | None: - """Return specialised explanations for some operators/operands.""" - verbose = config.get_verbosity(Config.VERBOSITY_ASSERTIONS) - - # Strings which normalize equal are often hard to distinguish when printed; use ascii() to make this easier. - # See issue #3246. - use_ascii = ( - isinstance(left, str) - and isinstance(right, str) - and normalize("NFD", left) == normalize("NFD", right) - ) - - if verbose > 1: - left_repr = saferepr_unlimited(left, use_ascii=use_ascii) - right_repr = saferepr_unlimited(right, use_ascii=use_ascii) - else: - # XXX: "15 chars indentation" is wrong - # ("E AssertionError: assert "); should use term width. - maxsize = ( - 80 - 15 - len(op) - 2 - ) // 2 # 15 chars indentation, 1 space around op - - left_repr = saferepr(left, maxsize=maxsize, use_ascii=use_ascii) - right_repr = saferepr(right, maxsize=maxsize, use_ascii=use_ascii) - - summary = f"{left_repr} {op} {right_repr}" - highlighter = config.get_terminal_writer()._highlight - - explanation = None - try: - if op == "==": - explanation = _compare_eq_any(left, right, highlighter, verbose) - elif op == "not in": - if istext(left) and istext(right): - explanation = _notin_text(left, right, verbose) - elif op == "!=": - if isset(left) and isset(right): - explanation = ["Both sets are equal"] - elif op == ">=": - if isset(left) and isset(right): - explanation = _compare_gte_set(left, right, highlighter, verbose) - elif op == "<=": - if isset(left) and isset(right): - explanation = _compare_lte_set(left, right, highlighter, verbose) - elif op == ">": - if isset(left) and isset(right): - explanation = _compare_gt_set(left, right, highlighter, verbose) - elif op == "<": - if isset(left) and isset(right): - explanation = _compare_lt_set(left, right, highlighter, verbose) - - except outcomes.Exit: - raise - except Exception: - repr_crash = _pytest._code.ExceptionInfo.from_current()._getreprcrash() - explanation = [ - f"(pytest_assertion plugin: representation of details failed: {repr_crash}.", - " Probably an object has a faulty __repr__.)", - ] - - if not explanation: - return None - - if explanation[0] != "": - explanation = ["", *explanation] - return [summary, *explanation] - - -def _compare_eq_any( - left: Any, right: Any, highlighter: _HighlightFunc, verbose: int = 0 -) -> list[str]: - explanation = [] - if istext(left) and istext(right): - explanation = _diff_text(left, right, verbose) - else: - from _pytest.python_api import ApproxBase - - if isinstance(left, ApproxBase) or isinstance(right, ApproxBase): - # Although the common order should be obtained == expected, this ensures both ways - approx_side = left if isinstance(left, ApproxBase) else right - other_side = right if isinstance(left, ApproxBase) else left - - explanation = approx_side._repr_compare(other_side) - elif type(left) is type(right) and ( - isdatacls(left) or isattrs(left) or isnamedtuple(left) - ): - # Note: unlike dataclasses/attrs, namedtuples compare only the - # field values, not the type or field names. But this branch - # intentionally only handles the same-type case, which was often - # used in older code bases before dataclasses/attrs were available. - explanation = _compare_eq_cls(left, right, highlighter, verbose) - elif issequence(left) and issequence(right): - explanation = _compare_eq_sequence(left, right, highlighter, verbose) - elif isset(left) and isset(right): - explanation = _compare_eq_set(left, right, highlighter, verbose) - elif isdict(left) and isdict(right): - explanation = _compare_eq_dict(left, right, highlighter, verbose) - - if isiterable(left) and isiterable(right): - expl = _compare_eq_iterable(left, right, highlighter, verbose) - explanation.extend(expl) - - return explanation - - -def _diff_text(left: str, right: str, verbose: int = 0) -> list[str]: - """Return the explanation for the diff between text. - - Unless --verbose is used this will skip leading and trailing - characters which are identical to keep the diff minimal. - """ - from difflib import ndiff - - explanation: list[str] = [] - - if verbose < 1: - i = 0 # just in case left or right has zero length - for i in range(min(len(left), len(right))): - if left[i] != right[i]: - break - if i > 42: - i -= 10 # Provide some context - explanation = [ - f"Skipping {i} identical leading characters in diff, use -v to show" - ] - left = left[i:] - right = right[i:] - if len(left) == len(right): - for i in range(len(left)): - if left[-i] != right[-i]: - break - if i > 42: - i -= 10 # Provide some context - explanation += [ - f"Skipping {i} identical trailing " - "characters in diff, use -v to show" - ] - left = left[:-i] - right = right[:-i] - keepends = True - if left.isspace() or right.isspace(): - left = repr(str(left)) - right = repr(str(right)) - explanation += ["Strings contain only whitespace, escaping them using repr()"] - # "right" is the expected base against which we compare "left", - # see https://github.com/pytest-dev/pytest/issues/3333 - explanation += [ - line.strip("\n") - for line in ndiff(right.splitlines(keepends), left.splitlines(keepends)) - ] - return explanation - - -def _compare_eq_iterable( - left: Iterable[Any], - right: Iterable[Any], - highlighter: _HighlightFunc, - verbose: int = 0, -) -> list[str]: - if verbose <= 0 and not running_on_ci(): - return ["Use -v to get more diff"] - # dynamic import to speedup pytest - import difflib - - left_formatting = PrettyPrinter().pformat(left).splitlines() - right_formatting = PrettyPrinter().pformat(right).splitlines() - - explanation = ["", "Full diff:"] - # "right" is the expected base against which we compare "left", - # see https://github.com/pytest-dev/pytest/issues/3333 - explanation.extend( - highlighter( - "\n".join( - line.rstrip() - for line in difflib.ndiff(right_formatting, left_formatting) - ), - lexer="diff", - ).splitlines() - ) - return explanation - - -def _compare_eq_sequence( - left: Sequence[Any], - right: Sequence[Any], - highlighter: _HighlightFunc, - verbose: int = 0, -) -> list[str]: - comparing_bytes = isinstance(left, bytes) and isinstance(right, bytes) - explanation: list[str] = [] - len_left = len(left) - len_right = len(right) - for i in range(min(len_left, len_right)): - if left[i] != right[i]: - if comparing_bytes: - # when comparing bytes, we want to see their ascii representation - # instead of their numeric values (#5260) - # using a slice gives us the ascii representation: - # >>> s = b'foo' - # >>> s[0] - # 102 - # >>> s[0:1] - # b'f' - left_value = left[i : i + 1] - right_value = right[i : i + 1] - else: - left_value = left[i] - right_value = right[i] - - explanation.append( - f"At index {i} diff:" - f" {highlighter(repr(left_value))} != {highlighter(repr(right_value))}" - ) - break - - if comparing_bytes: - # when comparing bytes, it doesn't help to show the "sides contain one or more - # items" longer explanation, so skip it - - return explanation - - len_diff = len_left - len_right - if len_diff: - if len_diff > 0: - dir_with_more = "Left" - extra = saferepr(left[len_right]) - else: - len_diff = 0 - len_diff - dir_with_more = "Right" - extra = saferepr(right[len_left]) - - if len_diff == 1: - explanation += [ - f"{dir_with_more} contains one more item: {highlighter(extra)}" - ] - else: - explanation += [ - "%s contains %d more items, first extra item: %s" - % (dir_with_more, len_diff, highlighter(extra)) - ] - return explanation - - -def _compare_eq_set( - left: AbstractSet[Any], - right: AbstractSet[Any], - highlighter: _HighlightFunc, - verbose: int = 0, -) -> list[str]: - explanation = [] - explanation.extend(_set_one_sided_diff("left", left, right, highlighter)) - explanation.extend(_set_one_sided_diff("right", right, left, highlighter)) - return explanation - - -def _compare_gt_set( - left: AbstractSet[Any], - right: AbstractSet[Any], - highlighter: _HighlightFunc, - verbose: int = 0, -) -> list[str]: - explanation = _compare_gte_set(left, right, highlighter) - if not explanation: - return ["Both sets are equal"] - return explanation - - -def _compare_lt_set( - left: AbstractSet[Any], - right: AbstractSet[Any], - highlighter: _HighlightFunc, - verbose: int = 0, -) -> list[str]: - explanation = _compare_lte_set(left, right, highlighter) - if not explanation: - return ["Both sets are equal"] - return explanation - - -def _compare_gte_set( - left: AbstractSet[Any], - right: AbstractSet[Any], - highlighter: _HighlightFunc, - verbose: int = 0, -) -> list[str]: - return _set_one_sided_diff("right", right, left, highlighter) - - -def _compare_lte_set( - left: AbstractSet[Any], - right: AbstractSet[Any], - highlighter: _HighlightFunc, - verbose: int = 0, -) -> list[str]: - return _set_one_sided_diff("left", left, right, highlighter) - - -def _set_one_sided_diff( - posn: str, - set1: AbstractSet[Any], - set2: AbstractSet[Any], - highlighter: _HighlightFunc, -) -> list[str]: - explanation = [] - diff = set1 - set2 - if diff: - explanation.append(f"Extra items in the {posn} set:") - for item in diff: - explanation.append(highlighter(saferepr(item))) - return explanation - - -def _compare_eq_dict( - left: Mapping[Any, Any], - right: Mapping[Any, Any], - highlighter: _HighlightFunc, - verbose: int = 0, -) -> list[str]: - explanation: list[str] = [] - set_left = set(left) - set_right = set(right) - common = set_left.intersection(set_right) - same = {k: left[k] for k in common if left[k] == right[k]} - if same and verbose < 2: - explanation += [f"Omitting {len(same)} identical items, use -vv to show"] - elif same: - explanation += ["Common items:"] - explanation += highlighter(pprint.pformat(same)).splitlines() - diff = {k for k in common if left[k] != right[k]} - if diff: - explanation += ["Differing items:"] - for k in diff: - explanation += [ - highlighter(saferepr({k: left[k]})) - + " != " - + highlighter(saferepr({k: right[k]})) - ] - extra_left = set_left - set_right - len_extra_left = len(extra_left) - if len_extra_left: - explanation.append( - "Left contains %d more item%s:" - % (len_extra_left, "" if len_extra_left == 1 else "s") - ) - explanation.extend( - highlighter(pprint.pformat({k: left[k] for k in extra_left})).splitlines() - ) - extra_right = set_right - set_left - len_extra_right = len(extra_right) - if len_extra_right: - explanation.append( - "Right contains %d more item%s:" - % (len_extra_right, "" if len_extra_right == 1 else "s") - ) - explanation.extend( - highlighter(pprint.pformat({k: right[k] for k in extra_right})).splitlines() - ) - return explanation - - -def _compare_eq_cls( - left: Any, right: Any, highlighter: _HighlightFunc, verbose: int -) -> list[str]: - if not has_default_eq(left): - return [] - if isdatacls(left): - import dataclasses - - all_fields = dataclasses.fields(left) - fields_to_check = [info.name for info in all_fields if info.compare] - elif isattrs(left): - all_fields = left.__attrs_attrs__ - fields_to_check = [field.name for field in all_fields if getattr(field, "eq")] - elif isnamedtuple(left): - fields_to_check = left._fields - else: - assert False - - indent = " " - same = [] - diff = [] - for field in fields_to_check: - if getattr(left, field) == getattr(right, field): - same.append(field) - else: - diff.append(field) - - explanation = [] - if same or diff: - explanation += [""] - if same and verbose < 2: - explanation.append(f"Omitting {len(same)} identical items, use -vv to show") - elif same: - explanation += ["Matching attributes:"] - explanation += highlighter(pprint.pformat(same)).splitlines() - if diff: - explanation += ["Differing attributes:"] - explanation += highlighter(pprint.pformat(diff)).splitlines() - for field in diff: - field_left = getattr(left, field) - field_right = getattr(right, field) - explanation += [ - "", - f"Drill down into differing attribute {field}:", - f"{indent}{field}: {highlighter(repr(field_left))} != {highlighter(repr(field_right))}", - ] - explanation += [ - indent + line - for line in _compare_eq_any( - field_left, field_right, highlighter, verbose - ) - ] - return explanation - - -def _notin_text(term: str, text: str, verbose: int = 0) -> list[str]: - index = text.find(term) - head = text[:index] - tail = text[index + len(term) :] - correct_text = head + tail - diff = _diff_text(text, correct_text, verbose) - newdiff = [f"{saferepr(term, maxsize=42)} is contained here:"] - for line in diff: - if line.startswith("Skipping"): - continue - if line.startswith("- "): - continue - if line.startswith("+ "): - newdiff.append(" " + line[2:]) - else: - newdiff.append(line) - return newdiff - - -def running_on_ci() -> bool: - """Check if we're currently running on a CI system.""" - env_vars = ["CI", "BUILD_NUMBER"] - return any(var in os.environ for var in env_vars) diff --git a/.venv/lib/python3.12/site-packages/_pytest/cacheprovider.py b/.venv/lib/python3.12/site-packages/_pytest/cacheprovider.py deleted file mode 100644 index 1b236efd..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/cacheprovider.py +++ /dev/null @@ -1,626 +0,0 @@ -# mypy: allow-untyped-defs -"""Implementation of the cache provider.""" - -# This plugin was not named "cache" to avoid conflicts with the external -# pytest-cache version. -from __future__ import annotations - -import dataclasses -import errno -import json -import os -from pathlib import Path -import tempfile -from typing import final -from typing import Generator -from typing import Iterable - -from .pathlib import resolve_from_str -from .pathlib import rm_rf -from .reports import CollectReport -from _pytest import nodes -from _pytest._io import TerminalWriter -from _pytest.config import Config -from _pytest.config import ExitCode -from _pytest.config import hookimpl -from _pytest.config.argparsing import Parser -from _pytest.deprecated import check_ispytest -from _pytest.fixtures import fixture -from _pytest.fixtures import FixtureRequest -from _pytest.main import Session -from _pytest.nodes import Directory -from _pytest.nodes import File -from _pytest.reports import TestReport - - -README_CONTENT = """\ -# pytest cache directory # - -This directory contains data from the pytest's cache plugin, -which provides the `--lf` and `--ff` options, as well as the `cache` fixture. - -**Do not** commit this to version control. - -See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information. -""" - -CACHEDIR_TAG_CONTENT = b"""\ -Signature: 8a477f597d28d172789f06886806bc55 -# This file is a cache directory tag created by pytest. -# For information about cache directory tags, see: -# https://bford.info/cachedir/spec.html -""" - - -@final -@dataclasses.dataclass -class Cache: - """Instance of the `cache` fixture.""" - - _cachedir: Path = dataclasses.field(repr=False) - _config: Config = dataclasses.field(repr=False) - - # Sub-directory under cache-dir for directories created by `mkdir()`. - _CACHE_PREFIX_DIRS = "d" - - # Sub-directory under cache-dir for values created by `set()`. - _CACHE_PREFIX_VALUES = "v" - - def __init__( - self, cachedir: Path, config: Config, *, _ispytest: bool = False - ) -> None: - check_ispytest(_ispytest) - self._cachedir = cachedir - self._config = config - - @classmethod - def for_config(cls, config: Config, *, _ispytest: bool = False) -> Cache: - """Create the Cache instance for a Config. - - :meta private: - """ - check_ispytest(_ispytest) - cachedir = cls.cache_dir_from_config(config, _ispytest=True) - if config.getoption("cacheclear") and cachedir.is_dir(): - cls.clear_cache(cachedir, _ispytest=True) - return cls(cachedir, config, _ispytest=True) - - @classmethod - def clear_cache(cls, cachedir: Path, _ispytest: bool = False) -> None: - """Clear the sub-directories used to hold cached directories and values. - - :meta private: - """ - check_ispytest(_ispytest) - for prefix in (cls._CACHE_PREFIX_DIRS, cls._CACHE_PREFIX_VALUES): - d = cachedir / prefix - if d.is_dir(): - rm_rf(d) - - @staticmethod - def cache_dir_from_config(config: Config, *, _ispytest: bool = False) -> Path: - """Get the path to the cache directory for a Config. - - :meta private: - """ - check_ispytest(_ispytest) - return resolve_from_str(config.getini("cache_dir"), config.rootpath) - - def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None: - """Issue a cache warning. - - :meta private: - """ - check_ispytest(_ispytest) - import warnings - - from _pytest.warning_types import PytestCacheWarning - - warnings.warn( - PytestCacheWarning(fmt.format(**args) if args else fmt), - self._config.hook, - stacklevel=3, - ) - - def _mkdir(self, path: Path) -> None: - self._ensure_cache_dir_and_supporting_files() - path.mkdir(exist_ok=True, parents=True) - - def mkdir(self, name: str) -> Path: - """Return a directory path object with the given name. - - If the directory does not yet exist, it will be created. You can use - it to manage files to e.g. store/retrieve database dumps across test - sessions. - - .. versionadded:: 7.0 - - :param name: - Must be a string not containing a ``/`` separator. - Make sure the name contains your plugin or application - identifiers to prevent clashes with other cache users. - """ - path = Path(name) - if len(path.parts) > 1: - raise ValueError("name is not allowed to contain path separators") - res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, path) - self._mkdir(res) - return res - - def _getvaluepath(self, key: str) -> Path: - return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key)) - - def get(self, key: str, default): - """Return the cached value for the given key. - - If no value was yet cached or the value cannot be read, the specified - default is returned. - - :param key: - Must be a ``/`` separated value. Usually the first - name is the name of your plugin or your application. - :param default: - The value to return in case of a cache-miss or invalid cache value. - """ - path = self._getvaluepath(key) - try: - with path.open("r", encoding="UTF-8") as f: - return json.load(f) - except (ValueError, OSError): - return default - - def set(self, key: str, value: object) -> None: - """Save value for the given key. - - :param key: - Must be a ``/`` separated value. Usually the first - name is the name of your plugin or your application. - :param value: - Must be of any combination of basic python types, - including nested types like lists of dictionaries. - """ - path = self._getvaluepath(key) - try: - self._mkdir(path.parent) - except OSError as exc: - self.warn( - f"could not create cache path {path}: {exc}", - _ispytest=True, - ) - return - data = json.dumps(value, ensure_ascii=False, indent=2) - try: - f = path.open("w", encoding="UTF-8") - except OSError as exc: - self.warn( - f"cache could not write path {path}: {exc}", - _ispytest=True, - ) - else: - with f: - f.write(data) - - def _ensure_cache_dir_and_supporting_files(self) -> None: - """Create the cache dir and its supporting files.""" - if self._cachedir.is_dir(): - return - - self._cachedir.parent.mkdir(parents=True, exist_ok=True) - with tempfile.TemporaryDirectory( - prefix="pytest-cache-files-", - dir=self._cachedir.parent, - ) as newpath: - path = Path(newpath) - - # Reset permissions to the default, see #12308. - # Note: there's no way to get the current umask atomically, eek. - umask = os.umask(0o022) - os.umask(umask) - path.chmod(0o777 - umask) - - with open(path.joinpath("README.md"), "x", encoding="UTF-8") as f: - f.write(README_CONTENT) - with open(path.joinpath(".gitignore"), "x", encoding="UTF-8") as f: - f.write("# Created by pytest automatically.\n*\n") - with open(path.joinpath("CACHEDIR.TAG"), "xb") as f: - f.write(CACHEDIR_TAG_CONTENT) - - try: - path.rename(self._cachedir) - except OSError as e: - # If 2 concurrent pytests both race to the rename, the loser - # gets "Directory not empty" from the rename. In this case, - # everything is handled so just continue (while letting the - # temporary directory be cleaned up). - # On Windows, the error is a FileExistsError which translates to EEXIST. - if e.errno not in (errno.ENOTEMPTY, errno.EEXIST): - raise - else: - # Create a directory in place of the one we just moved so that - # `TemporaryDirectory`'s cleanup doesn't complain. - # - # TODO: pass ignore_cleanup_errors=True when we no longer support python < 3.10. - # See https://github.com/python/cpython/issues/74168. Note that passing - # delete=False would do the wrong thing in case of errors and isn't supported - # until python 3.12. - path.mkdir() - - -class LFPluginCollWrapper: - def __init__(self, lfplugin: LFPlugin) -> None: - self.lfplugin = lfplugin - self._collected_at_least_one_failure = False - - @hookimpl(wrapper=True) - def pytest_make_collect_report( - self, collector: nodes.Collector - ) -> Generator[None, CollectReport, CollectReport]: - res = yield - if isinstance(collector, (Session, Directory)): - # Sort any lf-paths to the beginning. - lf_paths = self.lfplugin._last_failed_paths - - # Use stable sort to prioritize last failed. - def sort_key(node: nodes.Item | nodes.Collector) -> bool: - return node.path in lf_paths - - res.result = sorted( - res.result, - key=sort_key, - reverse=True, - ) - - elif isinstance(collector, File): - if collector.path in self.lfplugin._last_failed_paths: - result = res.result - lastfailed = self.lfplugin.lastfailed - - # Only filter with known failures. - if not self._collected_at_least_one_failure: - if not any(x.nodeid in lastfailed for x in result): - return res - self.lfplugin.config.pluginmanager.register( - LFPluginCollSkipfiles(self.lfplugin), "lfplugin-collskip" - ) - self._collected_at_least_one_failure = True - - session = collector.session - result[:] = [ - x - for x in result - if x.nodeid in lastfailed - # Include any passed arguments (not trivial to filter). - or session.isinitpath(x.path) - # Keep all sub-collectors. - or isinstance(x, nodes.Collector) - ] - - return res - - -class LFPluginCollSkipfiles: - def __init__(self, lfplugin: LFPlugin) -> None: - self.lfplugin = lfplugin - - @hookimpl - def pytest_make_collect_report( - self, collector: nodes.Collector - ) -> CollectReport | None: - if isinstance(collector, File): - if collector.path not in self.lfplugin._last_failed_paths: - self.lfplugin._skipped_files += 1 - - return CollectReport( - collector.nodeid, "passed", longrepr=None, result=[] - ) - return None - - -class LFPlugin: - """Plugin which implements the --lf (run last-failing) option.""" - - def __init__(self, config: Config) -> None: - self.config = config - active_keys = "lf", "failedfirst" - self.active = any(config.getoption(key) for key in active_keys) - assert config.cache - self.lastfailed: dict[str, bool] = config.cache.get("cache/lastfailed", {}) - self._previously_failed_count: int | None = None - self._report_status: str | None = None - self._skipped_files = 0 # count skipped files during collection due to --lf - - if config.getoption("lf"): - self._last_failed_paths = self.get_last_failed_paths() - config.pluginmanager.register( - LFPluginCollWrapper(self), "lfplugin-collwrapper" - ) - - def get_last_failed_paths(self) -> set[Path]: - """Return a set with all Paths of the previously failed nodeids and - their parents.""" - rootpath = self.config.rootpath - result = set() - for nodeid in self.lastfailed: - path = rootpath / nodeid.split("::")[0] - result.add(path) - result.update(path.parents) - return {x for x in result if x.exists()} - - def pytest_report_collectionfinish(self) -> str | None: - if self.active and self.config.get_verbosity() >= 0: - return f"run-last-failure: {self._report_status}" - return None - - def pytest_runtest_logreport(self, report: TestReport) -> None: - if (report.when == "call" and report.passed) or report.skipped: - self.lastfailed.pop(report.nodeid, None) - elif report.failed: - self.lastfailed[report.nodeid] = True - - def pytest_collectreport(self, report: CollectReport) -> None: - passed = report.outcome in ("passed", "skipped") - if passed: - if report.nodeid in self.lastfailed: - self.lastfailed.pop(report.nodeid) - self.lastfailed.update((item.nodeid, True) for item in report.result) - else: - self.lastfailed[report.nodeid] = True - - @hookimpl(wrapper=True, tryfirst=True) - def pytest_collection_modifyitems( - self, config: Config, items: list[nodes.Item] - ) -> Generator[None]: - res = yield - - if not self.active: - return res - - if self.lastfailed: - previously_failed = [] - previously_passed = [] - for item in items: - if item.nodeid in self.lastfailed: - previously_failed.append(item) - else: - previously_passed.append(item) - self._previously_failed_count = len(previously_failed) - - if not previously_failed: - # Running a subset of all tests with recorded failures - # only outside of it. - self._report_status = "%d known failures not in selected tests" % ( - len(self.lastfailed), - ) - else: - if self.config.getoption("lf"): - items[:] = previously_failed - config.hook.pytest_deselected(items=previously_passed) - else: # --failedfirst - items[:] = previously_failed + previously_passed - - noun = "failure" if self._previously_failed_count == 1 else "failures" - suffix = " first" if self.config.getoption("failedfirst") else "" - self._report_status = ( - f"rerun previous {self._previously_failed_count} {noun}{suffix}" - ) - - if self._skipped_files > 0: - files_noun = "file" if self._skipped_files == 1 else "files" - self._report_status += f" (skipped {self._skipped_files} {files_noun})" - else: - self._report_status = "no previously failed tests, " - if self.config.getoption("last_failed_no_failures") == "none": - self._report_status += "deselecting all items." - config.hook.pytest_deselected(items=items[:]) - items[:] = [] - else: - self._report_status += "not deselecting items." - - return res - - def pytest_sessionfinish(self, session: Session) -> None: - config = self.config - if config.getoption("cacheshow") or hasattr(config, "workerinput"): - return - - assert config.cache is not None - saved_lastfailed = config.cache.get("cache/lastfailed", {}) - if saved_lastfailed != self.lastfailed: - config.cache.set("cache/lastfailed", self.lastfailed) - - -class NFPlugin: - """Plugin which implements the --nf (run new-first) option.""" - - def __init__(self, config: Config) -> None: - self.config = config - self.active = config.option.newfirst - assert config.cache is not None - self.cached_nodeids = set(config.cache.get("cache/nodeids", [])) - - @hookimpl(wrapper=True, tryfirst=True) - def pytest_collection_modifyitems(self, items: list[nodes.Item]) -> Generator[None]: - res = yield - - if self.active: - new_items: dict[str, nodes.Item] = {} - other_items: dict[str, nodes.Item] = {} - for item in items: - if item.nodeid not in self.cached_nodeids: - new_items[item.nodeid] = item - else: - other_items[item.nodeid] = item - - items[:] = self._get_increasing_order( - new_items.values() - ) + self._get_increasing_order(other_items.values()) - self.cached_nodeids.update(new_items) - else: - self.cached_nodeids.update(item.nodeid for item in items) - - return res - - def _get_increasing_order(self, items: Iterable[nodes.Item]) -> list[nodes.Item]: - return sorted(items, key=lambda item: item.path.stat().st_mtime, reverse=True) - - def pytest_sessionfinish(self) -> None: - config = self.config - if config.getoption("cacheshow") or hasattr(config, "workerinput"): - return - - if config.getoption("collectonly"): - return - - assert config.cache is not None - config.cache.set("cache/nodeids", sorted(self.cached_nodeids)) - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") - group.addoption( - "--lf", - "--last-failed", - action="store_true", - dest="lf", - help="Rerun only the tests that failed " - "at the last run (or all if none failed)", - ) - group.addoption( - "--ff", - "--failed-first", - action="store_true", - dest="failedfirst", - help="Run all tests, but run the last failures first. " - "This may re-order tests and thus lead to " - "repeated fixture setup/teardown.", - ) - group.addoption( - "--nf", - "--new-first", - action="store_true", - dest="newfirst", - help="Run tests from new files first, then the rest of the tests " - "sorted by file mtime", - ) - group.addoption( - "--cache-show", - action="append", - nargs="?", - dest="cacheshow", - help=( - "Show cache contents, don't perform collection or tests. " - "Optional argument: glob (default: '*')." - ), - ) - group.addoption( - "--cache-clear", - action="store_true", - dest="cacheclear", - help="Remove all cache contents at start of test run", - ) - cache_dir_default = ".pytest_cache" - if "TOX_ENV_DIR" in os.environ: - cache_dir_default = os.path.join(os.environ["TOX_ENV_DIR"], cache_dir_default) - parser.addini("cache_dir", default=cache_dir_default, help="Cache directory path") - group.addoption( - "--lfnf", - "--last-failed-no-failures", - action="store", - dest="last_failed_no_failures", - choices=("all", "none"), - default="all", - help="With ``--lf``, determines whether to execute tests when there " - "are no previously (known) failures or when no " - "cached ``lastfailed`` data was found. " - "``all`` (the default) runs the full test suite again. " - "``none`` just emits a message about no known failures and exits successfully.", - ) - - -def pytest_cmdline_main(config: Config) -> int | ExitCode | None: - if config.option.cacheshow and not config.option.help: - from _pytest.main import wrap_session - - return wrap_session(config, cacheshow) - return None - - -@hookimpl(tryfirst=True) -def pytest_configure(config: Config) -> None: - config.cache = Cache.for_config(config, _ispytest=True) - config.pluginmanager.register(LFPlugin(config), "lfplugin") - config.pluginmanager.register(NFPlugin(config), "nfplugin") - - -@fixture -def cache(request: FixtureRequest) -> Cache: - """Return a cache object that can persist state between testing sessions. - - cache.get(key, default) - cache.set(key, value) - - Keys must be ``/`` separated strings, where the first part is usually the - name of your plugin or application to avoid clashes with other cache users. - - Values can be any object handled by the json stdlib module. - """ - assert request.config.cache is not None - return request.config.cache - - -def pytest_report_header(config: Config) -> str | None: - """Display cachedir with --cache-show and if non-default.""" - if config.option.verbose > 0 or config.getini("cache_dir") != ".pytest_cache": - assert config.cache is not None - cachedir = config.cache._cachedir - # TODO: evaluate generating upward relative paths - # starting with .., ../.. if sensible - - try: - displaypath = cachedir.relative_to(config.rootpath) - except ValueError: - displaypath = cachedir - return f"cachedir: {displaypath}" - return None - - -def cacheshow(config: Config, session: Session) -> int: - from pprint import pformat - - assert config.cache is not None - - tw = TerminalWriter() - tw.line("cachedir: " + str(config.cache._cachedir)) - if not config.cache._cachedir.is_dir(): - tw.line("cache is empty") - return 0 - - glob = config.option.cacheshow[0] - if glob is None: - glob = "*" - - dummy = object() - basedir = config.cache._cachedir - vdir = basedir / Cache._CACHE_PREFIX_VALUES - tw.sep("-", f"cache values for {glob!r}") - for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()): - key = str(valpath.relative_to(vdir)) - val = config.cache.get(key, dummy) - if val is dummy: - tw.line(f"{key} contains unreadable content, will be ignored") - else: - tw.line(f"{key} contains:") - for line in pformat(val).splitlines(): - tw.line(" " + line) - - ddir = basedir / Cache._CACHE_PREFIX_DIRS - if ddir.is_dir(): - contents = sorted(ddir.rglob(glob)) - tw.sep("-", f"cache directories for {glob!r}") - for p in contents: - # if p.is_dir(): - # print("%s/" % p.relative_to(basedir)) - if p.is_file(): - key = str(p.relative_to(basedir)) - tw.line(f"{key} is a file of length {p.stat().st_size:d}") - return 0 diff --git a/.venv/lib/python3.12/site-packages/_pytest/capture.py b/.venv/lib/python3.12/site-packages/_pytest/capture.py deleted file mode 100644 index 506c0b3d..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/capture.py +++ /dev/null @@ -1,1087 +0,0 @@ -# mypy: allow-untyped-defs -"""Per-test stdout/stderr capturing mechanism.""" - -from __future__ import annotations - -import abc -import collections -import contextlib -import io -from io import UnsupportedOperation -import os -import sys -from tempfile import TemporaryFile -from types import TracebackType -from typing import Any -from typing import AnyStr -from typing import BinaryIO -from typing import Final -from typing import final -from typing import Generator -from typing import Generic -from typing import Iterable -from typing import Iterator -from typing import Literal -from typing import NamedTuple -from typing import TextIO -from typing import TYPE_CHECKING - - -if TYPE_CHECKING: - from typing_extensions import Self - -from _pytest.config import Config -from _pytest.config import hookimpl -from _pytest.config.argparsing import Parser -from _pytest.deprecated import check_ispytest -from _pytest.fixtures import fixture -from _pytest.fixtures import SubRequest -from _pytest.nodes import Collector -from _pytest.nodes import File -from _pytest.nodes import Item -from _pytest.reports import CollectReport - - -_CaptureMethod = Literal["fd", "sys", "no", "tee-sys"] - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") - group._addoption( - "--capture", - action="store", - default="fd", - metavar="method", - choices=["fd", "sys", "no", "tee-sys"], - help="Per-test capturing method: one of fd|sys|no|tee-sys", - ) - group._addoption( - "-s", - action="store_const", - const="no", - dest="capture", - help="Shortcut for --capture=no", - ) - - -def _colorama_workaround() -> None: - """Ensure colorama is imported so that it attaches to the correct stdio - handles on Windows. - - colorama uses the terminal on import time. So if something does the - first import of colorama while I/O capture is active, colorama will - fail in various ways. - """ - if sys.platform.startswith("win32"): - try: - import colorama # noqa: F401 - except ImportError: - pass - - -def _windowsconsoleio_workaround(stream: TextIO) -> None: - """Workaround for Windows Unicode console handling. - - Python 3.6 implemented Unicode console handling for Windows. This works - by reading/writing to the raw console handle using - ``{Read,Write}ConsoleW``. - - The problem is that we are going to ``dup2`` over the stdio file - descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the - handles used by Python to write to the console. Though there is still some - weirdness and the console handle seems to only be closed randomly and not - on the first call to ``CloseHandle``, or maybe it gets reopened with the - same handle value when we suspend capturing. - - The workaround in this case will reopen stdio with a different fd which - also means a different handle by replicating the logic in - "Py_lifecycle.c:initstdio/create_stdio". - - :param stream: - In practice ``sys.stdout`` or ``sys.stderr``, but given - here as parameter for unittesting purposes. - - See https://github.com/pytest-dev/py/issues/103. - """ - if not sys.platform.startswith("win32") or hasattr(sys, "pypy_version_info"): - return - - # Bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666). - if not hasattr(stream, "buffer"): # type: ignore[unreachable,unused-ignore] - return - - raw_stdout = stream.buffer.raw if hasattr(stream.buffer, "raw") else stream.buffer - - if not isinstance(raw_stdout, io._WindowsConsoleIO): # type: ignore[attr-defined,unused-ignore] - return - - def _reopen_stdio(f, mode): - if not hasattr(stream.buffer, "raw") and mode[0] == "w": - buffering = 0 - else: - buffering = -1 - - return io.TextIOWrapper( - open(os.dup(f.fileno()), mode, buffering), - f.encoding, - f.errors, - f.newlines, - f.line_buffering, - ) - - sys.stdin = _reopen_stdio(sys.stdin, "rb") - sys.stdout = _reopen_stdio(sys.stdout, "wb") - sys.stderr = _reopen_stdio(sys.stderr, "wb") - - -@hookimpl(wrapper=True) -def pytest_load_initial_conftests(early_config: Config) -> Generator[None]: - ns = early_config.known_args_namespace - if ns.capture == "fd": - _windowsconsoleio_workaround(sys.stdout) - _colorama_workaround() - pluginmanager = early_config.pluginmanager - capman = CaptureManager(ns.capture) - pluginmanager.register(capman, "capturemanager") - - # Make sure that capturemanager is properly reset at final shutdown. - early_config.add_cleanup(capman.stop_global_capturing) - - # Finally trigger conftest loading but while capturing (issue #93). - capman.start_global_capturing() - try: - try: - yield - finally: - capman.suspend_global_capture() - except BaseException: - out, err = capman.read_global_capture() - sys.stdout.write(out) - sys.stderr.write(err) - raise - - -# IO Helpers. - - -class EncodedFile(io.TextIOWrapper): - __slots__ = () - - @property - def name(self) -> str: - # Ensure that file.name is a string. Workaround for a Python bug - # fixed in >=3.7.4: https://bugs.python.org/issue36015 - return repr(self.buffer) - - @property - def mode(self) -> str: - # TextIOWrapper doesn't expose a mode, but at least some of our - # tests check it. - return self.buffer.mode.replace("b", "") - - -class CaptureIO(io.TextIOWrapper): - def __init__(self) -> None: - super().__init__(io.BytesIO(), encoding="UTF-8", newline="", write_through=True) - - def getvalue(self) -> str: - assert isinstance(self.buffer, io.BytesIO) - return self.buffer.getvalue().decode("UTF-8") - - -class TeeCaptureIO(CaptureIO): - def __init__(self, other: TextIO) -> None: - self._other = other - super().__init__() - - def write(self, s: str) -> int: - super().write(s) - return self._other.write(s) - - -class DontReadFromInput(TextIO): - @property - def encoding(self) -> str: - assert sys.__stdin__ is not None - return sys.__stdin__.encoding - - def read(self, size: int = -1) -> str: - raise OSError( - "pytest: reading from stdin while output is captured! Consider using `-s`." - ) - - readline = read - - def __next__(self) -> str: - return self.readline() - - def readlines(self, hint: int | None = -1) -> list[str]: - raise OSError( - "pytest: reading from stdin while output is captured! Consider using `-s`." - ) - - def __iter__(self) -> Iterator[str]: - return self - - def fileno(self) -> int: - raise UnsupportedOperation("redirected stdin is pseudofile, has no fileno()") - - def flush(self) -> None: - raise UnsupportedOperation("redirected stdin is pseudofile, has no flush()") - - def isatty(self) -> bool: - return False - - def close(self) -> None: - pass - - def readable(self) -> bool: - return False - - def seek(self, offset: int, whence: int = 0) -> int: - raise UnsupportedOperation("redirected stdin is pseudofile, has no seek(int)") - - def seekable(self) -> bool: - return False - - def tell(self) -> int: - raise UnsupportedOperation("redirected stdin is pseudofile, has no tell()") - - def truncate(self, size: int | None = None) -> int: - raise UnsupportedOperation("cannot truncate stdin") - - def write(self, data: str) -> int: - raise UnsupportedOperation("cannot write to stdin") - - def writelines(self, lines: Iterable[str]) -> None: - raise UnsupportedOperation("Cannot write to stdin") - - def writable(self) -> bool: - return False - - def __enter__(self) -> Self: - return self - - def __exit__( - self, - type: type[BaseException] | None, - value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - pass - - @property - def buffer(self) -> BinaryIO: - # The str/bytes doesn't actually matter in this type, so OK to fake. - return self # type: ignore[return-value] - - -# Capture classes. - - -class CaptureBase(abc.ABC, Generic[AnyStr]): - EMPTY_BUFFER: AnyStr - - @abc.abstractmethod - def __init__(self, fd: int) -> None: - raise NotImplementedError() - - @abc.abstractmethod - def start(self) -> None: - raise NotImplementedError() - - @abc.abstractmethod - def done(self) -> None: - raise NotImplementedError() - - @abc.abstractmethod - def suspend(self) -> None: - raise NotImplementedError() - - @abc.abstractmethod - def resume(self) -> None: - raise NotImplementedError() - - @abc.abstractmethod - def writeorg(self, data: AnyStr) -> None: - raise NotImplementedError() - - @abc.abstractmethod - def snap(self) -> AnyStr: - raise NotImplementedError() - - -patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"} - - -class NoCapture(CaptureBase[str]): - EMPTY_BUFFER = "" - - def __init__(self, fd: int) -> None: - pass - - def start(self) -> None: - pass - - def done(self) -> None: - pass - - def suspend(self) -> None: - pass - - def resume(self) -> None: - pass - - def snap(self) -> str: - return "" - - def writeorg(self, data: str) -> None: - pass - - -class SysCaptureBase(CaptureBase[AnyStr]): - def __init__( - self, fd: int, tmpfile: TextIO | None = None, *, tee: bool = False - ) -> None: - name = patchsysdict[fd] - self._old: TextIO = getattr(sys, name) - self.name = name - if tmpfile is None: - if name == "stdin": - tmpfile = DontReadFromInput() - else: - tmpfile = CaptureIO() if not tee else TeeCaptureIO(self._old) - self.tmpfile = tmpfile - self._state = "initialized" - - def repr(self, class_name: str) -> str: - return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( - class_name, - self.name, - hasattr(self, "_old") and repr(self._old) or "", - self._state, - self.tmpfile, - ) - - def __repr__(self) -> str: - return "<{} {} _old={} _state={!r} tmpfile={!r}>".format( - self.__class__.__name__, - self.name, - hasattr(self, "_old") and repr(self._old) or "", - self._state, - self.tmpfile, - ) - - def _assert_state(self, op: str, states: tuple[str, ...]) -> None: - assert ( - self._state in states - ), "cannot {} in state {!r}: expected one of {}".format( - op, self._state, ", ".join(states) - ) - - def start(self) -> None: - self._assert_state("start", ("initialized",)) - setattr(sys, self.name, self.tmpfile) - self._state = "started" - - def done(self) -> None: - self._assert_state("done", ("initialized", "started", "suspended", "done")) - if self._state == "done": - return - setattr(sys, self.name, self._old) - del self._old - self.tmpfile.close() - self._state = "done" - - def suspend(self) -> None: - self._assert_state("suspend", ("started", "suspended")) - setattr(sys, self.name, self._old) - self._state = "suspended" - - def resume(self) -> None: - self._assert_state("resume", ("started", "suspended")) - if self._state == "started": - return - setattr(sys, self.name, self.tmpfile) - self._state = "started" - - -class SysCaptureBinary(SysCaptureBase[bytes]): - EMPTY_BUFFER = b"" - - def snap(self) -> bytes: - self._assert_state("snap", ("started", "suspended")) - self.tmpfile.seek(0) - res = self.tmpfile.buffer.read() - self.tmpfile.seek(0) - self.tmpfile.truncate() - return res - - def writeorg(self, data: bytes) -> None: - self._assert_state("writeorg", ("started", "suspended")) - self._old.flush() - self._old.buffer.write(data) - self._old.buffer.flush() - - -class SysCapture(SysCaptureBase[str]): - EMPTY_BUFFER = "" - - def snap(self) -> str: - self._assert_state("snap", ("started", "suspended")) - assert isinstance(self.tmpfile, CaptureIO) - res = self.tmpfile.getvalue() - self.tmpfile.seek(0) - self.tmpfile.truncate() - return res - - def writeorg(self, data: str) -> None: - self._assert_state("writeorg", ("started", "suspended")) - self._old.write(data) - self._old.flush() - - -class FDCaptureBase(CaptureBase[AnyStr]): - def __init__(self, targetfd: int) -> None: - self.targetfd = targetfd - - try: - os.fstat(targetfd) - except OSError: - # FD capturing is conceptually simple -- create a temporary file, - # redirect the FD to it, redirect back when done. But when the - # target FD is invalid it throws a wrench into this lovely scheme. - # - # Tests themselves shouldn't care if the FD is valid, FD capturing - # should work regardless of external circumstances. So falling back - # to just sys capturing is not a good option. - # - # Further complications are the need to support suspend() and the - # possibility of FD reuse (e.g. the tmpfile getting the very same - # target FD). The following approach is robust, I believe. - self.targetfd_invalid: int | None = os.open(os.devnull, os.O_RDWR) - os.dup2(self.targetfd_invalid, targetfd) - else: - self.targetfd_invalid = None - self.targetfd_save = os.dup(targetfd) - - if targetfd == 0: - self.tmpfile = open(os.devnull, encoding="utf-8") - self.syscapture: CaptureBase[str] = SysCapture(targetfd) - else: - self.tmpfile = EncodedFile( - TemporaryFile(buffering=0), - encoding="utf-8", - errors="replace", - newline="", - write_through=True, - ) - if targetfd in patchsysdict: - self.syscapture = SysCapture(targetfd, self.tmpfile) - else: - self.syscapture = NoCapture(targetfd) - - self._state = "initialized" - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__} {self.targetfd} oldfd={self.targetfd_save} " - f"_state={self._state!r} tmpfile={self.tmpfile!r}>" - ) - - def _assert_state(self, op: str, states: tuple[str, ...]) -> None: - assert ( - self._state in states - ), "cannot {} in state {!r}: expected one of {}".format( - op, self._state, ", ".join(states) - ) - - def start(self) -> None: - """Start capturing on targetfd using memorized tmpfile.""" - self._assert_state("start", ("initialized",)) - os.dup2(self.tmpfile.fileno(), self.targetfd) - self.syscapture.start() - self._state = "started" - - def done(self) -> None: - """Stop capturing, restore streams, return original capture file, - seeked to position zero.""" - self._assert_state("done", ("initialized", "started", "suspended", "done")) - if self._state == "done": - return - os.dup2(self.targetfd_save, self.targetfd) - os.close(self.targetfd_save) - if self.targetfd_invalid is not None: - if self.targetfd_invalid != self.targetfd: - os.close(self.targetfd) - os.close(self.targetfd_invalid) - self.syscapture.done() - self.tmpfile.close() - self._state = "done" - - def suspend(self) -> None: - self._assert_state("suspend", ("started", "suspended")) - if self._state == "suspended": - return - self.syscapture.suspend() - os.dup2(self.targetfd_save, self.targetfd) - self._state = "suspended" - - def resume(self) -> None: - self._assert_state("resume", ("started", "suspended")) - if self._state == "started": - return - self.syscapture.resume() - os.dup2(self.tmpfile.fileno(), self.targetfd) - self._state = "started" - - -class FDCaptureBinary(FDCaptureBase[bytes]): - """Capture IO to/from a given OS-level file descriptor. - - snap() produces `bytes`. - """ - - EMPTY_BUFFER = b"" - - def snap(self) -> bytes: - self._assert_state("snap", ("started", "suspended")) - self.tmpfile.seek(0) - res = self.tmpfile.buffer.read() - self.tmpfile.seek(0) - self.tmpfile.truncate() - return res - - def writeorg(self, data: bytes) -> None: - """Write to original file descriptor.""" - self._assert_state("writeorg", ("started", "suspended")) - os.write(self.targetfd_save, data) - - -class FDCapture(FDCaptureBase[str]): - """Capture IO to/from a given OS-level file descriptor. - - snap() produces text. - """ - - EMPTY_BUFFER = "" - - def snap(self) -> str: - self._assert_state("snap", ("started", "suspended")) - self.tmpfile.seek(0) - res = self.tmpfile.read() - self.tmpfile.seek(0) - self.tmpfile.truncate() - return res - - def writeorg(self, data: str) -> None: - """Write to original file descriptor.""" - self._assert_state("writeorg", ("started", "suspended")) - # XXX use encoding of original stream - os.write(self.targetfd_save, data.encode("utf-8")) - - -# MultiCapture - - -# Generic NamedTuple only supported since Python 3.11. -if sys.version_info >= (3, 11) or TYPE_CHECKING: - - @final - class CaptureResult(NamedTuple, Generic[AnyStr]): - """The result of :method:`caplog.readouterr() `.""" - - out: AnyStr - err: AnyStr - -else: - - class CaptureResult( - collections.namedtuple("CaptureResult", ["out", "err"]), # noqa: PYI024 - Generic[AnyStr], - ): - """The result of :method:`caplog.readouterr() `.""" - - __slots__ = () - - -class MultiCapture(Generic[AnyStr]): - _state = None - _in_suspended = False - - def __init__( - self, - in_: CaptureBase[AnyStr] | None, - out: CaptureBase[AnyStr] | None, - err: CaptureBase[AnyStr] | None, - ) -> None: - self.in_: CaptureBase[AnyStr] | None = in_ - self.out: CaptureBase[AnyStr] | None = out - self.err: CaptureBase[AnyStr] | None = err - - def __repr__(self) -> str: - return ( - f"" - ) - - def start_capturing(self) -> None: - self._state = "started" - if self.in_: - self.in_.start() - if self.out: - self.out.start() - if self.err: - self.err.start() - - def pop_outerr_to_orig(self) -> tuple[AnyStr, AnyStr]: - """Pop current snapshot out/err capture and flush to orig streams.""" - out, err = self.readouterr() - if out: - assert self.out is not None - self.out.writeorg(out) - if err: - assert self.err is not None - self.err.writeorg(err) - return out, err - - def suspend_capturing(self, in_: bool = False) -> None: - self._state = "suspended" - if self.out: - self.out.suspend() - if self.err: - self.err.suspend() - if in_ and self.in_: - self.in_.suspend() - self._in_suspended = True - - def resume_capturing(self) -> None: - self._state = "started" - if self.out: - self.out.resume() - if self.err: - self.err.resume() - if self._in_suspended: - assert self.in_ is not None - self.in_.resume() - self._in_suspended = False - - def stop_capturing(self) -> None: - """Stop capturing and reset capturing streams.""" - if self._state == "stopped": - raise ValueError("was already stopped") - self._state = "stopped" - if self.out: - self.out.done() - if self.err: - self.err.done() - if self.in_: - self.in_.done() - - def is_started(self) -> bool: - """Whether actively capturing -- not suspended or stopped.""" - return self._state == "started" - - def readouterr(self) -> CaptureResult[AnyStr]: - out = self.out.snap() if self.out else "" - err = self.err.snap() if self.err else "" - # TODO: This type error is real, need to fix. - return CaptureResult(out, err) # type: ignore[arg-type] - - -def _get_multicapture(method: _CaptureMethod) -> MultiCapture[str]: - if method == "fd": - return MultiCapture(in_=FDCapture(0), out=FDCapture(1), err=FDCapture(2)) - elif method == "sys": - return MultiCapture(in_=SysCapture(0), out=SysCapture(1), err=SysCapture(2)) - elif method == "no": - return MultiCapture(in_=None, out=None, err=None) - elif method == "tee-sys": - return MultiCapture( - in_=None, out=SysCapture(1, tee=True), err=SysCapture(2, tee=True) - ) - raise ValueError(f"unknown capturing method: {method!r}") - - -# CaptureManager and CaptureFixture - - -class CaptureManager: - """The capture plugin. - - Manages that the appropriate capture method is enabled/disabled during - collection and each test phase (setup, call, teardown). After each of - those points, the captured output is obtained and attached to the - collection/runtest report. - - There are two levels of capture: - - * global: enabled by default and can be suppressed by the ``-s`` - option. This is always enabled/disabled during collection and each test - phase. - - * fixture: when a test function or one of its fixture depend on the - ``capsys`` or ``capfd`` fixtures. In this case special handling is - needed to ensure the fixtures take precedence over the global capture. - """ - - def __init__(self, method: _CaptureMethod) -> None: - self._method: Final = method - self._global_capturing: MultiCapture[str] | None = None - self._capture_fixture: CaptureFixture[Any] | None = None - - def __repr__(self) -> str: - return ( - f"" - ) - - def is_capturing(self) -> str | bool: - if self.is_globally_capturing(): - return "global" - if self._capture_fixture: - return f"fixture {self._capture_fixture.request.fixturename}" - return False - - # Global capturing control - - def is_globally_capturing(self) -> bool: - return self._method != "no" - - def start_global_capturing(self) -> None: - assert self._global_capturing is None - self._global_capturing = _get_multicapture(self._method) - self._global_capturing.start_capturing() - - def stop_global_capturing(self) -> None: - if self._global_capturing is not None: - self._global_capturing.pop_outerr_to_orig() - self._global_capturing.stop_capturing() - self._global_capturing = None - - def resume_global_capture(self) -> None: - # During teardown of the python process, and on rare occasions, capture - # attributes can be `None` while trying to resume global capture. - if self._global_capturing is not None: - self._global_capturing.resume_capturing() - - def suspend_global_capture(self, in_: bool = False) -> None: - if self._global_capturing is not None: - self._global_capturing.suspend_capturing(in_=in_) - - def suspend(self, in_: bool = False) -> None: - # Need to undo local capsys-et-al if it exists before disabling global capture. - self.suspend_fixture() - self.suspend_global_capture(in_) - - def resume(self) -> None: - self.resume_global_capture() - self.resume_fixture() - - def read_global_capture(self) -> CaptureResult[str]: - assert self._global_capturing is not None - return self._global_capturing.readouterr() - - # Fixture Control - - def set_fixture(self, capture_fixture: CaptureFixture[Any]) -> None: - if self._capture_fixture: - current_fixture = self._capture_fixture.request.fixturename - requested_fixture = capture_fixture.request.fixturename - capture_fixture.request.raiseerror( - f"cannot use {requested_fixture} and {current_fixture} at the same time" - ) - self._capture_fixture = capture_fixture - - def unset_fixture(self) -> None: - self._capture_fixture = None - - def activate_fixture(self) -> None: - """If the current item is using ``capsys`` or ``capfd``, activate - them so they take precedence over the global capture.""" - if self._capture_fixture: - self._capture_fixture._start() - - def deactivate_fixture(self) -> None: - """Deactivate the ``capsys`` or ``capfd`` fixture of this item, if any.""" - if self._capture_fixture: - self._capture_fixture.close() - - def suspend_fixture(self) -> None: - if self._capture_fixture: - self._capture_fixture._suspend() - - def resume_fixture(self) -> None: - if self._capture_fixture: - self._capture_fixture._resume() - - # Helper context managers - - @contextlib.contextmanager - def global_and_fixture_disabled(self) -> Generator[None]: - """Context manager to temporarily disable global and current fixture capturing.""" - do_fixture = self._capture_fixture and self._capture_fixture._is_started() - if do_fixture: - self.suspend_fixture() - do_global = self._global_capturing and self._global_capturing.is_started() - if do_global: - self.suspend_global_capture() - try: - yield - finally: - if do_global: - self.resume_global_capture() - if do_fixture: - self.resume_fixture() - - @contextlib.contextmanager - def item_capture(self, when: str, item: Item) -> Generator[None]: - self.resume_global_capture() - self.activate_fixture() - try: - yield - finally: - self.deactivate_fixture() - self.suspend_global_capture(in_=False) - - out, err = self.read_global_capture() - item.add_report_section(when, "stdout", out) - item.add_report_section(when, "stderr", err) - - # Hooks - - @hookimpl(wrapper=True) - def pytest_make_collect_report( - self, collector: Collector - ) -> Generator[None, CollectReport, CollectReport]: - if isinstance(collector, File): - self.resume_global_capture() - try: - rep = yield - finally: - self.suspend_global_capture() - out, err = self.read_global_capture() - if out: - rep.sections.append(("Captured stdout", out)) - if err: - rep.sections.append(("Captured stderr", err)) - else: - rep = yield - return rep - - @hookimpl(wrapper=True) - def pytest_runtest_setup(self, item: Item) -> Generator[None]: - with self.item_capture("setup", item): - return (yield) - - @hookimpl(wrapper=True) - def pytest_runtest_call(self, item: Item) -> Generator[None]: - with self.item_capture("call", item): - return (yield) - - @hookimpl(wrapper=True) - def pytest_runtest_teardown(self, item: Item) -> Generator[None]: - with self.item_capture("teardown", item): - return (yield) - - @hookimpl(tryfirst=True) - def pytest_keyboard_interrupt(self) -> None: - self.stop_global_capturing() - - @hookimpl(tryfirst=True) - def pytest_internalerror(self) -> None: - self.stop_global_capturing() - - -class CaptureFixture(Generic[AnyStr]): - """Object returned by the :fixture:`capsys`, :fixture:`capsysbinary`, - :fixture:`capfd` and :fixture:`capfdbinary` fixtures.""" - - def __init__( - self, - captureclass: type[CaptureBase[AnyStr]], - request: SubRequest, - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - self.captureclass: type[CaptureBase[AnyStr]] = captureclass - self.request = request - self._capture: MultiCapture[AnyStr] | None = None - self._captured_out: AnyStr = self.captureclass.EMPTY_BUFFER - self._captured_err: AnyStr = self.captureclass.EMPTY_BUFFER - - def _start(self) -> None: - if self._capture is None: - self._capture = MultiCapture( - in_=None, - out=self.captureclass(1), - err=self.captureclass(2), - ) - self._capture.start_capturing() - - def close(self) -> None: - if self._capture is not None: - out, err = self._capture.pop_outerr_to_orig() - self._captured_out += out - self._captured_err += err - self._capture.stop_capturing() - self._capture = None - - def readouterr(self) -> CaptureResult[AnyStr]: - """Read and return the captured output so far, resetting the internal - buffer. - - :returns: - The captured content as a namedtuple with ``out`` and ``err`` - string attributes. - """ - captured_out, captured_err = self._captured_out, self._captured_err - if self._capture is not None: - out, err = self._capture.readouterr() - captured_out += out - captured_err += err - self._captured_out = self.captureclass.EMPTY_BUFFER - self._captured_err = self.captureclass.EMPTY_BUFFER - return CaptureResult(captured_out, captured_err) - - def _suspend(self) -> None: - """Suspend this fixture's own capturing temporarily.""" - if self._capture is not None: - self._capture.suspend_capturing() - - def _resume(self) -> None: - """Resume this fixture's own capturing temporarily.""" - if self._capture is not None: - self._capture.resume_capturing() - - def _is_started(self) -> bool: - """Whether actively capturing -- not disabled or closed.""" - if self._capture is not None: - return self._capture.is_started() - return False - - @contextlib.contextmanager - def disabled(self) -> Generator[None]: - """Temporarily disable capturing while inside the ``with`` block.""" - capmanager: CaptureManager = self.request.config.pluginmanager.getplugin( - "capturemanager" - ) - with capmanager.global_and_fixture_disabled(): - yield - - -# The fixtures. - - -@fixture -def capsys(request: SubRequest) -> Generator[CaptureFixture[str]]: - r"""Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``. - - The captured output is made available via ``capsys.readouterr()`` method - calls, which return a ``(out, err)`` namedtuple. - ``out`` and ``err`` will be ``text`` objects. - - Returns an instance of :class:`CaptureFixture[str] `. - - Example: - - .. code-block:: python - - def test_output(capsys): - print("hello") - captured = capsys.readouterr() - assert captured.out == "hello\n" - """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") - capture_fixture = CaptureFixture(SysCapture, request, _ispytest=True) - capman.set_fixture(capture_fixture) - capture_fixture._start() - yield capture_fixture - capture_fixture.close() - capman.unset_fixture() - - -@fixture -def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]: - r"""Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``. - - The captured output is made available via ``capsysbinary.readouterr()`` - method calls, which return a ``(out, err)`` namedtuple. - ``out`` and ``err`` will be ``bytes`` objects. - - Returns an instance of :class:`CaptureFixture[bytes] `. - - Example: - - .. code-block:: python - - def test_output(capsysbinary): - print("hello") - captured = capsysbinary.readouterr() - assert captured.out == b"hello\n" - """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") - capture_fixture = CaptureFixture(SysCaptureBinary, request, _ispytest=True) - capman.set_fixture(capture_fixture) - capture_fixture._start() - yield capture_fixture - capture_fixture.close() - capman.unset_fixture() - - -@fixture -def capfd(request: SubRequest) -> Generator[CaptureFixture[str]]: - r"""Enable text capturing of writes to file descriptors ``1`` and ``2``. - - The captured output is made available via ``capfd.readouterr()`` method - calls, which return a ``(out, err)`` namedtuple. - ``out`` and ``err`` will be ``text`` objects. - - Returns an instance of :class:`CaptureFixture[str] `. - - Example: - - .. code-block:: python - - def test_system_echo(capfd): - os.system('echo "hello"') - captured = capfd.readouterr() - assert captured.out == "hello\n" - """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") - capture_fixture = CaptureFixture(FDCapture, request, _ispytest=True) - capman.set_fixture(capture_fixture) - capture_fixture._start() - yield capture_fixture - capture_fixture.close() - capman.unset_fixture() - - -@fixture -def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]: - r"""Enable bytes capturing of writes to file descriptors ``1`` and ``2``. - - The captured output is made available via ``capfd.readouterr()`` method - calls, which return a ``(out, err)`` namedtuple. - ``out`` and ``err`` will be ``byte`` objects. - - Returns an instance of :class:`CaptureFixture[bytes] `. - - Example: - - .. code-block:: python - - def test_system_echo(capfdbinary): - os.system('echo "hello"') - captured = capfdbinary.readouterr() - assert captured.out == b"hello\n" - - """ - capman: CaptureManager = request.config.pluginmanager.getplugin("capturemanager") - capture_fixture = CaptureFixture(FDCaptureBinary, request, _ispytest=True) - capman.set_fixture(capture_fixture) - capture_fixture._start() - yield capture_fixture - capture_fixture.close() - capman.unset_fixture() diff --git a/.venv/lib/python3.12/site-packages/_pytest/compat.py b/.venv/lib/python3.12/site-packages/_pytest/compat.py deleted file mode 100644 index 614848e0..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/compat.py +++ /dev/null @@ -1,351 +0,0 @@ -# mypy: allow-untyped-defs -"""Python version compatibility code.""" - -from __future__ import annotations - -import dataclasses -import enum -import functools -import inspect -from inspect import Parameter -from inspect import signature -import os -from pathlib import Path -import sys -from typing import Any -from typing import Callable -from typing import Final -from typing import NoReturn - -import py - - -#: constant to prepare valuing pylib path replacements/lazy proxies later on -# intended for removal in pytest 8.0 or 9.0 - -# fmt: off -# intentional space to create a fake difference for the verification -LEGACY_PATH = py.path. local -# fmt: on - - -def legacy_path(path: str | os.PathLike[str]) -> LEGACY_PATH: - """Internal wrapper to prepare lazy proxies for legacy_path instances""" - return LEGACY_PATH(path) - - -# fmt: off -# Singleton type for NOTSET, as described in: -# https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions -class NotSetType(enum.Enum): - token = 0 -NOTSET: Final = NotSetType.token -# fmt: on - - -def is_generator(func: object) -> bool: - genfunc = inspect.isgeneratorfunction(func) - return genfunc and not iscoroutinefunction(func) - - -def iscoroutinefunction(func: object) -> bool: - """Return True if func is a coroutine function (a function defined with async - def syntax, and doesn't contain yield), or a function decorated with - @asyncio.coroutine. - - Note: copied and modified from Python 3.5's builtin coroutines.py to avoid - importing asyncio directly, which in turns also initializes the "logging" - module as a side-effect (see issue #8). - """ - return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False) - - -def is_async_function(func: object) -> bool: - """Return True if the given function seems to be an async function or - an async generator.""" - return iscoroutinefunction(func) or inspect.isasyncgenfunction(func) - - -def getlocation(function, curdir: str | os.PathLike[str] | None = None) -> str: - function = get_real_func(function) - fn = Path(inspect.getfile(function)) - lineno = function.__code__.co_firstlineno - if curdir is not None: - try: - relfn = fn.relative_to(curdir) - except ValueError: - pass - else: - return "%s:%d" % (relfn, lineno + 1) - return "%s:%d" % (fn, lineno + 1) - - -def num_mock_patch_args(function) -> int: - """Return number of arguments used up by mock arguments (if any).""" - patchings = getattr(function, "patchings", None) - if not patchings: - return 0 - - mock_sentinel = getattr(sys.modules.get("mock"), "DEFAULT", object()) - ut_mock_sentinel = getattr(sys.modules.get("unittest.mock"), "DEFAULT", object()) - - return len( - [ - p - for p in patchings - if not p.attribute_name - and (p.new is mock_sentinel or p.new is ut_mock_sentinel) - ] - ) - - -def getfuncargnames( - function: Callable[..., object], - *, - name: str = "", - cls: type | None = None, -) -> tuple[str, ...]: - """Return the names of a function's mandatory arguments. - - Should return the names of all function arguments that: - * Aren't bound to an instance or type as in instance or class methods. - * Don't have default values. - * Aren't bound with functools.partial. - * Aren't replaced with mocks. - - The cls arguments indicate that the function should be treated as a bound - method even though it's not unless the function is a static method. - - The name parameter should be the original name in which the function was collected. - """ - # TODO(RonnyPfannschmidt): This function should be refactored when we - # revisit fixtures. The fixture mechanism should ask the node for - # the fixture names, and not try to obtain directly from the - # function object well after collection has occurred. - - # The parameters attribute of a Signature object contains an - # ordered mapping of parameter names to Parameter instances. This - # creates a tuple of the names of the parameters that don't have - # defaults. - try: - parameters = signature(function).parameters - except (ValueError, TypeError) as e: - from _pytest.outcomes import fail - - fail( - f"Could not determine arguments of {function!r}: {e}", - pytrace=False, - ) - - arg_names = tuple( - p.name - for p in parameters.values() - if ( - p.kind is Parameter.POSITIONAL_OR_KEYWORD - or p.kind is Parameter.KEYWORD_ONLY - ) - and p.default is Parameter.empty - ) - if not name: - name = function.__name__ - - # If this function should be treated as a bound method even though - # it's passed as an unbound method or function, remove the first - # parameter name. - if ( - # Not using `getattr` because we don't want to resolve the staticmethod. - # Not using `cls.__dict__` because we want to check the entire MRO. - cls - and not isinstance( - inspect.getattr_static(cls, name, default=None), staticmethod - ) - ): - arg_names = arg_names[1:] - # Remove any names that will be replaced with mocks. - if hasattr(function, "__wrapped__"): - arg_names = arg_names[num_mock_patch_args(function) :] - return arg_names - - -def get_default_arg_names(function: Callable[..., Any]) -> tuple[str, ...]: - # Note: this code intentionally mirrors the code at the beginning of - # getfuncargnames, to get the arguments which were excluded from its result - # because they had default values. - return tuple( - p.name - for p in signature(function).parameters.values() - if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY) - and p.default is not Parameter.empty - ) - - -_non_printable_ascii_translate_table = { - i: f"\\x{i:02x}" for i in range(128) if i not in range(32, 127) -} -_non_printable_ascii_translate_table.update( - {ord("\t"): "\\t", ord("\r"): "\\r", ord("\n"): "\\n"} -) - - -def ascii_escaped(val: bytes | str) -> str: - r"""If val is pure ASCII, return it as an str, otherwise, escape - bytes objects into a sequence of escaped bytes: - - b'\xc3\xb4\xc5\xd6' -> r'\xc3\xb4\xc5\xd6' - - and escapes strings into a sequence of escaped unicode ids, e.g.: - - r'4\nV\U00043efa\x0eMXWB\x1e\u3028\u15fd\xcd\U0007d944' - - Note: - The obvious "v.decode('unicode-escape')" will return - valid UTF-8 unicode if it finds them in bytes, but we - want to return escaped bytes for any byte, even if they match - a UTF-8 string. - """ - if isinstance(val, bytes): - ret = val.decode("ascii", "backslashreplace") - else: - ret = val.encode("unicode_escape").decode("ascii") - return ret.translate(_non_printable_ascii_translate_table) - - -@dataclasses.dataclass -class _PytestWrapper: - """Dummy wrapper around a function object for internal use only. - - Used to correctly unwrap the underlying function object when we are - creating fixtures, because we wrap the function object ourselves with a - decorator to issue warnings when the fixture function is called directly. - """ - - obj: Any - - -def get_real_func(obj): - """Get the real function object of the (possibly) wrapped object by - functools.wraps or functools.partial.""" - start_obj = obj - for i in range(100): - # __pytest_wrapped__ is set by @pytest.fixture when wrapping the fixture function - # to trigger a warning if it gets called directly instead of by pytest: we don't - # want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774) - new_obj = getattr(obj, "__pytest_wrapped__", None) - if isinstance(new_obj, _PytestWrapper): - obj = new_obj.obj - break - new_obj = getattr(obj, "__wrapped__", None) - if new_obj is None: - break - obj = new_obj - else: - from _pytest._io.saferepr import saferepr - - raise ValueError( - f"could not find real function of {saferepr(start_obj)}\nstopped at {saferepr(obj)}" - ) - if isinstance(obj, functools.partial): - obj = obj.func - return obj - - -def get_real_method(obj, holder): - """Attempt to obtain the real function object that might be wrapping - ``obj``, while at the same time returning a bound method to ``holder`` if - the original object was a bound method.""" - try: - is_method = hasattr(obj, "__func__") - obj = get_real_func(obj) - except Exception: # pragma: no cover - return obj - if is_method and hasattr(obj, "__get__") and callable(obj.__get__): - obj = obj.__get__(holder) - return obj - - -def getimfunc(func): - try: - return func.__func__ - except AttributeError: - return func - - -def safe_getattr(object: Any, name: str, default: Any) -> Any: - """Like getattr but return default upon any Exception or any OutcomeException. - - Attribute access can potentially fail for 'evil' Python objects. - See issue #214. - It catches OutcomeException because of #2490 (issue #580), new outcomes - are derived from BaseException instead of Exception (for more details - check #2707). - """ - from _pytest.outcomes import TEST_OUTCOME - - try: - return getattr(object, name, default) - except TEST_OUTCOME: - return default - - -def safe_isclass(obj: object) -> bool: - """Ignore any exception via isinstance on Python 3.""" - try: - return inspect.isclass(obj) - except Exception: - return False - - -def get_user_id() -> int | None: - """Return the current process's real user id or None if it could not be - determined. - - :return: The user id or None if it could not be determined. - """ - # mypy follows the version and platform checking expectation of PEP 484: - # https://mypy.readthedocs.io/en/stable/common_issues.html?highlight=platform#python-version-and-system-platform-checks - # Containment checks are too complex for mypy v1.5.0 and cause failure. - if sys.platform == "win32" or sys.platform == "emscripten": - # win32 does not have a getuid() function. - # Emscripten has a return 0 stub. - return None - else: - # On other platforms, a return value of -1 is assumed to indicate that - # the current process's real user id could not be determined. - ERROR = -1 - uid = os.getuid() - return uid if uid != ERROR else None - - -# Perform exhaustiveness checking. -# -# Consider this example: -# -# MyUnion = Union[int, str] -# -# def handle(x: MyUnion) -> int { -# if isinstance(x, int): -# return 1 -# elif isinstance(x, str): -# return 2 -# else: -# raise Exception('unreachable') -# -# Now suppose we add a new variant: -# -# MyUnion = Union[int, str, bytes] -# -# After doing this, we must remember ourselves to go and update the handle -# function to handle the new variant. -# -# With `assert_never` we can do better: -# -# // raise Exception('unreachable') -# return assert_never(x) -# -# Now, if we forget to handle the new variant, the type-checker will emit a -# compile-time error, instead of the runtime error we would have gotten -# previously. -# -# This also work for Enums (if you use `is` to compare) and Literals. -def assert_never(value: NoReturn) -> NoReturn: - assert False, f"Unhandled value: {value} ({type(value).__name__})" diff --git a/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py deleted file mode 100644 index 710e03e4..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/config/__init__.py +++ /dev/null @@ -1,1973 +0,0 @@ -# mypy: allow-untyped-defs -"""Command line options, ini-file and conftest.py processing.""" - -from __future__ import annotations - -import argparse -import collections.abc -import copy -import dataclasses -import enum -from functools import lru_cache -import glob -import importlib.metadata -import inspect -import os -import pathlib -import re -import shlex -import sys -from textwrap import dedent -import types -from types import FunctionType -from typing import Any -from typing import Callable -from typing import cast -from typing import Final -from typing import final -from typing import Generator -from typing import IO -from typing import Iterable -from typing import Iterator -from typing import Sequence -from typing import TextIO -from typing import Type -from typing import TYPE_CHECKING -import warnings - -import pluggy -from pluggy import HookimplMarker -from pluggy import HookimplOpts -from pluggy import HookspecMarker -from pluggy import HookspecOpts -from pluggy import PluginManager - -from .compat import PathAwareHookProxy -from .exceptions import PrintHelp as PrintHelp -from .exceptions import UsageError as UsageError -from .findpaths import determine_setup -from _pytest import __version__ -import _pytest._code -from _pytest._code import ExceptionInfo -from _pytest._code import filter_traceback -from _pytest._code.code import TracebackStyle -from _pytest._io import TerminalWriter -from _pytest.config.argparsing import Argument -from _pytest.config.argparsing import Parser -import _pytest.deprecated -import _pytest.hookspec -from _pytest.outcomes import fail -from _pytest.outcomes import Skipped -from _pytest.pathlib import absolutepath -from _pytest.pathlib import bestrelpath -from _pytest.pathlib import import_path -from _pytest.pathlib import ImportMode -from _pytest.pathlib import resolve_package_path -from _pytest.pathlib import safe_exists -from _pytest.stash import Stash -from _pytest.warning_types import PytestConfigWarning -from _pytest.warning_types import warn_explicit_for - - -if TYPE_CHECKING: - from _pytest.cacheprovider import Cache - from _pytest.terminal import TerminalReporter - - -_PluggyPlugin = object -"""A type to represent plugin objects. - -Plugins can be any namespace, so we can't narrow it down much, but we use an -alias to make the intent clear. - -Ideally this type would be provided by pluggy itself. -""" - - -hookimpl = HookimplMarker("pytest") -hookspec = HookspecMarker("pytest") - - -@final -class ExitCode(enum.IntEnum): - """Encodes the valid exit codes by pytest. - - Currently users and plugins may supply other exit codes as well. - - .. versionadded:: 5.0 - """ - - #: Tests passed. - OK = 0 - #: Tests failed. - TESTS_FAILED = 1 - #: pytest was interrupted. - INTERRUPTED = 2 - #: An internal error got in the way. - INTERNAL_ERROR = 3 - #: pytest was misused. - USAGE_ERROR = 4 - #: pytest couldn't find tests. - NO_TESTS_COLLECTED = 5 - - -class ConftestImportFailure(Exception): - def __init__( - self, - path: pathlib.Path, - *, - cause: Exception, - ) -> None: - self.path = path - self.cause = cause - - def __str__(self) -> str: - return f"{type(self.cause).__name__}: {self.cause} (from {self.path})" - - -def filter_traceback_for_conftest_import_failure( - entry: _pytest._code.TracebackEntry, -) -> bool: - """Filter tracebacks entries which point to pytest internals or importlib. - - Make a special case for importlib because we use it to import test modules and conftest files - in _pytest.pathlib.import_path. - """ - return filter_traceback(entry) and "importlib" not in str(entry.path).split(os.sep) - - -def main( - args: list[str] | os.PathLike[str] | None = None, - plugins: Sequence[str | _PluggyPlugin] | None = None, -) -> int | ExitCode: - """Perform an in-process test run. - - :param args: - List of command line arguments. If `None` or not given, defaults to reading - arguments directly from the process command line (:data:`sys.argv`). - :param plugins: List of plugin objects to be auto-registered during initialization. - - :returns: An exit code. - """ - old_pytest_version = os.environ.get("PYTEST_VERSION") - try: - os.environ["PYTEST_VERSION"] = __version__ - try: - config = _prepareconfig(args, plugins) - except ConftestImportFailure as e: - exc_info = ExceptionInfo.from_exception(e.cause) - tw = TerminalWriter(sys.stderr) - tw.line(f"ImportError while loading conftest '{e.path}'.", red=True) - exc_info.traceback = exc_info.traceback.filter( - filter_traceback_for_conftest_import_failure - ) - exc_repr = ( - exc_info.getrepr(style="short", chain=False) - if exc_info.traceback - else exc_info.exconly() - ) - formatted_tb = str(exc_repr) - for line in formatted_tb.splitlines(): - tw.line(line.rstrip(), red=True) - return ExitCode.USAGE_ERROR - else: - try: - ret: ExitCode | int = config.hook.pytest_cmdline_main(config=config) - try: - return ExitCode(ret) - except ValueError: - return ret - finally: - config._ensure_unconfigure() - except UsageError as e: - tw = TerminalWriter(sys.stderr) - for msg in e.args: - tw.line(f"ERROR: {msg}\n", red=True) - return ExitCode.USAGE_ERROR - finally: - if old_pytest_version is None: - os.environ.pop("PYTEST_VERSION", None) - else: - os.environ["PYTEST_VERSION"] = old_pytest_version - - -def console_main() -> int: - """The CLI entry point of pytest. - - This function is not meant for programmable use; use `main()` instead. - """ - # https://docs.python.org/3/library/signal.html#note-on-sigpipe - try: - code = main() - sys.stdout.flush() - return code - except BrokenPipeError: - # Python flushes standard streams on exit; redirect remaining output - # to devnull to avoid another BrokenPipeError at shutdown - devnull = os.open(os.devnull, os.O_WRONLY) - os.dup2(devnull, sys.stdout.fileno()) - return 1 # Python exits with error code 1 on EPIPE - - -class cmdline: # compatibility namespace - main = staticmethod(main) - - -def filename_arg(path: str, optname: str) -> str: - """Argparse type validator for filename arguments. - - :path: Path of filename. - :optname: Name of the option. - """ - if os.path.isdir(path): - raise UsageError(f"{optname} must be a filename, given: {path}") - return path - - -def directory_arg(path: str, optname: str) -> str: - """Argparse type validator for directory arguments. - - :path: Path of directory. - :optname: Name of the option. - """ - if not os.path.isdir(path): - raise UsageError(f"{optname} must be a directory, given: {path}") - return path - - -# Plugins that cannot be disabled via "-p no:X" currently. -essential_plugins = ( - "mark", - "main", - "runner", - "fixtures", - "helpconfig", # Provides -p. -) - -default_plugins = ( - *essential_plugins, - "python", - "terminal", - "debugging", - "unittest", - "capture", - "skipping", - "legacypath", - "tmpdir", - "monkeypatch", - "recwarn", - "pastebin", - "assertion", - "junitxml", - "doctest", - "cacheprovider", - "freeze_support", - "setuponly", - "setupplan", - "stepwise", - "warnings", - "logging", - "reports", - "python_path", - "unraisableexception", - "threadexception", - "faulthandler", -) - -builtin_plugins = set(default_plugins) -builtin_plugins.add("pytester") -builtin_plugins.add("pytester_assertions") - - -def get_config( - args: list[str] | None = None, - plugins: Sequence[str | _PluggyPlugin] | None = None, -) -> Config: - # subsequent calls to main will create a fresh instance - pluginmanager = PytestPluginManager() - config = Config( - pluginmanager, - invocation_params=Config.InvocationParams( - args=args or (), - plugins=plugins, - dir=pathlib.Path.cwd(), - ), - ) - - if args is not None: - # Handle any "-p no:plugin" args. - pluginmanager.consider_preparse(args, exclude_only=True) - - for spec in default_plugins: - pluginmanager.import_plugin(spec) - - return config - - -def get_plugin_manager() -> PytestPluginManager: - """Obtain a new instance of the - :py:class:`pytest.PytestPluginManager`, with default plugins - already loaded. - - This function can be used by integration with other tools, like hooking - into pytest to run tests into an IDE. - """ - return get_config().pluginmanager - - -def _prepareconfig( - args: list[str] | os.PathLike[str] | None = None, - plugins: Sequence[str | _PluggyPlugin] | None = None, -) -> Config: - if args is None: - args = sys.argv[1:] - elif isinstance(args, os.PathLike): - args = [os.fspath(args)] - elif not isinstance(args, list): - msg = ( # type:ignore[unreachable] - "`args` parameter expected to be a list of strings, got: {!r} (type: {})" - ) - raise TypeError(msg.format(args, type(args))) - - config = get_config(args, plugins) - pluginmanager = config.pluginmanager - try: - if plugins: - for plugin in plugins: - if isinstance(plugin, str): - pluginmanager.consider_pluginarg(plugin) - else: - pluginmanager.register(plugin) - config = pluginmanager.hook.pytest_cmdline_parse( - pluginmanager=pluginmanager, args=args - ) - return config - except BaseException: - config._ensure_unconfigure() - raise - - -def _get_directory(path: pathlib.Path) -> pathlib.Path: - """Get the directory of a path - itself if already a directory.""" - if path.is_file(): - return path.parent - else: - return path - - -def _get_legacy_hook_marks( - method: Any, - hook_type: str, - opt_names: tuple[str, ...], -) -> dict[str, bool]: - if TYPE_CHECKING: - # abuse typeguard from importlib to avoid massive method type union that's lacking an alias - assert inspect.isroutine(method) - known_marks: set[str] = {m.name for m in getattr(method, "pytestmark", [])} - must_warn: list[str] = [] - opts: dict[str, bool] = {} - for opt_name in opt_names: - opt_attr = getattr(method, opt_name, AttributeError) - if opt_attr is not AttributeError: - must_warn.append(f"{opt_name}={opt_attr}") - opts[opt_name] = True - elif opt_name in known_marks: - must_warn.append(f"{opt_name}=True") - opts[opt_name] = True - else: - opts[opt_name] = False - if must_warn: - hook_opts = ", ".join(must_warn) - message = _pytest.deprecated.HOOK_LEGACY_MARKING.format( - type=hook_type, - fullname=method.__qualname__, - hook_opts=hook_opts, - ) - warn_explicit_for(cast(FunctionType, method), message) - return opts - - -@final -class PytestPluginManager(PluginManager): - """A :py:class:`pluggy.PluginManager ` with - additional pytest-specific functionality: - - * Loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and - ``pytest_plugins`` global variables found in plugins being loaded. - * ``conftest.py`` loading during start-up. - """ - - def __init__(self) -> None: - import _pytest.assertion - - super().__init__("pytest") - - # -- State related to local conftest plugins. - # All loaded conftest modules. - self._conftest_plugins: set[types.ModuleType] = set() - # All conftest modules applicable for a directory. - # This includes the directory's own conftest modules as well - # as those of its parent directories. - self._dirpath2confmods: dict[pathlib.Path, list[types.ModuleType]] = {} - # Cutoff directory above which conftests are no longer discovered. - self._confcutdir: pathlib.Path | None = None - # If set, conftest loading is skipped. - self._noconftest = False - - # _getconftestmodules()'s call to _get_directory() causes a stat - # storm when it's called potentially thousands of times in a test - # session (#9478), often with the same path, so cache it. - self._get_directory = lru_cache(256)(_get_directory) - - # plugins that were explicitly skipped with pytest.skip - # list of (module name, skip reason) - # previously we would issue a warning when a plugin was skipped, but - # since we refactored warnings as first citizens of Config, they are - # just stored here to be used later. - self.skipped_plugins: list[tuple[str, str]] = [] - - self.add_hookspecs(_pytest.hookspec) - self.register(self) - if os.environ.get("PYTEST_DEBUG"): - err: IO[str] = sys.stderr - encoding: str = getattr(err, "encoding", "utf8") - try: - err = open( - os.dup(err.fileno()), - mode=err.mode, - buffering=1, - encoding=encoding, - ) - except Exception: - pass - self.trace.root.setwriter(err.write) - self.enable_tracing() - - # Config._consider_importhook will set a real object if required. - self.rewrite_hook = _pytest.assertion.DummyRewriteHook() - # Used to know when we are importing conftests after the pytest_configure stage. - self._configured = False - - def parse_hookimpl_opts( - self, plugin: _PluggyPlugin, name: str - ) -> HookimplOpts | None: - """:meta private:""" - # pytest hooks are always prefixed with "pytest_", - # so we avoid accessing possibly non-readable attributes - # (see issue #1073). - if not name.startswith("pytest_"): - return None - # Ignore names which cannot be hooks. - if name == "pytest_plugins": - return None - - opts = super().parse_hookimpl_opts(plugin, name) - if opts is not None: - return opts - - method = getattr(plugin, name) - # Consider only actual functions for hooks (#3775). - if not inspect.isroutine(method): - return None - # Collect unmarked hooks as long as they have the `pytest_' prefix. - return _get_legacy_hook_marks( # type: ignore[return-value] - method, "impl", ("tryfirst", "trylast", "optionalhook", "hookwrapper") - ) - - def parse_hookspec_opts(self, module_or_class, name: str) -> HookspecOpts | None: - """:meta private:""" - opts = super().parse_hookspec_opts(module_or_class, name) - if opts is None: - method = getattr(module_or_class, name) - if name.startswith("pytest_"): - opts = _get_legacy_hook_marks( # type: ignore[assignment] - method, - "spec", - ("firstresult", "historic"), - ) - return opts - - def register(self, plugin: _PluggyPlugin, name: str | None = None) -> str | None: - if name in _pytest.deprecated.DEPRECATED_EXTERNAL_PLUGINS: - warnings.warn( - PytestConfigWarning( - "{} plugin has been merged into the core, " - "please remove it from your requirements.".format( - name.replace("_", "-") - ) - ) - ) - return None - plugin_name = super().register(plugin, name) - if plugin_name is not None: - self.hook.pytest_plugin_registered.call_historic( - kwargs=dict( - plugin=plugin, - plugin_name=plugin_name, - manager=self, - ) - ) - - if isinstance(plugin, types.ModuleType): - self.consider_module(plugin) - return plugin_name - - def getplugin(self, name: str): - # Support deprecated naming because plugins (xdist e.g.) use it. - plugin: _PluggyPlugin | None = self.get_plugin(name) - return plugin - - def hasplugin(self, name: str) -> bool: - """Return whether a plugin with the given name is registered.""" - return bool(self.get_plugin(name)) - - def pytest_configure(self, config: Config) -> None: - """:meta private:""" - # XXX now that the pluginmanager exposes hookimpl(tryfirst...) - # we should remove tryfirst/trylast as markers. - config.addinivalue_line( - "markers", - "tryfirst: mark a hook implementation function such that the " - "plugin machinery will try to call it first/as early as possible. " - "DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.", - ) - config.addinivalue_line( - "markers", - "trylast: mark a hook implementation function such that the " - "plugin machinery will try to call it last/as late as possible. " - "DEPRECATED, use @pytest.hookimpl(trylast=True) instead.", - ) - self._configured = True - - # - # Internal API for local conftest plugin handling. - # - def _set_initial_conftests( - self, - args: Sequence[str | pathlib.Path], - pyargs: bool, - noconftest: bool, - rootpath: pathlib.Path, - confcutdir: pathlib.Path | None, - invocation_dir: pathlib.Path, - importmode: ImportMode | str, - *, - consider_namespace_packages: bool, - ) -> None: - """Load initial conftest files given a preparsed "namespace". - - As conftest files may add their own command line options which have - arguments ('--my-opt somepath') we might get some false positives. - All builtin and 3rd party plugins will have been loaded, however, so - common options will not confuse our logic here. - """ - self._confcutdir = ( - absolutepath(invocation_dir / confcutdir) if confcutdir else None - ) - self._noconftest = noconftest - self._using_pyargs = pyargs - foundanchor = False - for initial_path in args: - path = str(initial_path) - # remove node-id syntax - i = path.find("::") - if i != -1: - path = path[:i] - anchor = absolutepath(invocation_dir / path) - - # Ensure we do not break if what appears to be an anchor - # is in fact a very long option (#10169, #11394). - if safe_exists(anchor): - self._try_load_conftest( - anchor, - importmode, - rootpath, - consider_namespace_packages=consider_namespace_packages, - ) - foundanchor = True - if not foundanchor: - self._try_load_conftest( - invocation_dir, - importmode, - rootpath, - consider_namespace_packages=consider_namespace_packages, - ) - - def _is_in_confcutdir(self, path: pathlib.Path) -> bool: - """Whether to consider the given path to load conftests from.""" - if self._confcutdir is None: - return True - # The semantics here are literally: - # Do not load a conftest if it is found upwards from confcut dir. - # But this is *not* the same as: - # Load only conftests from confcutdir or below. - # At first glance they might seem the same thing, however we do support use cases where - # we want to load conftests that are not found in confcutdir or below, but are found - # in completely different directory hierarchies like packages installed - # in out-of-source trees. - # (see #9767 for a regression where the logic was inverted). - return path not in self._confcutdir.parents - - def _try_load_conftest( - self, - anchor: pathlib.Path, - importmode: str | ImportMode, - rootpath: pathlib.Path, - *, - consider_namespace_packages: bool, - ) -> None: - self._loadconftestmodules( - anchor, - importmode, - rootpath, - consider_namespace_packages=consider_namespace_packages, - ) - # let's also consider test* subdirs - if anchor.is_dir(): - for x in anchor.glob("test*"): - if x.is_dir(): - self._loadconftestmodules( - x, - importmode, - rootpath, - consider_namespace_packages=consider_namespace_packages, - ) - - def _loadconftestmodules( - self, - path: pathlib.Path, - importmode: str | ImportMode, - rootpath: pathlib.Path, - *, - consider_namespace_packages: bool, - ) -> None: - if self._noconftest: - return - - directory = self._get_directory(path) - - # Optimization: avoid repeated searches in the same directory. - # Assumes always called with same importmode and rootpath. - if directory in self._dirpath2confmods: - return - - clist = [] - for parent in reversed((directory, *directory.parents)): - if self._is_in_confcutdir(parent): - conftestpath = parent / "conftest.py" - if conftestpath.is_file(): - mod = self._importconftest( - conftestpath, - importmode, - rootpath, - consider_namespace_packages=consider_namespace_packages, - ) - clist.append(mod) - self._dirpath2confmods[directory] = clist - - def _getconftestmodules(self, path: pathlib.Path) -> Sequence[types.ModuleType]: - directory = self._get_directory(path) - return self._dirpath2confmods.get(directory, ()) - - def _rget_with_confmod( - self, - name: str, - path: pathlib.Path, - ) -> tuple[types.ModuleType, Any]: - modules = self._getconftestmodules(path) - for mod in reversed(modules): - try: - return mod, getattr(mod, name) - except AttributeError: - continue - raise KeyError(name) - - def _importconftest( - self, - conftestpath: pathlib.Path, - importmode: str | ImportMode, - rootpath: pathlib.Path, - *, - consider_namespace_packages: bool, - ) -> types.ModuleType: - conftestpath_plugin_name = str(conftestpath) - existing = self.get_plugin(conftestpath_plugin_name) - if existing is not None: - return cast(types.ModuleType, existing) - - # conftest.py files there are not in a Python package all have module - # name "conftest", and thus conflict with each other. Clear the existing - # before loading the new one, otherwise the existing one will be - # returned from the module cache. - pkgpath = resolve_package_path(conftestpath) - if pkgpath is None: - try: - del sys.modules[conftestpath.stem] - except KeyError: - pass - - try: - mod = import_path( - conftestpath, - mode=importmode, - root=rootpath, - consider_namespace_packages=consider_namespace_packages, - ) - except Exception as e: - assert e.__traceback__ is not None - raise ConftestImportFailure(conftestpath, cause=e) from e - - self._check_non_top_pytest_plugins(mod, conftestpath) - - self._conftest_plugins.add(mod) - dirpath = conftestpath.parent - if dirpath in self._dirpath2confmods: - for path, mods in self._dirpath2confmods.items(): - if dirpath in path.parents or path == dirpath: - if mod in mods: - raise AssertionError( - f"While trying to load conftest path {conftestpath!s}, " - f"found that the module {mod} is already loaded with path {mod.__file__}. " - "This is not supposed to happen. Please report this issue to pytest." - ) - mods.append(mod) - self.trace(f"loading conftestmodule {mod!r}") - self.consider_conftest(mod, registration_name=conftestpath_plugin_name) - return mod - - def _check_non_top_pytest_plugins( - self, - mod: types.ModuleType, - conftestpath: pathlib.Path, - ) -> None: - if ( - hasattr(mod, "pytest_plugins") - and self._configured - and not self._using_pyargs - ): - msg = ( - "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported:\n" - "It affects the entire test suite instead of just below the conftest as expected.\n" - " {}\n" - "Please move it to a top level conftest file at the rootdir:\n" - " {}\n" - "For more information, visit:\n" - " https://docs.pytest.org/en/stable/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" - ) - fail(msg.format(conftestpath, self._confcutdir), pytrace=False) - - # - # API for bootstrapping plugin loading - # - # - - def consider_preparse( - self, args: Sequence[str], *, exclude_only: bool = False - ) -> None: - """:meta private:""" - i = 0 - n = len(args) - while i < n: - opt = args[i] - i += 1 - if isinstance(opt, str): - if opt == "-p": - try: - parg = args[i] - except IndexError: - return - i += 1 - elif opt.startswith("-p"): - parg = opt[2:] - else: - continue - parg = parg.strip() - if exclude_only and not parg.startswith("no:"): - continue - self.consider_pluginarg(parg) - - def consider_pluginarg(self, arg: str) -> None: - """:meta private:""" - if arg.startswith("no:"): - name = arg[3:] - if name in essential_plugins: - raise UsageError(f"plugin {name} cannot be disabled") - - # PR #4304: remove stepwise if cacheprovider is blocked. - if name == "cacheprovider": - self.set_blocked("stepwise") - self.set_blocked("pytest_stepwise") - - self.set_blocked(name) - if not name.startswith("pytest_"): - self.set_blocked("pytest_" + name) - else: - name = arg - # Unblock the plugin. - self.unblock(name) - if not name.startswith("pytest_"): - self.unblock("pytest_" + name) - self.import_plugin(arg, consider_entry_points=True) - - def consider_conftest( - self, conftestmodule: types.ModuleType, registration_name: str - ) -> None: - """:meta private:""" - self.register(conftestmodule, name=registration_name) - - def consider_env(self) -> None: - """:meta private:""" - self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) - - def consider_module(self, mod: types.ModuleType) -> None: - """:meta private:""" - self._import_plugin_specs(getattr(mod, "pytest_plugins", [])) - - def _import_plugin_specs( - self, spec: None | types.ModuleType | str | Sequence[str] - ) -> None: - plugins = _get_plugin_specs_as_list(spec) - for import_spec in plugins: - self.import_plugin(import_spec) - - def import_plugin(self, modname: str, consider_entry_points: bool = False) -> None: - """Import a plugin with ``modname``. - - If ``consider_entry_points`` is True, entry point names are also - considered to find a plugin. - """ - # Most often modname refers to builtin modules, e.g. "pytester", - # "terminal" or "capture". Those plugins are registered under their - # basename for historic purposes but must be imported with the - # _pytest prefix. - assert isinstance( - modname, str - ), f"module name as text required, got {modname!r}" - if self.is_blocked(modname) or self.get_plugin(modname) is not None: - return - - importspec = "_pytest." + modname if modname in builtin_plugins else modname - self.rewrite_hook.mark_rewrite(importspec) - - if consider_entry_points: - loaded = self.load_setuptools_entrypoints("pytest11", name=modname) - if loaded: - return - - try: - __import__(importspec) - except ImportError as e: - raise ImportError( - f'Error importing plugin "{modname}": {e.args[0]}' - ).with_traceback(e.__traceback__) from e - - except Skipped as e: - self.skipped_plugins.append((modname, e.msg or "")) - else: - mod = sys.modules[importspec] - self.register(mod, modname) - - -def _get_plugin_specs_as_list( - specs: None | types.ModuleType | str | Sequence[str], -) -> list[str]: - """Parse a plugins specification into a list of plugin names.""" - # None means empty. - if specs is None: - return [] - # Workaround for #3899 - a submodule which happens to be called "pytest_plugins". - if isinstance(specs, types.ModuleType): - return [] - # Comma-separated list. - if isinstance(specs, str): - return specs.split(",") if specs else [] - # Direct specification. - if isinstance(specs, collections.abc.Sequence): - return list(specs) - raise UsageError( - f"Plugins may be specified as a sequence or a ','-separated string of plugin names. Got: {specs!r}" - ) - - -class Notset: - def __repr__(self): - return "" - - -notset = Notset() - - -def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]: - """Given an iterable of file names in a source distribution, return the "names" that should - be marked for assertion rewrite. - - For example the package "pytest_mock/__init__.py" should be added as "pytest_mock" in - the assertion rewrite mechanism. - - This function has to deal with dist-info based distributions and egg based distributions - (which are still very much in use for "editable" installs). - - Here are the file names as seen in a dist-info based distribution: - - pytest_mock/__init__.py - pytest_mock/_version.py - pytest_mock/plugin.py - pytest_mock.egg-info/PKG-INFO - - Here are the file names as seen in an egg based distribution: - - src/pytest_mock/__init__.py - src/pytest_mock/_version.py - src/pytest_mock/plugin.py - src/pytest_mock.egg-info/PKG-INFO - LICENSE - setup.py - - We have to take in account those two distribution flavors in order to determine which - names should be considered for assertion rewriting. - - More information: - https://github.com/pytest-dev/pytest-mock/issues/167 - """ - package_files = list(package_files) - seen_some = False - for fn in package_files: - is_simple_module = "/" not in fn and fn.endswith(".py") - is_package = fn.count("/") == 1 and fn.endswith("__init__.py") - if is_simple_module: - module_name, _ = os.path.splitext(fn) - # we ignore "setup.py" at the root of the distribution - # as well as editable installation finder modules made by setuptools - if module_name != "setup" and not module_name.startswith("__editable__"): - seen_some = True - yield module_name - elif is_package: - package_name = os.path.dirname(fn) - seen_some = True - yield package_name - - if not seen_some: - # At this point we did not find any packages or modules suitable for assertion - # rewriting, so we try again by stripping the first path component (to account for - # "src" based source trees for example). - # This approach lets us have the common case continue to be fast, as egg-distributions - # are rarer. - new_package_files = [] - for fn in package_files: - parts = fn.split("/") - new_fn = "/".join(parts[1:]) - if new_fn: - new_package_files.append(new_fn) - if new_package_files: - yield from _iter_rewritable_modules(new_package_files) - - -@final -class Config: - """Access to configuration values, pluginmanager and plugin hooks. - - :param PytestPluginManager pluginmanager: - A pytest PluginManager. - - :param InvocationParams invocation_params: - Object containing parameters regarding the :func:`pytest.main` - invocation. - """ - - @final - @dataclasses.dataclass(frozen=True) - class InvocationParams: - """Holds parameters passed during :func:`pytest.main`. - - The object attributes are read-only. - - .. versionadded:: 5.1 - - .. note:: - - Note that the environment variable ``PYTEST_ADDOPTS`` and the ``addopts`` - ini option are handled by pytest, not being included in the ``args`` attribute. - - Plugins accessing ``InvocationParams`` must be aware of that. - """ - - args: tuple[str, ...] - """The command-line arguments as passed to :func:`pytest.main`.""" - plugins: Sequence[str | _PluggyPlugin] | None - """Extra plugins, might be `None`.""" - dir: pathlib.Path - """The directory from which :func:`pytest.main` was invoked. :type: pathlib.Path""" - - def __init__( - self, - *, - args: Iterable[str], - plugins: Sequence[str | _PluggyPlugin] | None, - dir: pathlib.Path, - ) -> None: - object.__setattr__(self, "args", tuple(args)) - object.__setattr__(self, "plugins", plugins) - object.__setattr__(self, "dir", dir) - - class ArgsSource(enum.Enum): - """Indicates the source of the test arguments. - - .. versionadded:: 7.2 - """ - - #: Command line arguments. - ARGS = enum.auto() - #: Invocation directory. - INVOCATION_DIR = enum.auto() - INCOVATION_DIR = INVOCATION_DIR # backwards compatibility alias - #: 'testpaths' configuration value. - TESTPATHS = enum.auto() - - # Set by cacheprovider plugin. - cache: Cache - - def __init__( - self, - pluginmanager: PytestPluginManager, - *, - invocation_params: InvocationParams | None = None, - ) -> None: - from .argparsing import FILE_OR_DIR - from .argparsing import Parser - - if invocation_params is None: - invocation_params = self.InvocationParams( - args=(), plugins=None, dir=pathlib.Path.cwd() - ) - - self.option = argparse.Namespace() - """Access to command line option as attributes. - - :type: argparse.Namespace - """ - - self.invocation_params = invocation_params - """The parameters with which pytest was invoked. - - :type: InvocationParams - """ - - _a = FILE_OR_DIR - self._parser = Parser( - usage=f"%(prog)s [options] [{_a}] [{_a}] [...]", - processopt=self._processopt, - _ispytest=True, - ) - self.pluginmanager = pluginmanager - """The plugin manager handles plugin registration and hook invocation. - - :type: PytestPluginManager - """ - - self.stash = Stash() - """A place where plugins can store information on the config for their - own use. - - :type: Stash - """ - # Deprecated alias. Was never public. Can be removed in a few releases. - self._store = self.stash - - self.trace = self.pluginmanager.trace.root.get("config") - self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment] - self._inicache: dict[str, Any] = {} - self._override_ini: Sequence[str] = () - self._opt2dest: dict[str, str] = {} - self._cleanup: list[Callable[[], None]] = [] - self.pluginmanager.register(self, "pytestconfig") - self._configured = False - self.hook.pytest_addoption.call_historic( - kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager) - ) - self.args_source = Config.ArgsSource.ARGS - self.args: list[str] = [] - - @property - def rootpath(self) -> pathlib.Path: - """The path to the :ref:`rootdir `. - - :type: pathlib.Path - - .. versionadded:: 6.1 - """ - return self._rootpath - - @property - def inipath(self) -> pathlib.Path | None: - """The path to the :ref:`configfile `. - - .. versionadded:: 6.1 - """ - return self._inipath - - def add_cleanup(self, func: Callable[[], None]) -> None: - """Add a function to be called when the config object gets out of - use (usually coinciding with pytest_unconfigure).""" - self._cleanup.append(func) - - def _do_configure(self) -> None: - assert not self._configured - self._configured = True - with warnings.catch_warnings(): - warnings.simplefilter("default") - self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) - - def _ensure_unconfigure(self) -> None: - if self._configured: - self._configured = False - self.hook.pytest_unconfigure(config=self) - self.hook.pytest_configure._call_history = [] - while self._cleanup: - fin = self._cleanup.pop() - fin() - - def get_terminal_writer(self) -> TerminalWriter: - terminalreporter: TerminalReporter | None = self.pluginmanager.get_plugin( - "terminalreporter" - ) - assert terminalreporter is not None - return terminalreporter._tw - - def pytest_cmdline_parse( - self, pluginmanager: PytestPluginManager, args: list[str] - ) -> Config: - try: - self.parse(args) - except UsageError: - # Handle --version and --help here in a minimal fashion. - # This gets done via helpconfig normally, but its - # pytest_cmdline_main is not called in case of errors. - if getattr(self.option, "version", False) or "--version" in args: - from _pytest.helpconfig import showversion - - showversion(self) - elif ( - getattr(self.option, "help", False) or "--help" in args or "-h" in args - ): - self._parser._getparser().print_help() - sys.stdout.write( - "\nNOTE: displaying only minimal help due to UsageError.\n\n" - ) - - raise - - return self - - def notify_exception( - self, - excinfo: ExceptionInfo[BaseException], - option: argparse.Namespace | None = None, - ) -> None: - if option and getattr(option, "fulltrace", False): - style: TracebackStyle = "long" - else: - style = "native" - excrepr = excinfo.getrepr( - funcargs=True, showlocals=getattr(option, "showlocals", False), style=style - ) - res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo) - if not any(res): - for line in str(excrepr).split("\n"): - sys.stderr.write(f"INTERNALERROR> {line}\n") - sys.stderr.flush() - - def cwd_relative_nodeid(self, nodeid: str) -> str: - # nodeid's are relative to the rootpath, compute relative to cwd. - if self.invocation_params.dir != self.rootpath: - base_path_part, *nodeid_part = nodeid.split("::") - # Only process path part - fullpath = self.rootpath / base_path_part - relative_path = bestrelpath(self.invocation_params.dir, fullpath) - - nodeid = "::".join([relative_path, *nodeid_part]) - return nodeid - - @classmethod - def fromdictargs(cls, option_dict, args) -> Config: - """Constructor usable for subprocesses.""" - config = get_config(args) - config.option.__dict__.update(option_dict) - config.parse(args, addopts=False) - for x in config.option.plugins: - config.pluginmanager.consider_pluginarg(x) - return config - - def _processopt(self, opt: Argument) -> None: - for name in opt._short_opts + opt._long_opts: - self._opt2dest[name] = opt.dest - - if hasattr(opt, "default"): - if not hasattr(self.option, opt.dest): - setattr(self.option, opt.dest, opt.default) - - @hookimpl(trylast=True) - def pytest_load_initial_conftests(self, early_config: Config) -> None: - # We haven't fully parsed the command line arguments yet, so - # early_config.args it not set yet. But we need it for - # discovering the initial conftests. So "pre-run" the logic here. - # It will be done for real in `parse()`. - args, args_source = early_config._decide_args( - args=early_config.known_args_namespace.file_or_dir, - pyargs=early_config.known_args_namespace.pyargs, - testpaths=early_config.getini("testpaths"), - invocation_dir=early_config.invocation_params.dir, - rootpath=early_config.rootpath, - warn=False, - ) - self.pluginmanager._set_initial_conftests( - args=args, - pyargs=early_config.known_args_namespace.pyargs, - noconftest=early_config.known_args_namespace.noconftest, - rootpath=early_config.rootpath, - confcutdir=early_config.known_args_namespace.confcutdir, - invocation_dir=early_config.invocation_params.dir, - importmode=early_config.known_args_namespace.importmode, - consider_namespace_packages=early_config.getini( - "consider_namespace_packages" - ), - ) - - def _initini(self, args: Sequence[str]) -> None: - ns, unknown_args = self._parser.parse_known_and_unknown_args( - args, namespace=copy.copy(self.option) - ) - rootpath, inipath, inicfg = determine_setup( - inifile=ns.inifilename, - args=ns.file_or_dir + unknown_args, - rootdir_cmd_arg=ns.rootdir or None, - invocation_dir=self.invocation_params.dir, - ) - self._rootpath = rootpath - self._inipath = inipath - self.inicfg = inicfg - self._parser.extra_info["rootdir"] = str(self.rootpath) - self._parser.extra_info["inifile"] = str(self.inipath) - self._parser.addini("addopts", "Extra command line options", "args") - self._parser.addini("minversion", "Minimally required pytest version") - self._parser.addini( - "required_plugins", - "Plugins that must be present for pytest to run", - type="args", - default=[], - ) - self._override_ini = ns.override_ini or () - - def _consider_importhook(self, args: Sequence[str]) -> None: - """Install the PEP 302 import hook if using assertion rewriting. - - Needs to parse the --assert= option from the commandline - and find all the installed plugins to mark them for rewriting - by the importhook. - """ - ns, unknown_args = self._parser.parse_known_and_unknown_args(args) - mode = getattr(ns, "assertmode", "plain") - if mode == "rewrite": - import _pytest.assertion - - try: - hook = _pytest.assertion.install_importhook(self) - except SystemError: - mode = "plain" - else: - self._mark_plugins_for_rewrite(hook) - self._warn_about_missing_assertion(mode) - - def _mark_plugins_for_rewrite(self, hook) -> None: - """Given an importhook, mark for rewrite any top-level - modules or packages in the distribution package for - all pytest plugins.""" - self.pluginmanager.rewrite_hook = hook - - if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): - # We don't autoload from distribution package entry points, - # no need to continue. - return - - package_files = ( - str(file) - for dist in importlib.metadata.distributions() - if any(ep.group == "pytest11" for ep in dist.entry_points) - for file in dist.files or [] - ) - - for name in _iter_rewritable_modules(package_files): - hook.mark_rewrite(name) - - def _validate_args(self, args: list[str], via: str) -> list[str]: - """Validate known args.""" - self._parser._config_source_hint = via # type: ignore - try: - self._parser.parse_known_and_unknown_args( - args, namespace=copy.copy(self.option) - ) - finally: - del self._parser._config_source_hint # type: ignore - - return args - - def _decide_args( - self, - *, - args: list[str], - pyargs: bool, - testpaths: list[str], - invocation_dir: pathlib.Path, - rootpath: pathlib.Path, - warn: bool, - ) -> tuple[list[str], ArgsSource]: - """Decide the args (initial paths/nodeids) to use given the relevant inputs. - - :param warn: Whether can issue warnings. - - :returns: The args and the args source. Guaranteed to be non-empty. - """ - if args: - source = Config.ArgsSource.ARGS - result = args - else: - if invocation_dir == rootpath: - source = Config.ArgsSource.TESTPATHS - if pyargs: - result = testpaths - else: - result = [] - for path in testpaths: - result.extend(sorted(glob.iglob(path, recursive=True))) - if testpaths and not result: - if warn: - warning_text = ( - "No files were found in testpaths; " - "consider removing or adjusting your testpaths configuration. " - "Searching recursively from the current directory instead." - ) - self.issue_config_time_warning( - PytestConfigWarning(warning_text), stacklevel=3 - ) - else: - result = [] - if not result: - source = Config.ArgsSource.INVOCATION_DIR - result = [str(invocation_dir)] - return result, source - - def _preparse(self, args: list[str], addopts: bool = True) -> None: - if addopts: - env_addopts = os.environ.get("PYTEST_ADDOPTS", "") - if len(env_addopts): - args[:] = ( - self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") - + args - ) - self._initini(args) - if addopts: - args[:] = ( - self._validate_args(self.getini("addopts"), "via addopts config") + args - ) - - self.known_args_namespace = self._parser.parse_known_args( - args, namespace=copy.copy(self.option) - ) - self._checkversion() - self._consider_importhook(args) - self.pluginmanager.consider_preparse(args, exclude_only=False) - if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): - # Don't autoload from distribution package entry point. Only - # explicitly specified plugins are going to be loaded. - self.pluginmanager.load_setuptools_entrypoints("pytest11") - self.pluginmanager.consider_env() - - self.known_args_namespace = self._parser.parse_known_args( - args, namespace=copy.copy(self.known_args_namespace) - ) - - self._validate_plugins() - self._warn_about_skipped_plugins() - - if self.known_args_namespace.confcutdir is None: - if self.inipath is not None: - confcutdir = str(self.inipath.parent) - else: - confcutdir = str(self.rootpath) - self.known_args_namespace.confcutdir = confcutdir - try: - self.hook.pytest_load_initial_conftests( - early_config=self, args=args, parser=self._parser - ) - except ConftestImportFailure as e: - if self.known_args_namespace.help or self.known_args_namespace.version: - # we don't want to prevent --help/--version to work - # so just let is pass and print a warning at the end - self.issue_config_time_warning( - PytestConfigWarning(f"could not load initial conftests: {e.path}"), - stacklevel=2, - ) - else: - raise - - @hookimpl(wrapper=True) - def pytest_collection(self) -> Generator[None, object, object]: - # Validate invalid ini keys after collection is done so we take in account - # options added by late-loading conftest files. - try: - return (yield) - finally: - self._validate_config_options() - - def _checkversion(self) -> None: - import pytest - - minver = self.inicfg.get("minversion", None) - if minver: - # Imported lazily to improve start-up time. - from packaging.version import Version - - if not isinstance(minver, str): - raise pytest.UsageError( - f"{self.inipath}: 'minversion' must be a single value" - ) - - if Version(minver) > Version(pytest.__version__): - raise pytest.UsageError( - f"{self.inipath}: 'minversion' requires pytest-{minver}, actual pytest-{pytest.__version__}'" - ) - - def _validate_config_options(self) -> None: - for key in sorted(self._get_unknown_ini_keys()): - self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") - - def _validate_plugins(self) -> None: - required_plugins = sorted(self.getini("required_plugins")) - if not required_plugins: - return - - # Imported lazily to improve start-up time. - from packaging.requirements import InvalidRequirement - from packaging.requirements import Requirement - from packaging.version import Version - - plugin_info = self.pluginmanager.list_plugin_distinfo() - plugin_dist_info = {dist.project_name: dist.version for _, dist in plugin_info} - - missing_plugins = [] - for required_plugin in required_plugins: - try: - req = Requirement(required_plugin) - except InvalidRequirement: - missing_plugins.append(required_plugin) - continue - - if req.name not in plugin_dist_info: - missing_plugins.append(required_plugin) - elif not req.specifier.contains( - Version(plugin_dist_info[req.name]), prereleases=True - ): - missing_plugins.append(required_plugin) - - if missing_plugins: - raise UsageError( - "Missing required plugins: {}".format(", ".join(missing_plugins)), - ) - - def _warn_or_fail_if_strict(self, message: str) -> None: - if self.known_args_namespace.strict_config: - raise UsageError(message) - - self.issue_config_time_warning(PytestConfigWarning(message), stacklevel=3) - - def _get_unknown_ini_keys(self) -> list[str]: - parser_inicfg = self._parser._inidict - return [name for name in self.inicfg if name not in parser_inicfg] - - def parse(self, args: list[str], addopts: bool = True) -> None: - # Parse given cmdline arguments into this config object. - assert ( - self.args == [] - ), "can only parse cmdline args at most once per Config object" - self.hook.pytest_addhooks.call_historic( - kwargs=dict(pluginmanager=self.pluginmanager) - ) - self._preparse(args, addopts=addopts) - self._parser.after_preparse = True # type: ignore - try: - args = self._parser.parse_setoption( - args, self.option, namespace=self.option - ) - self.args, self.args_source = self._decide_args( - args=args, - pyargs=self.known_args_namespace.pyargs, - testpaths=self.getini("testpaths"), - invocation_dir=self.invocation_params.dir, - rootpath=self.rootpath, - warn=True, - ) - except PrintHelp: - pass - - def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None: - """Issue and handle a warning during the "configure" stage. - - During ``pytest_configure`` we can't capture warnings using the ``catch_warnings_for_item`` - function because it is not possible to have hook wrappers around ``pytest_configure``. - - This function is mainly intended for plugins that need to issue warnings during - ``pytest_configure`` (or similar stages). - - :param warning: The warning instance. - :param stacklevel: stacklevel forwarded to warnings.warn. - """ - if self.pluginmanager.is_blocked("warnings"): - return - - cmdline_filters = self.known_args_namespace.pythonwarnings or [] - config_filters = self.getini("filterwarnings") - - with warnings.catch_warnings(record=True) as records: - warnings.simplefilter("always", type(warning)) - apply_warning_filters(config_filters, cmdline_filters) - warnings.warn(warning, stacklevel=stacklevel) - - if records: - frame = sys._getframe(stacklevel - 1) - location = frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name - self.hook.pytest_warning_recorded.call_historic( - kwargs=dict( - warning_message=records[0], - when="config", - nodeid="", - location=location, - ) - ) - - def addinivalue_line(self, name: str, line: str) -> None: - """Add a line to an ini-file option. The option must have been - declared but might not yet be set in which case the line becomes - the first line in its value.""" - x = self.getini(name) - assert isinstance(x, list) - x.append(line) # modifies the cached list inline - - def getini(self, name: str): - """Return configuration value from an :ref:`ini file `. - - If a configuration value is not defined in an - :ref:`ini file `, then the ``default`` value provided while - registering the configuration through - :func:`parser.addini ` will be returned. - Please note that you can even provide ``None`` as a valid - default value. - - If ``default`` is not provided while registering using - :func:`parser.addini `, then a default value - based on the ``type`` parameter passed to - :func:`parser.addini ` will be returned. - The default values based on ``type`` are: - ``paths``, ``pathlist``, ``args`` and ``linelist`` : empty list ``[]`` - ``bool`` : ``False`` - ``string`` : empty string ``""`` - - If neither the ``default`` nor the ``type`` parameter is passed - while registering the configuration through - :func:`parser.addini `, then the configuration - is treated as a string and a default empty string '' is returned. - - If the specified name hasn't been registered through a prior - :func:`parser.addini ` call (usually from a - plugin), a ValueError is raised. - """ - try: - return self._inicache[name] - except KeyError: - self._inicache[name] = val = self._getini(name) - return val - - # Meant for easy monkeypatching by legacypath plugin. - # Can be inlined back (with no cover removed) once legacypath is gone. - def _getini_unknown_type(self, name: str, type: str, value: str | list[str]): - msg = f"unknown configuration type: {type}" - raise ValueError(msg, value) # pragma: no cover - - def _getini(self, name: str): - try: - description, type, default = self._parser._inidict[name] - except KeyError as e: - raise ValueError(f"unknown configuration value: {name!r}") from e - override_value = self._get_override_ini_value(name) - if override_value is None: - try: - value = self.inicfg[name] - except KeyError: - return default - else: - value = override_value - # Coerce the values based on types. - # - # Note: some coercions are only required if we are reading from .ini files, because - # the file format doesn't contain type information, but when reading from toml we will - # get either str or list of str values (see _parse_ini_config_from_pyproject_toml). - # For example: - # - # ini: - # a_line_list = "tests acceptance" - # in this case, we need to split the string to obtain a list of strings. - # - # toml: - # a_line_list = ["tests", "acceptance"] - # in this case, we already have a list ready to use. - # - if type == "paths": - dp = ( - self.inipath.parent - if self.inipath is not None - else self.invocation_params.dir - ) - input_values = shlex.split(value) if isinstance(value, str) else value - return [dp / x for x in input_values] - elif type == "args": - return shlex.split(value) if isinstance(value, str) else value - elif type == "linelist": - if isinstance(value, str): - return [t for t in map(lambda x: x.strip(), value.split("\n")) if t] - else: - return value - elif type == "bool": - return _strtobool(str(value).strip()) - elif type == "string": - return value - elif type is None: - return value - else: - return self._getini_unknown_type(name, type, value) - - def _getconftest_pathlist( - self, name: str, path: pathlib.Path - ) -> list[pathlib.Path] | None: - try: - mod, relroots = self.pluginmanager._rget_with_confmod(name, path) - except KeyError: - return None - assert mod.__file__ is not None - modpath = pathlib.Path(mod.__file__).parent - values: list[pathlib.Path] = [] - for relroot in relroots: - if isinstance(relroot, os.PathLike): - relroot = pathlib.Path(relroot) - else: - relroot = relroot.replace("/", os.sep) - relroot = absolutepath(modpath / relroot) - values.append(relroot) - return values - - def _get_override_ini_value(self, name: str) -> str | None: - value = None - # override_ini is a list of "ini=value" options. - # Always use the last item if multiple values are set for same ini-name, - # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2. - for ini_config in self._override_ini: - try: - key, user_ini_value = ini_config.split("=", 1) - except ValueError as e: - raise UsageError( - f"-o/--override-ini expects option=value style (got: {ini_config!r})." - ) from e - else: - if key == name: - value = user_ini_value - return value - - def getoption(self, name: str, default=notset, skip: bool = False): - """Return command line option value. - - :param name: Name of the option. You may also specify - the literal ``--OPT`` option instead of the "dest" option name. - :param default: Fallback value if no option of that name is **declared** via :hook:`pytest_addoption`. - Note this parameter will be ignored when the option is **declared** even if the option's value is ``None``. - :param skip: If ``True``, raise :func:`pytest.skip` if option is undeclared or has a ``None`` value. - Note that even if ``True``, if a default was specified it will be returned instead of a skip. - """ - name = self._opt2dest.get(name, name) - try: - val = getattr(self.option, name) - if val is None and skip: - raise AttributeError(name) - return val - except AttributeError as e: - if default is not notset: - return default - if skip: - import pytest - - pytest.skip(f"no {name!r} option found") - raise ValueError(f"no option named {name!r}") from e - - def getvalue(self, name: str, path=None): - """Deprecated, use getoption() instead.""" - return self.getoption(name) - - def getvalueorskip(self, name: str, path=None): - """Deprecated, use getoption(skip=True) instead.""" - return self.getoption(name, skip=True) - - #: Verbosity type for failed assertions (see :confval:`verbosity_assertions`). - VERBOSITY_ASSERTIONS: Final = "assertions" - #: Verbosity type for test case execution (see :confval:`verbosity_test_cases`). - VERBOSITY_TEST_CASES: Final = "test_cases" - _VERBOSITY_INI_DEFAULT: Final = "auto" - - def get_verbosity(self, verbosity_type: str | None = None) -> int: - r"""Retrieve the verbosity level for a fine-grained verbosity type. - - :param verbosity_type: Verbosity type to get level for. If a level is - configured for the given type, that value will be returned. If the - given type is not a known verbosity type, the global verbosity - level will be returned. If the given type is None (default), the - global verbosity level will be returned. - - To configure a level for a fine-grained verbosity type, the - configuration file should have a setting for the configuration name - and a numeric value for the verbosity level. A special value of "auto" - can be used to explicitly use the global verbosity level. - - Example: - - .. code-block:: ini - - # content of pytest.ini - [pytest] - verbosity_assertions = 2 - - .. code-block:: console - - pytest -v - - .. code-block:: python - - print(config.get_verbosity()) # 1 - print(config.get_verbosity(Config.VERBOSITY_ASSERTIONS)) # 2 - """ - global_level = self.getoption("verbose", default=0) - assert isinstance(global_level, int) - if verbosity_type is None: - return global_level - - ini_name = Config._verbosity_ini_name(verbosity_type) - if ini_name not in self._parser._inidict: - return global_level - - level = self.getini(ini_name) - if level == Config._VERBOSITY_INI_DEFAULT: - return global_level - - return int(level) - - @staticmethod - def _verbosity_ini_name(verbosity_type: str) -> str: - return f"verbosity_{verbosity_type}" - - @staticmethod - def _add_verbosity_ini(parser: Parser, verbosity_type: str, help: str) -> None: - """Add a output verbosity configuration option for the given output type. - - :param parser: Parser for command line arguments and ini-file values. - :param verbosity_type: Fine-grained verbosity category. - :param help: Description of the output this type controls. - - The value should be retrieved via a call to - :py:func:`config.get_verbosity(type) `. - """ - parser.addini( - Config._verbosity_ini_name(verbosity_type), - help=help, - type="string", - default=Config._VERBOSITY_INI_DEFAULT, - ) - - def _warn_about_missing_assertion(self, mode: str) -> None: - if not _assertion_supported(): - if mode == "plain": - warning_text = ( - "ASSERTIONS ARE NOT EXECUTED" - " and FAILING TESTS WILL PASS. Are you" - " using python -O?" - ) - else: - warning_text = ( - "assertions not in test modules or" - " plugins will be ignored" - " because assert statements are not executed " - "by the underlying Python interpreter " - "(are you using python -O?)\n" - ) - self.issue_config_time_warning( - PytestConfigWarning(warning_text), - stacklevel=3, - ) - - def _warn_about_skipped_plugins(self) -> None: - for module_name, msg in self.pluginmanager.skipped_plugins: - self.issue_config_time_warning( - PytestConfigWarning(f"skipped plugin {module_name!r}: {msg}"), - stacklevel=2, - ) - - -def _assertion_supported() -> bool: - try: - assert False - except AssertionError: - return True - else: - return False # type: ignore[unreachable] - - -def create_terminal_writer( - config: Config, file: TextIO | None = None -) -> TerminalWriter: - """Create a TerminalWriter instance configured according to the options - in the config object. - - Every code which requires a TerminalWriter object and has access to a - config object should use this function. - """ - tw = TerminalWriter(file=file) - - if config.option.color == "yes": - tw.hasmarkup = True - elif config.option.color == "no": - tw.hasmarkup = False - - if config.option.code_highlight == "yes": - tw.code_highlight = True - elif config.option.code_highlight == "no": - tw.code_highlight = False - - return tw - - -def _strtobool(val: str) -> bool: - """Convert a string representation of truth to True or False. - - True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values - are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if - 'val' is anything else. - - .. note:: Copied from distutils.util. - """ - val = val.lower() - if val in ("y", "yes", "t", "true", "on", "1"): - return True - elif val in ("n", "no", "f", "false", "off", "0"): - return False - else: - raise ValueError(f"invalid truth value {val!r}") - - -@lru_cache(maxsize=50) -def parse_warning_filter( - arg: str, *, escape: bool -) -> tuple[warnings._ActionKind, str, type[Warning], str, int]: - """Parse a warnings filter string. - - This is copied from warnings._setoption with the following changes: - - * Does not apply the filter. - * Escaping is optional. - * Raises UsageError so we get nice error messages on failure. - """ - __tracebackhide__ = True - error_template = dedent( - f"""\ - while parsing the following warning configuration: - - {arg} - - This error occurred: - - {{error}} - """ - ) - - parts = arg.split(":") - if len(parts) > 5: - doc_url = ( - "https://docs.python.org/3/library/warnings.html#describing-warning-filters" - ) - error = dedent( - f"""\ - Too many fields ({len(parts)}), expected at most 5 separated by colons: - - action:message:category:module:line - - For more information please consult: {doc_url} - """ - ) - raise UsageError(error_template.format(error=error)) - - while len(parts) < 5: - parts.append("") - action_, message, category_, module, lineno_ = (s.strip() for s in parts) - try: - action: warnings._ActionKind = warnings._getaction(action_) # type: ignore[attr-defined] - except warnings._OptionError as e: - raise UsageError(error_template.format(error=str(e))) from None - try: - category: type[Warning] = _resolve_warning_category(category_) - except Exception: - exc_info = ExceptionInfo.from_current() - exception_text = exc_info.getrepr(style="native") - raise UsageError(error_template.format(error=exception_text)) from None - if message and escape: - message = re.escape(message) - if module and escape: - module = re.escape(module) + r"\Z" - if lineno_: - try: - lineno = int(lineno_) - if lineno < 0: - raise ValueError("number is negative") - except ValueError as e: - raise UsageError( - error_template.format(error=f"invalid lineno {lineno_!r}: {e}") - ) from None - else: - lineno = 0 - return action, message, category, module, lineno - - -def _resolve_warning_category(category: str) -> type[Warning]: - """ - Copied from warnings._getcategory, but changed so it lets exceptions (specially ImportErrors) - propagate so we can get access to their tracebacks (#9218). - """ - __tracebackhide__ = True - if not category: - return Warning - - if "." not in category: - import builtins as m - - klass = category - else: - module, _, klass = category.rpartition(".") - m = __import__(module, None, None, [klass]) - cat = getattr(m, klass) - if not issubclass(cat, Warning): - raise UsageError(f"{cat} is not a Warning subclass") - return cast(Type[Warning], cat) - - -def apply_warning_filters( - config_filters: Iterable[str], cmdline_filters: Iterable[str] -) -> None: - """Applies pytest-configured filters to the warnings module""" - # Filters should have this precedence: cmdline options, config. - # Filters should be applied in the inverse order of precedence. - for arg in config_filters: - warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) - - for arg in cmdline_filters: - warnings.filterwarnings(*parse_warning_filter(arg, escape=True)) diff --git a/.venv/lib/python3.12/site-packages/_pytest/config/argparsing.py b/.venv/lib/python3.12/site-packages/_pytest/config/argparsing.py deleted file mode 100644 index 85aa4632..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/config/argparsing.py +++ /dev/null @@ -1,551 +0,0 @@ -# mypy: allow-untyped-defs -from __future__ import annotations - -import argparse -from gettext import gettext -import os -import sys -from typing import Any -from typing import Callable -from typing import cast -from typing import final -from typing import List -from typing import Literal -from typing import Mapping -from typing import NoReturn -from typing import Sequence - -import _pytest._io -from _pytest.config.exceptions import UsageError -from _pytest.deprecated import check_ispytest - - -FILE_OR_DIR = "file_or_dir" - - -class NotSet: - def __repr__(self) -> str: - return "" - - -NOT_SET = NotSet() - - -@final -class Parser: - """Parser for command line arguments and ini-file values. - - :ivar extra_info: Dict of generic param -> value to display in case - there's an error processing the command line arguments. - """ - - prog: str | None = None - - def __init__( - self, - usage: str | None = None, - processopt: Callable[[Argument], None] | None = None, - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - self._anonymous = OptionGroup("Custom options", parser=self, _ispytest=True) - self._groups: list[OptionGroup] = [] - self._processopt = processopt - self._usage = usage - self._inidict: dict[str, tuple[str, str | None, Any]] = {} - self._ininames: list[str] = [] - self.extra_info: dict[str, Any] = {} - - def processoption(self, option: Argument) -> None: - if self._processopt: - if option.dest: - self._processopt(option) - - def getgroup( - self, name: str, description: str = "", after: str | None = None - ) -> OptionGroup: - """Get (or create) a named option Group. - - :param name: Name of the option group. - :param description: Long description for --help output. - :param after: Name of another group, used for ordering --help output. - :returns: The option group. - - The returned group object has an ``addoption`` method with the same - signature as :func:`parser.addoption ` but - will be shown in the respective group in the output of - ``pytest --help``. - """ - for group in self._groups: - if group.name == name: - return group - group = OptionGroup(name, description, parser=self, _ispytest=True) - i = 0 - for i, grp in enumerate(self._groups): - if grp.name == after: - break - self._groups.insert(i + 1, group) - return group - - def addoption(self, *opts: str, **attrs: Any) -> None: - """Register a command line option. - - :param opts: - Option names, can be short or long options. - :param attrs: - Same attributes as the argparse library's :meth:`add_argument() - ` function accepts. - - After command line parsing, options are available on the pytest config - object via ``config.option.NAME`` where ``NAME`` is usually set - by passing a ``dest`` attribute, for example - ``addoption("--long", dest="NAME", ...)``. - """ - self._anonymous.addoption(*opts, **attrs) - - def parse( - self, - args: Sequence[str | os.PathLike[str]], - namespace: argparse.Namespace | None = None, - ) -> argparse.Namespace: - from _pytest._argcomplete import try_argcomplete - - self.optparser = self._getparser() - try_argcomplete(self.optparser) - strargs = [os.fspath(x) for x in args] - return self.optparser.parse_args(strargs, namespace=namespace) - - def _getparser(self) -> MyOptionParser: - from _pytest._argcomplete import filescompleter - - optparser = MyOptionParser(self, self.extra_info, prog=self.prog) - groups = [*self._groups, self._anonymous] - for group in groups: - if group.options: - desc = group.description or group.name - arggroup = optparser.add_argument_group(desc) - for option in group.options: - n = option.names() - a = option.attrs() - arggroup.add_argument(*n, **a) - file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*") - # bash like autocompletion for dirs (appending '/') - # Type ignored because typeshed doesn't know about argcomplete. - file_or_dir_arg.completer = filescompleter # type: ignore - return optparser - - def parse_setoption( - self, - args: Sequence[str | os.PathLike[str]], - option: argparse.Namespace, - namespace: argparse.Namespace | None = None, - ) -> list[str]: - parsedoption = self.parse(args, namespace=namespace) - for name, value in parsedoption.__dict__.items(): - setattr(option, name, value) - return cast(List[str], getattr(parsedoption, FILE_OR_DIR)) - - def parse_known_args( - self, - args: Sequence[str | os.PathLike[str]], - namespace: argparse.Namespace | None = None, - ) -> argparse.Namespace: - """Parse the known arguments at this point. - - :returns: An argparse namespace object. - """ - return self.parse_known_and_unknown_args(args, namespace=namespace)[0] - - def parse_known_and_unknown_args( - self, - args: Sequence[str | os.PathLike[str]], - namespace: argparse.Namespace | None = None, - ) -> tuple[argparse.Namespace, list[str]]: - """Parse the known arguments at this point, and also return the - remaining unknown arguments. - - :returns: - A tuple containing an argparse namespace object for the known - arguments, and a list of the unknown arguments. - """ - optparser = self._getparser() - strargs = [os.fspath(x) for x in args] - return optparser.parse_known_args(strargs, namespace=namespace) - - def addini( - self, - name: str, - help: str, - type: Literal["string", "paths", "pathlist", "args", "linelist", "bool"] - | None = None, - default: Any = NOT_SET, - ) -> None: - """Register an ini-file option. - - :param name: - Name of the ini-variable. - :param type: - Type of the variable. Can be: - - * ``string``: a string - * ``bool``: a boolean - * ``args``: a list of strings, separated as in a shell - * ``linelist``: a list of strings, separated by line breaks - * ``paths``: a list of :class:`pathlib.Path`, separated as in a shell - * ``pathlist``: a list of ``py.path``, separated as in a shell - - For ``paths`` and ``pathlist`` types, they are considered relative to the ini-file. - In case the execution is happening without an ini-file defined, - they will be considered relative to the current working directory (for example with ``--override-ini``). - - .. versionadded:: 7.0 - The ``paths`` variable type. - - .. versionadded:: 8.1 - Use the current working directory to resolve ``paths`` and ``pathlist`` in the absence of an ini-file. - - Defaults to ``string`` if ``None`` or not passed. - :param default: - Default value if no ini-file option exists but is queried. - - The value of ini-variables can be retrieved via a call to - :py:func:`config.getini(name) `. - """ - assert type in (None, "string", "paths", "pathlist", "args", "linelist", "bool") - if default is NOT_SET: - default = get_ini_default_for_type(type) - - self._inidict[name] = (help, type, default) - self._ininames.append(name) - - -def get_ini_default_for_type( - type: Literal["string", "paths", "pathlist", "args", "linelist", "bool"] | None, -) -> Any: - """ - Used by addini to get the default value for a given ini-option type, when - default is not supplied. - """ - if type is None: - return "" - elif type in ("paths", "pathlist", "args", "linelist"): - return [] - elif type == "bool": - return False - else: - return "" - - -class ArgumentError(Exception): - """Raised if an Argument instance is created with invalid or - inconsistent arguments.""" - - def __init__(self, msg: str, option: Argument | str) -> None: - self.msg = msg - self.option_id = str(option) - - def __str__(self) -> str: - if self.option_id: - return f"option {self.option_id}: {self.msg}" - else: - return self.msg - - -class Argument: - """Class that mimics the necessary behaviour of optparse.Option. - - It's currently a least effort implementation and ignoring choices - and integer prefixes. - - https://docs.python.org/3/library/optparse.html#optparse-standard-option-types - """ - - def __init__(self, *names: str, **attrs: Any) -> None: - """Store params in private vars for use in add_argument.""" - self._attrs = attrs - self._short_opts: list[str] = [] - self._long_opts: list[str] = [] - try: - self.type = attrs["type"] - except KeyError: - pass - try: - # Attribute existence is tested in Config._processopt. - self.default = attrs["default"] - except KeyError: - pass - self._set_opt_strings(names) - dest: str | None = attrs.get("dest") - if dest: - self.dest = dest - elif self._long_opts: - self.dest = self._long_opts[0][2:].replace("-", "_") - else: - try: - self.dest = self._short_opts[0][1:] - except IndexError as e: - self.dest = "???" # Needed for the error repr. - raise ArgumentError("need a long or short option", self) from e - - def names(self) -> list[str]: - return self._short_opts + self._long_opts - - def attrs(self) -> Mapping[str, Any]: - # Update any attributes set by processopt. - attrs = "default dest help".split() - attrs.append(self.dest) - for attr in attrs: - try: - self._attrs[attr] = getattr(self, attr) - except AttributeError: - pass - return self._attrs - - def _set_opt_strings(self, opts: Sequence[str]) -> None: - """Directly from optparse. - - Might not be necessary as this is passed to argparse later on. - """ - for opt in opts: - if len(opt) < 2: - raise ArgumentError( - f"invalid option string {opt!r}: " - "must be at least two characters long", - self, - ) - elif len(opt) == 2: - if not (opt[0] == "-" and opt[1] != "-"): - raise ArgumentError( - f"invalid short option string {opt!r}: " - "must be of the form -x, (x any non-dash char)", - self, - ) - self._short_opts.append(opt) - else: - if not (opt[0:2] == "--" and opt[2] != "-"): - raise ArgumentError( - f"invalid long option string {opt!r}: " - "must start with --, followed by non-dash", - self, - ) - self._long_opts.append(opt) - - def __repr__(self) -> str: - args: list[str] = [] - if self._short_opts: - args += ["_short_opts: " + repr(self._short_opts)] - if self._long_opts: - args += ["_long_opts: " + repr(self._long_opts)] - args += ["dest: " + repr(self.dest)] - if hasattr(self, "type"): - args += ["type: " + repr(self.type)] - if hasattr(self, "default"): - args += ["default: " + repr(self.default)] - return "Argument({})".format(", ".join(args)) - - -class OptionGroup: - """A group of options shown in its own section.""" - - def __init__( - self, - name: str, - description: str = "", - parser: Parser | None = None, - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - self.name = name - self.description = description - self.options: list[Argument] = [] - self.parser = parser - - def addoption(self, *opts: str, **attrs: Any) -> None: - """Add an option to this group. - - If a shortened version of a long option is specified, it will - be suppressed in the help. ``addoption('--twowords', '--two-words')`` - results in help showing ``--two-words`` only, but ``--twowords`` gets - accepted **and** the automatic destination is in ``args.twowords``. - - :param opts: - Option names, can be short or long options. - :param attrs: - Same attributes as the argparse library's :meth:`add_argument() - ` function accepts. - """ - conflict = set(opts).intersection( - name for opt in self.options for name in opt.names() - ) - if conflict: - raise ValueError(f"option names {conflict} already added") - option = Argument(*opts, **attrs) - self._addoption_instance(option, shortupper=False) - - def _addoption(self, *opts: str, **attrs: Any) -> None: - option = Argument(*opts, **attrs) - self._addoption_instance(option, shortupper=True) - - def _addoption_instance(self, option: Argument, shortupper: bool = False) -> None: - if not shortupper: - for opt in option._short_opts: - if opt[0] == "-" and opt[1].islower(): - raise ValueError("lowercase shortoptions reserved") - if self.parser: - self.parser.processoption(option) - self.options.append(option) - - -class MyOptionParser(argparse.ArgumentParser): - def __init__( - self, - parser: Parser, - extra_info: dict[str, Any] | None = None, - prog: str | None = None, - ) -> None: - self._parser = parser - super().__init__( - prog=prog, - usage=parser._usage, - add_help=False, - formatter_class=DropShorterLongHelpFormatter, - allow_abbrev=False, - fromfile_prefix_chars="@", - ) - # extra_info is a dict of (param -> value) to display if there's - # an usage error to provide more contextual information to the user. - self.extra_info = extra_info if extra_info else {} - - def error(self, message: str) -> NoReturn: - """Transform argparse error message into UsageError.""" - msg = f"{self.prog}: error: {message}" - - if hasattr(self._parser, "_config_source_hint"): - msg = f"{msg} ({self._parser._config_source_hint})" - - raise UsageError(self.format_usage() + msg) - - # Type ignored because typeshed has a very complex type in the superclass. - def parse_args( # type: ignore - self, - args: Sequence[str] | None = None, - namespace: argparse.Namespace | None = None, - ) -> argparse.Namespace: - """Allow splitting of positional arguments.""" - parsed, unrecognized = self.parse_known_args(args, namespace) - if unrecognized: - for arg in unrecognized: - if arg and arg[0] == "-": - lines = [ - "unrecognized arguments: {}".format(" ".join(unrecognized)) - ] - for k, v in sorted(self.extra_info.items()): - lines.append(f" {k}: {v}") - self.error("\n".join(lines)) - getattr(parsed, FILE_OR_DIR).extend(unrecognized) - return parsed - - if sys.version_info < (3, 9): # pragma: no cover - # Backport of https://github.com/python/cpython/pull/14316 so we can - # disable long --argument abbreviations without breaking short flags. - def _parse_optional( - self, arg_string: str - ) -> tuple[argparse.Action | None, str, str | None] | None: - if not arg_string: - return None - if arg_string[0] not in self.prefix_chars: - return None - if arg_string in self._option_string_actions: - action = self._option_string_actions[arg_string] - return action, arg_string, None - if len(arg_string) == 1: - return None - if "=" in arg_string: - option_string, explicit_arg = arg_string.split("=", 1) - if option_string in self._option_string_actions: - action = self._option_string_actions[option_string] - return action, option_string, explicit_arg - if self.allow_abbrev or not arg_string.startswith("--"): - option_tuples = self._get_option_tuples(arg_string) - if len(option_tuples) > 1: - msg = gettext( - "ambiguous option: %(option)s could match %(matches)s" - ) - options = ", ".join(option for _, option, _ in option_tuples) - self.error(msg % {"option": arg_string, "matches": options}) - elif len(option_tuples) == 1: - (option_tuple,) = option_tuples - return option_tuple - if self._negative_number_matcher.match(arg_string): - if not self._has_negative_number_optionals: - return None - if " " in arg_string: - return None - return None, arg_string, None - - -class DropShorterLongHelpFormatter(argparse.HelpFormatter): - """Shorten help for long options that differ only in extra hyphens. - - - Collapse **long** options that are the same except for extra hyphens. - - Shortcut if there are only two options and one of them is a short one. - - Cache result on the action object as this is called at least 2 times. - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - # Use more accurate terminal width. - if "width" not in kwargs: - kwargs["width"] = _pytest._io.get_terminal_width() - super().__init__(*args, **kwargs) - - def _format_action_invocation(self, action: argparse.Action) -> str: - orgstr = super()._format_action_invocation(action) - if orgstr and orgstr[0] != "-": # only optional arguments - return orgstr - res: str | None = getattr(action, "_formatted_action_invocation", None) - if res: - return res - options = orgstr.split(", ") - if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2): - # a shortcut for '-h, --help' or '--abc', '-a' - action._formatted_action_invocation = orgstr # type: ignore - return orgstr - return_list = [] - short_long: dict[str, str] = {} - for option in options: - if len(option) == 2 or option[2] == " ": - continue - if not option.startswith("--"): - raise ArgumentError( - f'long optional argument without "--": [{option}]', option - ) - xxoption = option[2:] - shortened = xxoption.replace("-", "") - if shortened not in short_long or len(short_long[shortened]) < len( - xxoption - ): - short_long[shortened] = xxoption - # now short_long has been filled out to the longest with dashes - # **and** we keep the right option ordering from add_argument - for option in options: - if len(option) == 2 or option[2] == " ": - return_list.append(option) - if option[2:] == short_long.get(option.replace("-", "")): - return_list.append(option.replace(" ", "=", 1)) - formatted_action_invocation = ", ".join(return_list) - action._formatted_action_invocation = formatted_action_invocation # type: ignore - return formatted_action_invocation - - def _split_lines(self, text, width): - """Wrap lines after splitting on original newlines. - - This allows to have explicit line breaks in the help text. - """ - import textwrap - - lines = [] - for line in text.splitlines(): - lines.extend(textwrap.wrap(line.strip(), width)) - return lines diff --git a/.venv/lib/python3.12/site-packages/_pytest/config/compat.py b/.venv/lib/python3.12/site-packages/_pytest/config/compat.py deleted file mode 100644 index 2856d85d..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/config/compat.py +++ /dev/null @@ -1,85 +0,0 @@ -from __future__ import annotations - -import functools -from pathlib import Path -from typing import Any -from typing import Mapping -import warnings - -import pluggy - -from ..compat import LEGACY_PATH -from ..compat import legacy_path -from ..deprecated import HOOK_LEGACY_PATH_ARG - - -# hookname: (Path, LEGACY_PATH) -imply_paths_hooks: Mapping[str, tuple[str, str]] = { - "pytest_ignore_collect": ("collection_path", "path"), - "pytest_collect_file": ("file_path", "path"), - "pytest_pycollect_makemodule": ("module_path", "path"), - "pytest_report_header": ("start_path", "startdir"), - "pytest_report_collectionfinish": ("start_path", "startdir"), -} - - -def _check_path(path: Path, fspath: LEGACY_PATH) -> None: - if Path(fspath) != path: - raise ValueError( - f"Path({fspath!r}) != {path!r}\n" - "if both path and fspath are given they need to be equal" - ) - - -class PathAwareHookProxy: - """ - this helper wraps around hook callers - until pluggy supports fixingcalls, this one will do - - it currently doesn't return full hook caller proxies for fixed hooks, - this may have to be changed later depending on bugs - """ - - def __init__(self, hook_relay: pluggy.HookRelay) -> None: - self._hook_relay = hook_relay - - def __dir__(self) -> list[str]: - return dir(self._hook_relay) - - def __getattr__(self, key: str) -> pluggy.HookCaller: - hook: pluggy.HookCaller = getattr(self._hook_relay, key) - if key not in imply_paths_hooks: - self.__dict__[key] = hook - return hook - else: - path_var, fspath_var = imply_paths_hooks[key] - - @functools.wraps(hook) - def fixed_hook(**kw: Any) -> Any: - path_value: Path | None = kw.pop(path_var, None) - fspath_value: LEGACY_PATH | None = kw.pop(fspath_var, None) - if fspath_value is not None: - warnings.warn( - HOOK_LEGACY_PATH_ARG.format( - pylib_path_arg=fspath_var, pathlib_path_arg=path_var - ), - stacklevel=2, - ) - if path_value is not None: - if fspath_value is not None: - _check_path(path_value, fspath_value) - else: - fspath_value = legacy_path(path_value) - else: - assert fspath_value is not None - path_value = Path(fspath_value) - - kw[path_var] = path_value - kw[fspath_var] = fspath_value - return hook(**kw) - - fixed_hook.name = hook.name # type: ignore[attr-defined] - fixed_hook.spec = hook.spec # type: ignore[attr-defined] - fixed_hook.__name__ = key - self.__dict__[key] = fixed_hook - return fixed_hook # type: ignore[return-value] diff --git a/.venv/lib/python3.12/site-packages/_pytest/config/exceptions.py b/.venv/lib/python3.12/site-packages/_pytest/config/exceptions.py deleted file mode 100644 index 90108eca..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/config/exceptions.py +++ /dev/null @@ -1,13 +0,0 @@ -from __future__ import annotations - -from typing import final - - -@final -class UsageError(Exception): - """Error in pytest usage or invocation.""" - - -class PrintHelp(Exception): - """Raised when pytest should print its help to skip the rest of the - argument parsing and validation.""" diff --git a/.venv/lib/python3.12/site-packages/_pytest/config/findpaths.py b/.venv/lib/python3.12/site-packages/_pytest/config/findpaths.py deleted file mode 100644 index ce4c990b..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/config/findpaths.py +++ /dev/null @@ -1,228 +0,0 @@ -from __future__ import annotations - -import os -from pathlib import Path -import sys -from typing import Iterable -from typing import Sequence - -import iniconfig - -from .exceptions import UsageError -from _pytest.outcomes import fail -from _pytest.pathlib import absolutepath -from _pytest.pathlib import commonpath -from _pytest.pathlib import safe_exists - - -def _parse_ini_config(path: Path) -> iniconfig.IniConfig: - """Parse the given generic '.ini' file using legacy IniConfig parser, returning - the parsed object. - - Raise UsageError if the file cannot be parsed. - """ - try: - return iniconfig.IniConfig(str(path)) - except iniconfig.ParseError as exc: - raise UsageError(str(exc)) from exc - - -def load_config_dict_from_file( - filepath: Path, -) -> dict[str, str | list[str]] | None: - """Load pytest configuration from the given file path, if supported. - - Return None if the file does not contain valid pytest configuration. - """ - # Configuration from ini files are obtained from the [pytest] section, if present. - if filepath.suffix == ".ini": - iniconfig = _parse_ini_config(filepath) - - if "pytest" in iniconfig: - return dict(iniconfig["pytest"].items()) - else: - # "pytest.ini" files are always the source of configuration, even if empty. - if filepath.name == "pytest.ini": - return {} - - # '.cfg' files are considered if they contain a "[tool:pytest]" section. - elif filepath.suffix == ".cfg": - iniconfig = _parse_ini_config(filepath) - - if "tool:pytest" in iniconfig.sections: - return dict(iniconfig["tool:pytest"].items()) - elif "pytest" in iniconfig.sections: - # If a setup.cfg contains a "[pytest]" section, we raise a failure to indicate users that - # plain "[pytest]" sections in setup.cfg files is no longer supported (#3086). - fail(CFG_PYTEST_SECTION.format(filename="setup.cfg"), pytrace=False) - - # '.toml' files are considered if they contain a [tool.pytest.ini_options] table. - elif filepath.suffix == ".toml": - if sys.version_info >= (3, 11): - import tomllib - else: - import tomli as tomllib - - toml_text = filepath.read_text(encoding="utf-8") - try: - config = tomllib.loads(toml_text) - except tomllib.TOMLDecodeError as exc: - raise UsageError(f"{filepath}: {exc}") from exc - - result = config.get("tool", {}).get("pytest", {}).get("ini_options", None) - if result is not None: - # TOML supports richer data types than ini files (strings, arrays, floats, ints, etc), - # however we need to convert all scalar values to str for compatibility with the rest - # of the configuration system, which expects strings only. - def make_scalar(v: object) -> str | list[str]: - return v if isinstance(v, list) else str(v) - - return {k: make_scalar(v) for k, v in result.items()} - - return None - - -def locate_config( - invocation_dir: Path, - args: Iterable[Path], -) -> tuple[Path | None, Path | None, dict[str, str | list[str]]]: - """Search in the list of arguments for a valid ini-file for pytest, - and return a tuple of (rootdir, inifile, cfg-dict).""" - config_names = [ - "pytest.ini", - ".pytest.ini", - "pyproject.toml", - "tox.ini", - "setup.cfg", - ] - args = [x for x in args if not str(x).startswith("-")] - if not args: - args = [invocation_dir] - found_pyproject_toml: Path | None = None - for arg in args: - argpath = absolutepath(arg) - for base in (argpath, *argpath.parents): - for config_name in config_names: - p = base / config_name - if p.is_file(): - if p.name == "pyproject.toml" and found_pyproject_toml is None: - found_pyproject_toml = p - ini_config = load_config_dict_from_file(p) - if ini_config is not None: - return base, p, ini_config - if found_pyproject_toml is not None: - return found_pyproject_toml.parent, found_pyproject_toml, {} - return None, None, {} - - -def get_common_ancestor( - invocation_dir: Path, - paths: Iterable[Path], -) -> Path: - common_ancestor: Path | None = None - for path in paths: - if not path.exists(): - continue - if common_ancestor is None: - common_ancestor = path - else: - if common_ancestor in path.parents or path == common_ancestor: - continue - elif path in common_ancestor.parents: - common_ancestor = path - else: - shared = commonpath(path, common_ancestor) - if shared is not None: - common_ancestor = shared - if common_ancestor is None: - common_ancestor = invocation_dir - elif common_ancestor.is_file(): - common_ancestor = common_ancestor.parent - return common_ancestor - - -def get_dirs_from_args(args: Iterable[str]) -> list[Path]: - def is_option(x: str) -> bool: - return x.startswith("-") - - def get_file_part_from_node_id(x: str) -> str: - return x.split("::")[0] - - def get_dir_from_path(path: Path) -> Path: - if path.is_dir(): - return path - return path.parent - - # These look like paths but may not exist - possible_paths = ( - absolutepath(get_file_part_from_node_id(arg)) - for arg in args - if not is_option(arg) - ) - - return [get_dir_from_path(path) for path in possible_paths if safe_exists(path)] - - -CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead." - - -def determine_setup( - *, - inifile: str | None, - args: Sequence[str], - rootdir_cmd_arg: str | None, - invocation_dir: Path, -) -> tuple[Path, Path | None, dict[str, str | list[str]]]: - """Determine the rootdir, inifile and ini configuration values from the - command line arguments. - - :param inifile: - The `--inifile` command line argument, if given. - :param args: - The free command line arguments. - :param rootdir_cmd_arg: - The `--rootdir` command line argument, if given. - :param invocation_dir: - The working directory when pytest was invoked. - """ - rootdir = None - dirs = get_dirs_from_args(args) - if inifile: - inipath_ = absolutepath(inifile) - inipath: Path | None = inipath_ - inicfg = load_config_dict_from_file(inipath_) or {} - if rootdir_cmd_arg is None: - rootdir = inipath_.parent - else: - ancestor = get_common_ancestor(invocation_dir, dirs) - rootdir, inipath, inicfg = locate_config(invocation_dir, [ancestor]) - if rootdir is None and rootdir_cmd_arg is None: - for possible_rootdir in (ancestor, *ancestor.parents): - if (possible_rootdir / "setup.py").is_file(): - rootdir = possible_rootdir - break - else: - if dirs != [ancestor]: - rootdir, inipath, inicfg = locate_config(invocation_dir, dirs) - if rootdir is None: - rootdir = get_common_ancestor( - invocation_dir, [invocation_dir, ancestor] - ) - if is_fs_root(rootdir): - rootdir = ancestor - if rootdir_cmd_arg: - rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg)) - if not rootdir.is_dir(): - raise UsageError( - f"Directory '{rootdir}' not found. Check your '--rootdir' option." - ) - assert rootdir is not None - return rootdir, inipath, inicfg or {} - - -def is_fs_root(p: Path) -> bool: - r""" - Return True if the given path is pointing to the root of the - file system ("/" on Unix and "C:\\" on Windows for example). - """ - return os.path.splitdrive(str(p))[1] == os.sep diff --git a/.venv/lib/python3.12/site-packages/_pytest/debugging.py b/.venv/lib/python3.12/site-packages/_pytest/debugging.py deleted file mode 100644 index 2dfe321e..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/debugging.py +++ /dev/null @@ -1,385 +0,0 @@ -# mypy: allow-untyped-defs -# ruff: noqa: T100 -"""Interactive debugging with PDB, the Python Debugger.""" - -from __future__ import annotations - -import argparse -import functools -import sys -import types -from typing import Any -from typing import Callable -from typing import Generator -import unittest - -from _pytest import outcomes -from _pytest._code import ExceptionInfo -from _pytest.capture import CaptureManager -from _pytest.config import Config -from _pytest.config import ConftestImportFailure -from _pytest.config import hookimpl -from _pytest.config import PytestPluginManager -from _pytest.config.argparsing import Parser -from _pytest.config.exceptions import UsageError -from _pytest.nodes import Node -from _pytest.reports import BaseReport -from _pytest.runner import CallInfo - - -def _validate_usepdb_cls(value: str) -> tuple[str, str]: - """Validate syntax of --pdbcls option.""" - try: - modname, classname = value.split(":") - except ValueError as e: - raise argparse.ArgumentTypeError( - f"{value!r} is not in the format 'modname:classname'" - ) from e - return (modname, classname) - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") - group._addoption( - "--pdb", - dest="usepdb", - action="store_true", - help="Start the interactive Python debugger on errors or KeyboardInterrupt", - ) - group._addoption( - "--pdbcls", - dest="usepdb_cls", - metavar="modulename:classname", - type=_validate_usepdb_cls, - help="Specify a custom interactive Python debugger for use with --pdb." - "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb", - ) - group._addoption( - "--trace", - dest="trace", - action="store_true", - help="Immediately break when running each test", - ) - - -def pytest_configure(config: Config) -> None: - import pdb - - if config.getvalue("trace"): - config.pluginmanager.register(PdbTrace(), "pdbtrace") - if config.getvalue("usepdb"): - config.pluginmanager.register(PdbInvoke(), "pdbinvoke") - - pytestPDB._saved.append( - (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config) - ) - pdb.set_trace = pytestPDB.set_trace - pytestPDB._pluginmanager = config.pluginmanager - pytestPDB._config = config - - # NOTE: not using pytest_unconfigure, since it might get called although - # pytest_configure was not (if another plugin raises UsageError). - def fin() -> None: - ( - pdb.set_trace, - pytestPDB._pluginmanager, - pytestPDB._config, - ) = pytestPDB._saved.pop() - - config.add_cleanup(fin) - - -class pytestPDB: - """Pseudo PDB that defers to the real pdb.""" - - _pluginmanager: PytestPluginManager | None = None - _config: Config | None = None - _saved: list[ - tuple[Callable[..., None], PytestPluginManager | None, Config | None] - ] = [] - _recursive_debug = 0 - _wrapped_pdb_cls: tuple[type[Any], type[Any]] | None = None - - @classmethod - def _is_capturing(cls, capman: CaptureManager | None) -> str | bool: - if capman: - return capman.is_capturing() - return False - - @classmethod - def _import_pdb_cls(cls, capman: CaptureManager | None): - if not cls._config: - import pdb - - # Happens when using pytest.set_trace outside of a test. - return pdb.Pdb - - usepdb_cls = cls._config.getvalue("usepdb_cls") - - if cls._wrapped_pdb_cls and cls._wrapped_pdb_cls[0] == usepdb_cls: - return cls._wrapped_pdb_cls[1] - - if usepdb_cls: - modname, classname = usepdb_cls - - try: - __import__(modname) - mod = sys.modules[modname] - - # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp). - parts = classname.split(".") - pdb_cls = getattr(mod, parts[0]) - for part in parts[1:]: - pdb_cls = getattr(pdb_cls, part) - except Exception as exc: - value = ":".join((modname, classname)) - raise UsageError( - f"--pdbcls: could not import {value!r}: {exc}" - ) from exc - else: - import pdb - - pdb_cls = pdb.Pdb - - wrapped_cls = cls._get_pdb_wrapper_class(pdb_cls, capman) - cls._wrapped_pdb_cls = (usepdb_cls, wrapped_cls) - return wrapped_cls - - @classmethod - def _get_pdb_wrapper_class(cls, pdb_cls, capman: CaptureManager | None): - import _pytest.config - - class PytestPdbWrapper(pdb_cls): - _pytest_capman = capman - _continued = False - - def do_debug(self, arg): - cls._recursive_debug += 1 - ret = super().do_debug(arg) - cls._recursive_debug -= 1 - return ret - - def do_continue(self, arg): - ret = super().do_continue(arg) - if cls._recursive_debug == 0: - assert cls._config is not None - tw = _pytest.config.create_terminal_writer(cls._config) - tw.line() - - capman = self._pytest_capman - capturing = pytestPDB._is_capturing(capman) - if capturing: - if capturing == "global": - tw.sep(">", "PDB continue (IO-capturing resumed)") - else: - tw.sep( - ">", - f"PDB continue (IO-capturing resumed for {capturing})", - ) - assert capman is not None - capman.resume() - else: - tw.sep(">", "PDB continue") - assert cls._pluginmanager is not None - cls._pluginmanager.hook.pytest_leave_pdb(config=cls._config, pdb=self) - self._continued = True - return ret - - do_c = do_cont = do_continue - - def do_quit(self, arg): - """Raise Exit outcome when quit command is used in pdb. - - This is a bit of a hack - it would be better if BdbQuit - could be handled, but this would require to wrap the - whole pytest run, and adjust the report etc. - """ - ret = super().do_quit(arg) - - if cls._recursive_debug == 0: - outcomes.exit("Quitting debugger") - - return ret - - do_q = do_quit - do_exit = do_quit - - def setup(self, f, tb): - """Suspend on setup(). - - Needed after do_continue resumed, and entering another - breakpoint again. - """ - ret = super().setup(f, tb) - if not ret and self._continued: - # pdb.setup() returns True if the command wants to exit - # from the interaction: do not suspend capturing then. - if self._pytest_capman: - self._pytest_capman.suspend_global_capture(in_=True) - return ret - - def get_stack(self, f, t): - stack, i = super().get_stack(f, t) - if f is None: - # Find last non-hidden frame. - i = max(0, len(stack) - 1) - while i and stack[i][0].f_locals.get("__tracebackhide__", False): - i -= 1 - return stack, i - - return PytestPdbWrapper - - @classmethod - def _init_pdb(cls, method, *args, **kwargs): - """Initialize PDB debugging, dropping any IO capturing.""" - import _pytest.config - - if cls._pluginmanager is None: - capman: CaptureManager | None = None - else: - capman = cls._pluginmanager.getplugin("capturemanager") - if capman: - capman.suspend(in_=True) - - if cls._config: - tw = _pytest.config.create_terminal_writer(cls._config) - tw.line() - - if cls._recursive_debug == 0: - # Handle header similar to pdb.set_trace in py37+. - header = kwargs.pop("header", None) - if header is not None: - tw.sep(">", header) - else: - capturing = cls._is_capturing(capman) - if capturing == "global": - tw.sep(">", f"PDB {method} (IO-capturing turned off)") - elif capturing: - tw.sep( - ">", - f"PDB {method} (IO-capturing turned off for {capturing})", - ) - else: - tw.sep(">", f"PDB {method}") - - _pdb = cls._import_pdb_cls(capman)(**kwargs) - - if cls._pluginmanager: - cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config, pdb=_pdb) - return _pdb - - @classmethod - def set_trace(cls, *args, **kwargs) -> None: - """Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing.""" - frame = sys._getframe().f_back - _pdb = cls._init_pdb("set_trace", *args, **kwargs) - _pdb.set_trace(frame) - - -class PdbInvoke: - def pytest_exception_interact( - self, node: Node, call: CallInfo[Any], report: BaseReport - ) -> None: - capman = node.config.pluginmanager.getplugin("capturemanager") - if capman: - capman.suspend_global_capture(in_=True) - out, err = capman.read_global_capture() - sys.stdout.write(out) - sys.stdout.write(err) - assert call.excinfo is not None - - if not isinstance(call.excinfo.value, unittest.SkipTest): - _enter_pdb(node, call.excinfo, report) - - def pytest_internalerror(self, excinfo: ExceptionInfo[BaseException]) -> None: - tb = _postmortem_traceback(excinfo) - post_mortem(tb) - - -class PdbTrace: - @hookimpl(wrapper=True) - def pytest_pyfunc_call(self, pyfuncitem) -> Generator[None, object, object]: - wrap_pytest_function_for_tracing(pyfuncitem) - return (yield) - - -def wrap_pytest_function_for_tracing(pyfuncitem) -> None: - """Change the Python function object of the given Function item by a - wrapper which actually enters pdb before calling the python function - itself, effectively leaving the user in the pdb prompt in the first - statement of the function.""" - _pdb = pytestPDB._init_pdb("runcall") - testfunction = pyfuncitem.obj - - # we can't just return `partial(pdb.runcall, testfunction)` because (on - # python < 3.7.4) runcall's first param is `func`, which means we'd get - # an exception if one of the kwargs to testfunction was called `func`. - @functools.wraps(testfunction) - def wrapper(*args, **kwargs) -> None: - func = functools.partial(testfunction, *args, **kwargs) - _pdb.runcall(func) - - pyfuncitem.obj = wrapper - - -def maybe_wrap_pytest_function_for_tracing(pyfuncitem) -> None: - """Wrap the given pytestfunct item for tracing support if --trace was given in - the command line.""" - if pyfuncitem.config.getvalue("trace"): - wrap_pytest_function_for_tracing(pyfuncitem) - - -def _enter_pdb( - node: Node, excinfo: ExceptionInfo[BaseException], rep: BaseReport -) -> BaseReport: - # XXX we reuse the TerminalReporter's terminalwriter - # because this seems to avoid some encoding related troubles - # for not completely clear reasons. - tw = node.config.pluginmanager.getplugin("terminalreporter")._tw - tw.line() - - showcapture = node.config.option.showcapture - - for sectionname, content in ( - ("stdout", rep.capstdout), - ("stderr", rep.capstderr), - ("log", rep.caplog), - ): - if showcapture in (sectionname, "all") and content: - tw.sep(">", "captured " + sectionname) - if content[-1:] == "\n": - content = content[:-1] - tw.line(content) - - tw.sep(">", "traceback") - rep.toterminal(tw) - tw.sep(">", "entering PDB") - tb = _postmortem_traceback(excinfo) - rep._pdbshown = True # type: ignore[attr-defined] - post_mortem(tb) - return rep - - -def _postmortem_traceback(excinfo: ExceptionInfo[BaseException]) -> types.TracebackType: - from doctest import UnexpectedException - - if isinstance(excinfo.value, UnexpectedException): - # A doctest.UnexpectedException is not useful for post_mortem. - # Use the underlying exception instead: - return excinfo.value.exc_info[2] - elif isinstance(excinfo.value, ConftestImportFailure): - # A config.ConftestImportFailure is not useful for post_mortem. - # Use the underlying exception instead: - assert excinfo.value.cause.__traceback__ is not None - return excinfo.value.cause.__traceback__ - else: - assert excinfo._excinfo is not None - return excinfo._excinfo[2] - - -def post_mortem(t: types.TracebackType) -> None: - p = pytestPDB._init_pdb("post_mortem") - p.reset() - p.interaction(None, t) - if p.quitting: - outcomes.exit("Quitting debugger") diff --git a/.venv/lib/python3.12/site-packages/_pytest/deprecated.py b/.venv/lib/python3.12/site-packages/_pytest/deprecated.py deleted file mode 100644 index a605c24e..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/deprecated.py +++ /dev/null @@ -1,91 +0,0 @@ -"""Deprecation messages and bits of code used elsewhere in the codebase that -is planned to be removed in the next pytest release. - -Keeping it in a central location makes it easy to track what is deprecated and should -be removed when the time comes. - -All constants defined in this module should be either instances of -:class:`PytestWarning`, or :class:`UnformattedWarning` -in case of warnings which need to format their messages. -""" - -from __future__ import annotations - -from warnings import warn - -from _pytest.warning_types import PytestDeprecationWarning -from _pytest.warning_types import PytestRemovedIn9Warning -from _pytest.warning_types import UnformattedWarning - - -# set of plugins which have been integrated into the core; we use this list to ignore -# them during registration to avoid conflicts -DEPRECATED_EXTERNAL_PLUGINS = { - "pytest_catchlog", - "pytest_capturelog", - "pytest_faulthandler", -} - - -# This can be* removed pytest 8, but it's harmless and common, so no rush to remove. -# * If you're in the future: "could have been". -YIELD_FIXTURE = PytestDeprecationWarning( - "@pytest.yield_fixture is deprecated.\n" - "Use @pytest.fixture instead; they are the same." -) - -# This deprecation is never really meant to be removed. -PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.") - - -HOOK_LEGACY_PATH_ARG = UnformattedWarning( - PytestRemovedIn9Warning, - "The ({pylib_path_arg}: py.path.local) argument is deprecated, please use ({pathlib_path_arg}: pathlib.Path)\n" - "see https://docs.pytest.org/en/latest/deprecations.html" - "#py-path-local-arguments-for-hooks-replaced-with-pathlib-path", -) - -NODE_CTOR_FSPATH_ARG = UnformattedWarning( - PytestRemovedIn9Warning, - "The (fspath: py.path.local) argument to {node_type_name} is deprecated. " - "Please use the (path: pathlib.Path) argument instead.\n" - "See https://docs.pytest.org/en/latest/deprecations.html" - "#fspath-argument-for-node-constructors-replaced-with-pathlib-path", -) - -HOOK_LEGACY_MARKING = UnformattedWarning( - PytestDeprecationWarning, - "The hook{type} {fullname} uses old-style configuration options (marks or attributes).\n" - "Please use the pytest.hook{type}({hook_opts}) decorator instead\n" - " to configure the hooks.\n" - " See https://docs.pytest.org/en/latest/deprecations.html" - "#configuring-hook-specs-impls-using-markers", -) - -MARKED_FIXTURE = PytestRemovedIn9Warning( - "Marks applied to fixtures have no effect\n" - "See docs: https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function" -) - -# You want to make some `__init__` or function "private". -# -# def my_private_function(some, args): -# ... -# -# Do this: -# -# def my_private_function(some, args, *, _ispytest: bool = False): -# check_ispytest(_ispytest) -# ... -# -# Change all internal/allowed calls to -# -# my_private_function(some, args, _ispytest=True) -# -# All other calls will get the default _ispytest=False and trigger -# the warning (possibly error in the future). - - -def check_ispytest(ispytest: bool) -> None: - if not ispytest: - warn(PRIVATE, stacklevel=3) diff --git a/.venv/lib/python3.12/site-packages/_pytest/doctest.py b/.venv/lib/python3.12/site-packages/_pytest/doctest.py deleted file mode 100644 index 384dea97..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/doctest.py +++ /dev/null @@ -1,755 +0,0 @@ -# mypy: allow-untyped-defs -"""Discover and run doctests in modules and test files.""" - -from __future__ import annotations - -import bdb -from contextlib import contextmanager -import functools -import inspect -import os -from pathlib import Path -import platform -import sys -import traceback -import types -from typing import Any -from typing import Callable -from typing import Generator -from typing import Iterable -from typing import Pattern -from typing import Sequence -from typing import TYPE_CHECKING -import warnings - -from _pytest import outcomes -from _pytest._code.code import ExceptionInfo -from _pytest._code.code import ReprFileLocation -from _pytest._code.code import TerminalRepr -from _pytest._io import TerminalWriter -from _pytest.compat import safe_getattr -from _pytest.config import Config -from _pytest.config.argparsing import Parser -from _pytest.fixtures import fixture -from _pytest.fixtures import TopRequest -from _pytest.nodes import Collector -from _pytest.nodes import Item -from _pytest.outcomes import OutcomeException -from _pytest.outcomes import skip -from _pytest.pathlib import fnmatch_ex -from _pytest.python import Module -from _pytest.python_api import approx -from _pytest.warning_types import PytestWarning - - -if TYPE_CHECKING: - import doctest - - from typing_extensions import Self - -DOCTEST_REPORT_CHOICE_NONE = "none" -DOCTEST_REPORT_CHOICE_CDIFF = "cdiff" -DOCTEST_REPORT_CHOICE_NDIFF = "ndiff" -DOCTEST_REPORT_CHOICE_UDIFF = "udiff" -DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = "only_first_failure" - -DOCTEST_REPORT_CHOICES = ( - DOCTEST_REPORT_CHOICE_NONE, - DOCTEST_REPORT_CHOICE_CDIFF, - DOCTEST_REPORT_CHOICE_NDIFF, - DOCTEST_REPORT_CHOICE_UDIFF, - DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE, -) - -# Lazy definition of runner class -RUNNER_CLASS = None -# Lazy definition of output checker class -CHECKER_CLASS: type[doctest.OutputChecker] | None = None - - -def pytest_addoption(parser: Parser) -> None: - parser.addini( - "doctest_optionflags", - "Option flags for doctests", - type="args", - default=["ELLIPSIS"], - ) - parser.addini( - "doctest_encoding", "Encoding used for doctest files", default="utf-8" - ) - group = parser.getgroup("collect") - group.addoption( - "--doctest-modules", - action="store_true", - default=False, - help="Run doctests in all .py modules", - dest="doctestmodules", - ) - group.addoption( - "--doctest-report", - type=str.lower, - default="udiff", - help="Choose another output format for diffs on doctest failure", - choices=DOCTEST_REPORT_CHOICES, - dest="doctestreport", - ) - group.addoption( - "--doctest-glob", - action="append", - default=[], - metavar="pat", - help="Doctests file matching pattern, default: test*.txt", - dest="doctestglob", - ) - group.addoption( - "--doctest-ignore-import-errors", - action="store_true", - default=False, - help="Ignore doctest collection errors", - dest="doctest_ignore_import_errors", - ) - group.addoption( - "--doctest-continue-on-failure", - action="store_true", - default=False, - help="For a given doctest, continue to run after the first failure", - dest="doctest_continue_on_failure", - ) - - -def pytest_unconfigure() -> None: - global RUNNER_CLASS - - RUNNER_CLASS = None - - -def pytest_collect_file( - file_path: Path, - parent: Collector, -) -> DoctestModule | DoctestTextfile | None: - config = parent.config - if file_path.suffix == ".py": - if config.option.doctestmodules and not any( - (_is_setup_py(file_path), _is_main_py(file_path)) - ): - return DoctestModule.from_parent(parent, path=file_path) - elif _is_doctest(config, file_path, parent): - return DoctestTextfile.from_parent(parent, path=file_path) - return None - - -def _is_setup_py(path: Path) -> bool: - if path.name != "setup.py": - return False - contents = path.read_bytes() - return b"setuptools" in contents or b"distutils" in contents - - -def _is_doctest(config: Config, path: Path, parent: Collector) -> bool: - if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path): - return True - globs = config.getoption("doctestglob") or ["test*.txt"] - return any(fnmatch_ex(glob, path) for glob in globs) - - -def _is_main_py(path: Path) -> bool: - return path.name == "__main__.py" - - -class ReprFailDoctest(TerminalRepr): - def __init__( - self, reprlocation_lines: Sequence[tuple[ReprFileLocation, Sequence[str]]] - ) -> None: - self.reprlocation_lines = reprlocation_lines - - def toterminal(self, tw: TerminalWriter) -> None: - for reprlocation, lines in self.reprlocation_lines: - for line in lines: - tw.line(line) - reprlocation.toterminal(tw) - - -class MultipleDoctestFailures(Exception): - def __init__(self, failures: Sequence[doctest.DocTestFailure]) -> None: - super().__init__() - self.failures = failures - - -def _init_runner_class() -> type[doctest.DocTestRunner]: - import doctest - - class PytestDoctestRunner(doctest.DebugRunner): - """Runner to collect failures. - - Note that the out variable in this case is a list instead of a - stdout-like object. - """ - - def __init__( - self, - checker: doctest.OutputChecker | None = None, - verbose: bool | None = None, - optionflags: int = 0, - continue_on_failure: bool = True, - ) -> None: - super().__init__(checker=checker, verbose=verbose, optionflags=optionflags) - self.continue_on_failure = continue_on_failure - - def report_failure( - self, - out, - test: doctest.DocTest, - example: doctest.Example, - got: str, - ) -> None: - failure = doctest.DocTestFailure(test, example, got) - if self.continue_on_failure: - out.append(failure) - else: - raise failure - - def report_unexpected_exception( - self, - out, - test: doctest.DocTest, - example: doctest.Example, - exc_info: tuple[type[BaseException], BaseException, types.TracebackType], - ) -> None: - if isinstance(exc_info[1], OutcomeException): - raise exc_info[1] - if isinstance(exc_info[1], bdb.BdbQuit): - outcomes.exit("Quitting debugger") - failure = doctest.UnexpectedException(test, example, exc_info) - if self.continue_on_failure: - out.append(failure) - else: - raise failure - - return PytestDoctestRunner - - -def _get_runner( - checker: doctest.OutputChecker | None = None, - verbose: bool | None = None, - optionflags: int = 0, - continue_on_failure: bool = True, -) -> doctest.DocTestRunner: - # We need this in order to do a lazy import on doctest - global RUNNER_CLASS - if RUNNER_CLASS is None: - RUNNER_CLASS = _init_runner_class() - # Type ignored because the continue_on_failure argument is only defined on - # PytestDoctestRunner, which is lazily defined so can't be used as a type. - return RUNNER_CLASS( # type: ignore - checker=checker, - verbose=verbose, - optionflags=optionflags, - continue_on_failure=continue_on_failure, - ) - - -class DoctestItem(Item): - def __init__( - self, - name: str, - parent: DoctestTextfile | DoctestModule, - runner: doctest.DocTestRunner, - dtest: doctest.DocTest, - ) -> None: - super().__init__(name, parent) - self.runner = runner - self.dtest = dtest - - # Stuff needed for fixture support. - self.obj = None - fm = self.session._fixturemanager - fixtureinfo = fm.getfixtureinfo(node=self, func=None, cls=None) - self._fixtureinfo = fixtureinfo - self.fixturenames = fixtureinfo.names_closure - self._initrequest() - - @classmethod - def from_parent( # type: ignore[override] - cls, - parent: DoctestTextfile | DoctestModule, - *, - name: str, - runner: doctest.DocTestRunner, - dtest: doctest.DocTest, - ) -> Self: - # incompatible signature due to imposed limits on subclass - """The public named constructor.""" - return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) - - def _initrequest(self) -> None: - self.funcargs: dict[str, object] = {} - self._request = TopRequest(self, _ispytest=True) # type: ignore[arg-type] - - def setup(self) -> None: - self._request._fillfixtures() - globs = dict(getfixture=self._request.getfixturevalue) - for name, value in self._request.getfixturevalue("doctest_namespace").items(): - globs[name] = value - self.dtest.globs.update(globs) - - def runtest(self) -> None: - _check_all_skipped(self.dtest) - self._disable_output_capturing_for_darwin() - failures: list[doctest.DocTestFailure] = [] - # Type ignored because we change the type of `out` from what - # doctest expects. - self.runner.run(self.dtest, out=failures) # type: ignore[arg-type] - if failures: - raise MultipleDoctestFailures(failures) - - def _disable_output_capturing_for_darwin(self) -> None: - """Disable output capturing. Otherwise, stdout is lost to doctest (#985).""" - if platform.system() != "Darwin": - return - capman = self.config.pluginmanager.getplugin("capturemanager") - if capman: - capman.suspend_global_capture(in_=True) - out, err = capman.read_global_capture() - sys.stdout.write(out) - sys.stderr.write(err) - - # TODO: Type ignored -- breaks Liskov Substitution. - def repr_failure( # type: ignore[override] - self, - excinfo: ExceptionInfo[BaseException], - ) -> str | TerminalRepr: - import doctest - - failures: ( - Sequence[doctest.DocTestFailure | doctest.UnexpectedException] | None - ) = None - if isinstance( - excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException) - ): - failures = [excinfo.value] - elif isinstance(excinfo.value, MultipleDoctestFailures): - failures = excinfo.value.failures - - if failures is None: - return super().repr_failure(excinfo) - - reprlocation_lines = [] - for failure in failures: - example = failure.example - test = failure.test - filename = test.filename - if test.lineno is None: - lineno = None - else: - lineno = test.lineno + example.lineno + 1 - message = type(failure).__name__ - # TODO: ReprFileLocation doesn't expect a None lineno. - reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] - checker = _get_checker() - report_choice = _get_report_choice(self.config.getoption("doctestreport")) - if lineno is not None: - assert failure.test.docstring is not None - lines = failure.test.docstring.splitlines(False) - # add line numbers to the left of the error message - assert test.lineno is not None - lines = [ - "%03d %s" % (i + test.lineno + 1, x) for (i, x) in enumerate(lines) - ] - # trim docstring error lines to 10 - lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] - else: - lines = [ - "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" - ] - indent = ">>>" - for line in example.source.splitlines(): - lines.append(f"??? {indent} {line}") - indent = "..." - if isinstance(failure, doctest.DocTestFailure): - lines += checker.output_difference( - example, failure.got, report_choice - ).split("\n") - else: - inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) - lines += [f"UNEXPECTED EXCEPTION: {inner_excinfo.value!r}"] - lines += [ - x.strip("\n") for x in traceback.format_exception(*failure.exc_info) - ] - reprlocation_lines.append((reprlocation, lines)) - return ReprFailDoctest(reprlocation_lines) - - def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: - return self.path, self.dtest.lineno, f"[doctest] {self.name}" - - -def _get_flag_lookup() -> dict[str, int]: - import doctest - - return dict( - DONT_ACCEPT_TRUE_FOR_1=doctest.DONT_ACCEPT_TRUE_FOR_1, - DONT_ACCEPT_BLANKLINE=doctest.DONT_ACCEPT_BLANKLINE, - NORMALIZE_WHITESPACE=doctest.NORMALIZE_WHITESPACE, - ELLIPSIS=doctest.ELLIPSIS, - IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL, - COMPARISON_FLAGS=doctest.COMPARISON_FLAGS, - ALLOW_UNICODE=_get_allow_unicode_flag(), - ALLOW_BYTES=_get_allow_bytes_flag(), - NUMBER=_get_number_flag(), - ) - - -def get_optionflags(config: Config) -> int: - optionflags_str = config.getini("doctest_optionflags") - flag_lookup_table = _get_flag_lookup() - flag_acc = 0 - for flag in optionflags_str: - flag_acc |= flag_lookup_table[flag] - return flag_acc - - -def _get_continue_on_failure(config: Config) -> bool: - continue_on_failure: bool = config.getvalue("doctest_continue_on_failure") - if continue_on_failure: - # We need to turn off this if we use pdb since we should stop at - # the first failure. - if config.getvalue("usepdb"): - continue_on_failure = False - return continue_on_failure - - -class DoctestTextfile(Module): - obj = None - - def collect(self) -> Iterable[DoctestItem]: - import doctest - - # Inspired by doctest.testfile; ideally we would use it directly, - # but it doesn't support passing a custom checker. - encoding = self.config.getini("doctest_encoding") - text = self.path.read_text(encoding) - filename = str(self.path) - name = self.path.name - globs = {"__name__": "__main__"} - - optionflags = get_optionflags(self.config) - - runner = _get_runner( - verbose=False, - optionflags=optionflags, - checker=_get_checker(), - continue_on_failure=_get_continue_on_failure(self.config), - ) - - parser = doctest.DocTestParser() - test = parser.get_doctest(text, globs, name, filename, 0) - if test.examples: - yield DoctestItem.from_parent( - self, name=test.name, runner=runner, dtest=test - ) - - -def _check_all_skipped(test: doctest.DocTest) -> None: - """Raise pytest.skip() if all examples in the given DocTest have the SKIP - option set.""" - import doctest - - all_skipped = all(x.options.get(doctest.SKIP, False) for x in test.examples) - if all_skipped: - skip("all tests skipped by +SKIP option") - - -def _is_mocked(obj: object) -> bool: - """Return if an object is possibly a mock object by checking the - existence of a highly improbable attribute.""" - return ( - safe_getattr(obj, "pytest_mock_example_attribute_that_shouldnt_exist", None) - is not None - ) - - -@contextmanager -def _patch_unwrap_mock_aware() -> Generator[None]: - """Context manager which replaces ``inspect.unwrap`` with a version - that's aware of mock objects and doesn't recurse into them.""" - real_unwrap = inspect.unwrap - - def _mock_aware_unwrap( - func: Callable[..., Any], *, stop: Callable[[Any], Any] | None = None - ) -> Any: - try: - if stop is None or stop is _is_mocked: - return real_unwrap(func, stop=_is_mocked) - _stop = stop - return real_unwrap(func, stop=lambda obj: _is_mocked(obj) or _stop(func)) - except Exception as e: - warnings.warn( - f"Got {e!r} when unwrapping {func!r}. This is usually caused " - "by a violation of Python's object protocol; see e.g. " - "https://github.com/pytest-dev/pytest/issues/5080", - PytestWarning, - ) - raise - - inspect.unwrap = _mock_aware_unwrap - try: - yield - finally: - inspect.unwrap = real_unwrap - - -class DoctestModule(Module): - def collect(self) -> Iterable[DoctestItem]: - import doctest - - class MockAwareDocTestFinder(doctest.DocTestFinder): - py_ver_info_minor = sys.version_info[:2] - is_find_lineno_broken = ( - py_ver_info_minor < (3, 11) - or (py_ver_info_minor == (3, 11) and sys.version_info.micro < 9) - or (py_ver_info_minor == (3, 12) and sys.version_info.micro < 3) - ) - if is_find_lineno_broken: - - def _find_lineno(self, obj, source_lines): - """On older Pythons, doctest code does not take into account - `@property`. https://github.com/python/cpython/issues/61648 - - Moreover, wrapped Doctests need to be unwrapped so the correct - line number is returned. #8796 - """ - if isinstance(obj, property): - obj = getattr(obj, "fget", obj) - - if hasattr(obj, "__wrapped__"): - # Get the main obj in case of it being wrapped - obj = inspect.unwrap(obj) - - # Type ignored because this is a private function. - return super()._find_lineno( # type:ignore[misc] - obj, - source_lines, - ) - - if sys.version_info < (3, 10): - - def _find( - self, tests, obj, name, module, source_lines, globs, seen - ) -> None: - """Override _find to work around issue in stdlib. - - https://github.com/pytest-dev/pytest/issues/3456 - https://github.com/python/cpython/issues/69718 - """ - if _is_mocked(obj): - return # pragma: no cover - with _patch_unwrap_mock_aware(): - # Type ignored because this is a private function. - super()._find( # type:ignore[misc] - tests, obj, name, module, source_lines, globs, seen - ) - - if sys.version_info < (3, 13): - - def _from_module(self, module, object): - """`cached_property` objects are never considered a part - of the 'current module'. As such they are skipped by doctest. - Here we override `_from_module` to check the underlying - function instead. https://github.com/python/cpython/issues/107995 - """ - if isinstance(object, functools.cached_property): - object = object.func - - # Type ignored because this is a private function. - return super()._from_module(module, object) # type: ignore[misc] - - try: - module = self.obj - except Collector.CollectError: - if self.config.getvalue("doctest_ignore_import_errors"): - skip(f"unable to import module {self.path!r}") - else: - raise - - # While doctests currently don't support fixtures directly, we still - # need to pick up autouse fixtures. - self.session._fixturemanager.parsefactories(self) - - # Uses internal doctest module parsing mechanism. - finder = MockAwareDocTestFinder() - optionflags = get_optionflags(self.config) - runner = _get_runner( - verbose=False, - optionflags=optionflags, - checker=_get_checker(), - continue_on_failure=_get_continue_on_failure(self.config), - ) - - for test in finder.find(module, module.__name__): - if test.examples: # skip empty doctests - yield DoctestItem.from_parent( - self, name=test.name, runner=runner, dtest=test - ) - - -def _init_checker_class() -> type[doctest.OutputChecker]: - import doctest - import re - - class LiteralsOutputChecker(doctest.OutputChecker): - # Based on doctest_nose_plugin.py from the nltk project - # (https://github.com/nltk/nltk) and on the "numtest" doctest extension - # by Sebastien Boisgerault (https://github.com/boisgera/numtest). - - _unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE) - _bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE) - _number_re = re.compile( - r""" - (?P - (?P - (?P [+-]?\d*)\.(?P\d+) - | - (?P [+-]?\d+)\. - ) - (?: - [Ee] - (?P [+-]?\d+) - )? - | - (?P [+-]?\d+) - (?: - [Ee] - (?P [+-]?\d+) - ) - ) - """, - re.VERBOSE, - ) - - def check_output(self, want: str, got: str, optionflags: int) -> bool: - if super().check_output(want, got, optionflags): - return True - - allow_unicode = optionflags & _get_allow_unicode_flag() - allow_bytes = optionflags & _get_allow_bytes_flag() - allow_number = optionflags & _get_number_flag() - - if not allow_unicode and not allow_bytes and not allow_number: - return False - - def remove_prefixes(regex: Pattern[str], txt: str) -> str: - return re.sub(regex, r"\1\2", txt) - - if allow_unicode: - want = remove_prefixes(self._unicode_literal_re, want) - got = remove_prefixes(self._unicode_literal_re, got) - - if allow_bytes: - want = remove_prefixes(self._bytes_literal_re, want) - got = remove_prefixes(self._bytes_literal_re, got) - - if allow_number: - got = self._remove_unwanted_precision(want, got) - - return super().check_output(want, got, optionflags) - - def _remove_unwanted_precision(self, want: str, got: str) -> str: - wants = list(self._number_re.finditer(want)) - gots = list(self._number_re.finditer(got)) - if len(wants) != len(gots): - return got - offset = 0 - for w, g in zip(wants, gots): - fraction: str | None = w.group("fraction") - exponent: str | None = w.group("exponent1") - if exponent is None: - exponent = w.group("exponent2") - precision = 0 if fraction is None else len(fraction) - if exponent is not None: - precision -= int(exponent) - if float(w.group()) == approx(float(g.group()), abs=10**-precision): - # They're close enough. Replace the text we actually - # got with the text we want, so that it will match when we - # check the string literally. - got = ( - got[: g.start() + offset] + w.group() + got[g.end() + offset :] - ) - offset += w.end() - w.start() - (g.end() - g.start()) - return got - - return LiteralsOutputChecker - - -def _get_checker() -> doctest.OutputChecker: - """Return a doctest.OutputChecker subclass that supports some - additional options: - - * ALLOW_UNICODE and ALLOW_BYTES options to ignore u'' and b'' - prefixes (respectively) in string literals. Useful when the same - doctest should run in Python 2 and Python 3. - - * NUMBER to ignore floating-point differences smaller than the - precision of the literal number in the doctest. - - An inner class is used to avoid importing "doctest" at the module - level. - """ - global CHECKER_CLASS - if CHECKER_CLASS is None: - CHECKER_CLASS = _init_checker_class() - return CHECKER_CLASS() - - -def _get_allow_unicode_flag() -> int: - """Register and return the ALLOW_UNICODE flag.""" - import doctest - - return doctest.register_optionflag("ALLOW_UNICODE") - - -def _get_allow_bytes_flag() -> int: - """Register and return the ALLOW_BYTES flag.""" - import doctest - - return doctest.register_optionflag("ALLOW_BYTES") - - -def _get_number_flag() -> int: - """Register and return the NUMBER flag.""" - import doctest - - return doctest.register_optionflag("NUMBER") - - -def _get_report_choice(key: str) -> int: - """Return the actual `doctest` module flag value. - - We want to do it as late as possible to avoid importing `doctest` and all - its dependencies when parsing options, as it adds overhead and breaks tests. - """ - import doctest - - return { - DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF, - DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF, - DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF, - DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE, - DOCTEST_REPORT_CHOICE_NONE: 0, - }[key] - - -@fixture(scope="session") -def doctest_namespace() -> dict[str, Any]: - """Fixture that returns a :py:class:`dict` that will be injected into the - namespace of doctests. - - Usually this fixture is used in conjunction with another ``autouse`` fixture: - - .. code-block:: python - - @pytest.fixture(autouse=True) - def add_np(doctest_namespace): - doctest_namespace["np"] = numpy - - For more details: :ref:`doctest_namespace`. - """ - return dict() diff --git a/.venv/lib/python3.12/site-packages/_pytest/faulthandler.py b/.venv/lib/python3.12/site-packages/_pytest/faulthandler.py deleted file mode 100644 index d16aea1e..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/faulthandler.py +++ /dev/null @@ -1,105 +0,0 @@ -from __future__ import annotations - -import os -import sys -from typing import Generator - -from _pytest.config import Config -from _pytest.config.argparsing import Parser -from _pytest.nodes import Item -from _pytest.stash import StashKey -import pytest - - -fault_handler_original_stderr_fd_key = StashKey[int]() -fault_handler_stderr_fd_key = StashKey[int]() - - -def pytest_addoption(parser: Parser) -> None: - help = ( - "Dump the traceback of all threads if a test takes " - "more than TIMEOUT seconds to finish" - ) - parser.addini("faulthandler_timeout", help, default=0.0) - - -def pytest_configure(config: Config) -> None: - import faulthandler - - # at teardown we want to restore the original faulthandler fileno - # but faulthandler has no api to return the original fileno - # so here we stash the stderr fileno to be used at teardown - # sys.stderr and sys.__stderr__ may be closed or patched during the session - # so we can't rely on their values being good at that point (#11572). - stderr_fileno = get_stderr_fileno() - if faulthandler.is_enabled(): - config.stash[fault_handler_original_stderr_fd_key] = stderr_fileno - config.stash[fault_handler_stderr_fd_key] = os.dup(stderr_fileno) - faulthandler.enable(file=config.stash[fault_handler_stderr_fd_key]) - - -def pytest_unconfigure(config: Config) -> None: - import faulthandler - - faulthandler.disable() - # Close the dup file installed during pytest_configure. - if fault_handler_stderr_fd_key in config.stash: - os.close(config.stash[fault_handler_stderr_fd_key]) - del config.stash[fault_handler_stderr_fd_key] - # Re-enable the faulthandler if it was originally enabled. - if fault_handler_original_stderr_fd_key in config.stash: - faulthandler.enable(config.stash[fault_handler_original_stderr_fd_key]) - del config.stash[fault_handler_original_stderr_fd_key] - - -def get_stderr_fileno() -> int: - try: - fileno = sys.stderr.fileno() - # The Twisted Logger will return an invalid file descriptor since it is not backed - # by an FD. So, let's also forward this to the same code path as with pytest-xdist. - if fileno == -1: - raise AttributeError() - return fileno - except (AttributeError, ValueError): - # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file. - # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors - # This is potentially dangerous, but the best we can do. - assert sys.__stderr__ is not None - return sys.__stderr__.fileno() - - -def get_timeout_config_value(config: Config) -> float: - return float(config.getini("faulthandler_timeout") or 0.0) - - -@pytest.hookimpl(wrapper=True, trylast=True) -def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: - timeout = get_timeout_config_value(item.config) - if timeout > 0: - import faulthandler - - stderr = item.config.stash[fault_handler_stderr_fd_key] - faulthandler.dump_traceback_later(timeout, file=stderr) - try: - return (yield) - finally: - faulthandler.cancel_dump_traceback_later() - else: - return (yield) - - -@pytest.hookimpl(tryfirst=True) -def pytest_enter_pdb() -> None: - """Cancel any traceback dumping due to timeout before entering pdb.""" - import faulthandler - - faulthandler.cancel_dump_traceback_later() - - -@pytest.hookimpl(tryfirst=True) -def pytest_exception_interact() -> None: - """Cancel any traceback dumping due to an interactive exception being - raised.""" - import faulthandler - - faulthandler.cancel_dump_traceback_later() diff --git a/.venv/lib/python3.12/site-packages/_pytest/fixtures.py b/.venv/lib/python3.12/site-packages/_pytest/fixtures.py deleted file mode 100644 index 6b882fa3..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/fixtures.py +++ /dev/null @@ -1,1932 +0,0 @@ -# mypy: allow-untyped-defs -from __future__ import annotations - -import abc -from collections import defaultdict -from collections import deque -import dataclasses -import functools -import inspect -import os -from pathlib import Path -import sys -import types -from typing import AbstractSet -from typing import Any -from typing import Callable -from typing import cast -from typing import Dict -from typing import Final -from typing import final -from typing import Generator -from typing import Generic -from typing import Iterable -from typing import Iterator -from typing import Mapping -from typing import MutableMapping -from typing import NoReturn -from typing import Optional -from typing import OrderedDict -from typing import overload -from typing import Sequence -from typing import Tuple -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union -import warnings - -import _pytest -from _pytest import nodes -from _pytest._code import getfslineno -from _pytest._code import Source -from _pytest._code.code import FormattedExcinfo -from _pytest._code.code import TerminalRepr -from _pytest._io import TerminalWriter -from _pytest.compat import _PytestWrapper -from _pytest.compat import assert_never -from _pytest.compat import get_real_func -from _pytest.compat import get_real_method -from _pytest.compat import getfuncargnames -from _pytest.compat import getimfunc -from _pytest.compat import getlocation -from _pytest.compat import is_generator -from _pytest.compat import NOTSET -from _pytest.compat import NotSetType -from _pytest.compat import safe_getattr -from _pytest.compat import safe_isclass -from _pytest.config import _PluggyPlugin -from _pytest.config import Config -from _pytest.config import ExitCode -from _pytest.config.argparsing import Parser -from _pytest.deprecated import check_ispytest -from _pytest.deprecated import MARKED_FIXTURE -from _pytest.deprecated import YIELD_FIXTURE -from _pytest.main import Session -from _pytest.mark import Mark -from _pytest.mark import ParameterSet -from _pytest.mark.structures import MarkDecorator -from _pytest.outcomes import fail -from _pytest.outcomes import skip -from _pytest.outcomes import TEST_OUTCOME -from _pytest.pathlib import absolutepath -from _pytest.pathlib import bestrelpath -from _pytest.scope import _ScopeName -from _pytest.scope import HIGH_SCOPES -from _pytest.scope import Scope - - -if sys.version_info < (3, 11): - from exceptiongroup import BaseExceptionGroup - - -if TYPE_CHECKING: - from _pytest.python import CallSpec2 - from _pytest.python import Function - from _pytest.python import Metafunc - - -# The value of the fixture -- return/yield of the fixture function (type variable). -FixtureValue = TypeVar("FixtureValue") -# The type of the fixture function (type variable). -FixtureFunction = TypeVar("FixtureFunction", bound=Callable[..., object]) -# The type of a fixture function (type alias generic in fixture value). -_FixtureFunc = Union[ - Callable[..., FixtureValue], Callable[..., Generator[FixtureValue, None, None]] -] -# The type of FixtureDef.cached_result (type alias generic in fixture value). -_FixtureCachedResult = Union[ - Tuple[ - # The result. - FixtureValue, - # Cache key. - object, - None, - ], - Tuple[ - None, - # Cache key. - object, - # The exception and the original traceback. - Tuple[BaseException, Optional[types.TracebackType]], - ], -] - - -@dataclasses.dataclass(frozen=True) -class PseudoFixtureDef(Generic[FixtureValue]): - cached_result: _FixtureCachedResult[FixtureValue] - _scope: Scope - - -def pytest_sessionstart(session: Session) -> None: - session._fixturemanager = FixtureManager(session) - - -def get_scope_package( - node: nodes.Item, - fixturedef: FixtureDef[object], -) -> nodes.Node | None: - from _pytest.python import Package - - for parent in node.iter_parents(): - if isinstance(parent, Package) and parent.nodeid == fixturedef.baseid: - return parent - return node.session - - -def get_scope_node(node: nodes.Node, scope: Scope) -> nodes.Node | None: - import _pytest.python - - if scope is Scope.Function: - # Type ignored because this is actually safe, see: - # https://github.com/python/mypy/issues/4717 - return node.getparent(nodes.Item) # type: ignore[type-abstract] - elif scope is Scope.Class: - return node.getparent(_pytest.python.Class) - elif scope is Scope.Module: - return node.getparent(_pytest.python.Module) - elif scope is Scope.Package: - return node.getparent(_pytest.python.Package) - elif scope is Scope.Session: - return node.getparent(_pytest.main.Session) - else: - assert_never(scope) - - -def getfixturemarker(obj: object) -> FixtureFunctionMarker | None: - """Return fixturemarker or None if it doesn't exist or raised - exceptions.""" - return cast( - Optional[FixtureFunctionMarker], - safe_getattr(obj, "_pytestfixturefunction", None), - ) - - -# Algorithm for sorting on a per-parametrized resource setup basis. -# It is called for Session scope first and performs sorting -# down to the lower scopes such as to minimize number of "high scope" -# setups and teardowns. - - -@dataclasses.dataclass(frozen=True) -class FixtureArgKey: - argname: str - param_index: int - scoped_item_path: Path | None - item_cls: type | None - - -_V = TypeVar("_V") -OrderedSet = Dict[_V, None] - - -def get_parametrized_fixture_argkeys( - item: nodes.Item, scope: Scope -) -> Iterator[FixtureArgKey]: - """Return list of keys for all parametrized arguments which match - the specified scope.""" - assert scope is not Scope.Function - - try: - callspec: CallSpec2 = item.callspec # type: ignore[attr-defined] - except AttributeError: - return - - item_cls = None - if scope is Scope.Session: - scoped_item_path = None - elif scope is Scope.Package: - # Package key = module's directory. - scoped_item_path = item.path.parent - elif scope is Scope.Module: - scoped_item_path = item.path - elif scope is Scope.Class: - scoped_item_path = item.path - item_cls = item.cls # type: ignore[attr-defined] - else: - assert_never(scope) - - for argname in callspec.indices: - if callspec._arg2scope[argname] != scope: - continue - param_index = callspec.indices[argname] - yield FixtureArgKey(argname, param_index, scoped_item_path, item_cls) - - -def reorder_items(items: Sequence[nodes.Item]) -> list[nodes.Item]: - argkeys_by_item: dict[Scope, dict[nodes.Item, OrderedSet[FixtureArgKey]]] = {} - items_by_argkey: dict[ - Scope, dict[FixtureArgKey, OrderedDict[nodes.Item, None]] - ] = {} - for scope in HIGH_SCOPES: - scoped_argkeys_by_item = argkeys_by_item[scope] = {} - scoped_items_by_argkey = items_by_argkey[scope] = defaultdict(OrderedDict) - for item in items: - argkeys = dict.fromkeys(get_parametrized_fixture_argkeys(item, scope)) - if argkeys: - scoped_argkeys_by_item[item] = argkeys - for argkey in argkeys: - scoped_items_by_argkey[argkey][item] = None - - items_set = dict.fromkeys(items) - return list( - reorder_items_atscope( - items_set, argkeys_by_item, items_by_argkey, Scope.Session - ) - ) - - -def reorder_items_atscope( - items: OrderedSet[nodes.Item], - argkeys_by_item: Mapping[Scope, Mapping[nodes.Item, OrderedSet[FixtureArgKey]]], - items_by_argkey: Mapping[ - Scope, Mapping[FixtureArgKey, OrderedDict[nodes.Item, None]] - ], - scope: Scope, -) -> OrderedSet[nodes.Item]: - if scope is Scope.Function or len(items) < 3: - return items - - scoped_items_by_argkey = items_by_argkey[scope] - scoped_argkeys_by_item = argkeys_by_item[scope] - - ignore: set[FixtureArgKey] = set() - items_deque = deque(items) - items_done: OrderedSet[nodes.Item] = {} - while items_deque: - no_argkey_items: OrderedSet[nodes.Item] = {} - slicing_argkey = None - while items_deque: - item = items_deque.popleft() - if item in items_done or item in no_argkey_items: - continue - argkeys = dict.fromkeys( - k for k in scoped_argkeys_by_item.get(item, ()) if k not in ignore - ) - if not argkeys: - no_argkey_items[item] = None - else: - slicing_argkey, _ = argkeys.popitem() - # We don't have to remove relevant items from later in the - # deque because they'll just be ignored. - matching_items = [ - i for i in scoped_items_by_argkey[slicing_argkey] if i in items - ] - for i in reversed(matching_items): - items_deque.appendleft(i) - # Fix items_by_argkey order. - for other_scope in HIGH_SCOPES: - other_scoped_items_by_argkey = items_by_argkey[other_scope] - for argkey in argkeys_by_item[other_scope].get(i, ()): - other_scoped_items_by_argkey[argkey][i] = None - other_scoped_items_by_argkey[argkey].move_to_end( - i, last=False - ) - break - if no_argkey_items: - reordered_no_argkey_items = reorder_items_atscope( - no_argkey_items, argkeys_by_item, items_by_argkey, scope.next_lower() - ) - items_done.update(reordered_no_argkey_items) - if slicing_argkey is not None: - ignore.add(slicing_argkey) - return items_done - - -@dataclasses.dataclass(frozen=True) -class FuncFixtureInfo: - """Fixture-related information for a fixture-requesting item (e.g. test - function). - - This is used to examine the fixtures which an item requests statically - (known during collection). This includes autouse fixtures, fixtures - requested by the `usefixtures` marker, fixtures requested in the function - parameters, and the transitive closure of these. - - An item may also request fixtures dynamically (using `request.getfixturevalue`); - these are not reflected here. - """ - - __slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs") - - # Fixture names that the item requests directly by function parameters. - argnames: tuple[str, ...] - # Fixture names that the item immediately requires. These include - # argnames + fixture names specified via usefixtures and via autouse=True in - # fixture definitions. - initialnames: tuple[str, ...] - # The transitive closure of the fixture names that the item requires. - # Note: can't include dynamic dependencies (`request.getfixturevalue` calls). - names_closure: list[str] - # A map from a fixture name in the transitive closure to the FixtureDefs - # matching the name which are applicable to this function. - # There may be multiple overriding fixtures with the same name. The - # sequence is ordered from furthest to closes to the function. - name2fixturedefs: dict[str, Sequence[FixtureDef[Any]]] - - def prune_dependency_tree(self) -> None: - """Recompute names_closure from initialnames and name2fixturedefs. - - Can only reduce names_closure, which means that the new closure will - always be a subset of the old one. The order is preserved. - - This method is needed because direct parametrization may shadow some - of the fixtures that were included in the originally built dependency - tree. In this way the dependency tree can get pruned, and the closure - of argnames may get reduced. - """ - closure: set[str] = set() - working_set = set(self.initialnames) - while working_set: - argname = working_set.pop() - # Argname may be something not included in the original names_closure, - # in which case we ignore it. This currently happens with pseudo - # FixtureDefs which wrap 'get_direct_param_fixture_func(request)'. - # So they introduce the new dependency 'request' which might have - # been missing in the original tree (closure). - if argname not in closure and argname in self.names_closure: - closure.add(argname) - if argname in self.name2fixturedefs: - working_set.update(self.name2fixturedefs[argname][-1].argnames) - - self.names_closure[:] = sorted(closure, key=self.names_closure.index) - - -class FixtureRequest(abc.ABC): - """The type of the ``request`` fixture. - - A request object gives access to the requesting test context and has a - ``param`` attribute in case the fixture is parametrized. - """ - - def __init__( - self, - pyfuncitem: Function, - fixturename: str | None, - arg2fixturedefs: dict[str, Sequence[FixtureDef[Any]]], - fixture_defs: dict[str, FixtureDef[Any]], - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - #: Fixture for which this request is being performed. - self.fixturename: Final = fixturename - self._pyfuncitem: Final = pyfuncitem - # The FixtureDefs for each fixture name requested by this item. - # Starts from the statically-known fixturedefs resolved during - # collection. Dynamically requested fixtures (using - # `request.getfixturevalue("foo")`) are added dynamically. - self._arg2fixturedefs: Final = arg2fixturedefs - # The evaluated argnames so far, mapping to the FixtureDef they resolved - # to. - self._fixture_defs: Final = fixture_defs - # Notes on the type of `param`: - # -`request.param` is only defined in parametrized fixtures, and will raise - # AttributeError otherwise. Python typing has no notion of "undefined", so - # this cannot be reflected in the type. - # - Technically `param` is only (possibly) defined on SubRequest, not - # FixtureRequest, but the typing of that is still in flux so this cheats. - # - In the future we might consider using a generic for the param type, but - # for now just using Any. - self.param: Any - - @property - def _fixturemanager(self) -> FixtureManager: - return self._pyfuncitem.session._fixturemanager - - @property - @abc.abstractmethod - def _scope(self) -> Scope: - raise NotImplementedError() - - @property - def scope(self) -> _ScopeName: - """Scope string, one of "function", "class", "module", "package", "session".""" - return self._scope.value - - @abc.abstractmethod - def _check_scope( - self, - requested_fixturedef: FixtureDef[object] | PseudoFixtureDef[object], - requested_scope: Scope, - ) -> None: - raise NotImplementedError() - - @property - def fixturenames(self) -> list[str]: - """Names of all active fixtures in this request.""" - result = list(self._pyfuncitem.fixturenames) - result.extend(set(self._fixture_defs).difference(result)) - return result - - @property - @abc.abstractmethod - def node(self): - """Underlying collection node (depends on current request scope).""" - raise NotImplementedError() - - @property - def config(self) -> Config: - """The pytest config object associated with this request.""" - return self._pyfuncitem.config - - @property - def function(self): - """Test function object if the request has a per-function scope.""" - if self.scope != "function": - raise AttributeError( - f"function not available in {self.scope}-scoped context" - ) - return self._pyfuncitem.obj - - @property - def cls(self): - """Class (can be None) where the test function was collected.""" - if self.scope not in ("class", "function"): - raise AttributeError(f"cls not available in {self.scope}-scoped context") - clscol = self._pyfuncitem.getparent(_pytest.python.Class) - if clscol: - return clscol.obj - - @property - def instance(self): - """Instance (can be None) on which test function was collected.""" - if self.scope != "function": - return None - return getattr(self._pyfuncitem, "instance", None) - - @property - def module(self): - """Python module object where the test function was collected.""" - if self.scope not in ("function", "class", "module"): - raise AttributeError(f"module not available in {self.scope}-scoped context") - mod = self._pyfuncitem.getparent(_pytest.python.Module) - assert mod is not None - return mod.obj - - @property - def path(self) -> Path: - """Path where the test function was collected.""" - if self.scope not in ("function", "class", "module", "package"): - raise AttributeError(f"path not available in {self.scope}-scoped context") - return self._pyfuncitem.path - - @property - def keywords(self) -> MutableMapping[str, Any]: - """Keywords/markers dictionary for the underlying node.""" - node: nodes.Node = self.node - return node.keywords - - @property - def session(self) -> Session: - """Pytest session object.""" - return self._pyfuncitem.session - - @abc.abstractmethod - def addfinalizer(self, finalizer: Callable[[], object]) -> None: - """Add finalizer/teardown function to be called without arguments after - the last test within the requesting test context finished execution.""" - raise NotImplementedError() - - def applymarker(self, marker: str | MarkDecorator) -> None: - """Apply a marker to a single test function invocation. - - This method is useful if you don't want to have a keyword/marker - on all function invocations. - - :param marker: - An object created by a call to ``pytest.mark.NAME(...)``. - """ - self.node.add_marker(marker) - - def raiseerror(self, msg: str | None) -> NoReturn: - """Raise a FixtureLookupError exception. - - :param msg: - An optional custom error message. - """ - raise FixtureLookupError(None, self, msg) - - def getfixturevalue(self, argname: str) -> Any: - """Dynamically run a named fixture function. - - Declaring fixtures via function argument is recommended where possible. - But if you can only decide whether to use another fixture at test - setup time, you may use this function to retrieve it inside a fixture - or test function body. - - This method can be used during the test setup phase or the test run - phase, but during the test teardown phase a fixture's value may not - be available. - - :param argname: - The fixture name. - :raises pytest.FixtureLookupError: - If the given fixture could not be found. - """ - # Note that in addition to the use case described in the docstring, - # getfixturevalue() is also called by pytest itself during item and fixture - # setup to evaluate the fixtures that are requested statically - # (using function parameters, autouse, etc). - - fixturedef = self._get_active_fixturedef(argname) - assert fixturedef.cached_result is not None, ( - f'The fixture value for "{argname}" is not available. ' - "This can happen when the fixture has already been torn down." - ) - return fixturedef.cached_result[0] - - def _iter_chain(self) -> Iterator[SubRequest]: - """Yield all SubRequests in the chain, from self up. - - Note: does *not* yield the TopRequest. - """ - current = self - while isinstance(current, SubRequest): - yield current - current = current._parent_request - - def _get_active_fixturedef( - self, argname: str - ) -> FixtureDef[object] | PseudoFixtureDef[object]: - if argname == "request": - cached_result = (self, [0], None) - return PseudoFixtureDef(cached_result, Scope.Function) - - # If we already finished computing a fixture by this name in this item, - # return it. - fixturedef = self._fixture_defs.get(argname) - if fixturedef is not None: - self._check_scope(fixturedef, fixturedef._scope) - return fixturedef - - # Find the appropriate fixturedef. - fixturedefs = self._arg2fixturedefs.get(argname, None) - if fixturedefs is None: - # We arrive here because of a dynamic call to - # getfixturevalue(argname) which was naturally - # not known at parsing/collection time. - fixturedefs = self._fixturemanager.getfixturedefs(argname, self._pyfuncitem) - if fixturedefs is not None: - self._arg2fixturedefs[argname] = fixturedefs - # No fixtures defined with this name. - if fixturedefs is None: - raise FixtureLookupError(argname, self) - # The are no fixtures with this name applicable for the function. - if not fixturedefs: - raise FixtureLookupError(argname, self) - # A fixture may override another fixture with the same name, e.g. a - # fixture in a module can override a fixture in a conftest, a fixture in - # a class can override a fixture in the module, and so on. - # An overriding fixture can request its own name (possibly indirectly); - # in this case it gets the value of the fixture it overrides, one level - # up. - # Check how many `argname`s deep we are, and take the next one. - # `fixturedefs` is sorted from furthest to closest, so use negative - # indexing to go in reverse. - index = -1 - for request in self._iter_chain(): - if request.fixturename == argname: - index -= 1 - # If already consumed all of the available levels, fail. - if -index > len(fixturedefs): - raise FixtureLookupError(argname, self) - fixturedef = fixturedefs[index] - - # Prepare a SubRequest object for calling the fixture. - try: - callspec = self._pyfuncitem.callspec - except AttributeError: - callspec = None - if callspec is not None and argname in callspec.params: - param = callspec.params[argname] - param_index = callspec.indices[argname] - # The parametrize invocation scope overrides the fixture's scope. - scope = callspec._arg2scope[argname] - else: - param = NOTSET - param_index = 0 - scope = fixturedef._scope - self._check_fixturedef_without_param(fixturedef) - self._check_scope(fixturedef, scope) - subrequest = SubRequest( - self, scope, param, param_index, fixturedef, _ispytest=True - ) - - # Make sure the fixture value is cached, running it if it isn't - fixturedef.execute(request=subrequest) - - self._fixture_defs[argname] = fixturedef - return fixturedef - - def _check_fixturedef_without_param(self, fixturedef: FixtureDef[object]) -> None: - """Check that this request is allowed to execute this fixturedef without - a param.""" - funcitem = self._pyfuncitem - has_params = fixturedef.params is not None - fixtures_not_supported = getattr(funcitem, "nofuncargs", False) - if has_params and fixtures_not_supported: - msg = ( - f"{funcitem.name} does not support fixtures, maybe unittest.TestCase subclass?\n" - f"Node id: {funcitem.nodeid}\n" - f"Function type: {type(funcitem).__name__}" - ) - fail(msg, pytrace=False) - if has_params: - frame = inspect.stack()[3] - frameinfo = inspect.getframeinfo(frame[0]) - source_path = absolutepath(frameinfo.filename) - source_lineno = frameinfo.lineno - try: - source_path_str = str(source_path.relative_to(funcitem.config.rootpath)) - except ValueError: - source_path_str = str(source_path) - location = getlocation(fixturedef.func, funcitem.config.rootpath) - msg = ( - "The requested fixture has no parameter defined for test:\n" - f" {funcitem.nodeid}\n\n" - f"Requested fixture '{fixturedef.argname}' defined in:\n" - f"{location}\n\n" - f"Requested here:\n" - f"{source_path_str}:{source_lineno}" - ) - fail(msg, pytrace=False) - - def _get_fixturestack(self) -> list[FixtureDef[Any]]: - values = [request._fixturedef for request in self._iter_chain()] - values.reverse() - return values - - -@final -class TopRequest(FixtureRequest): - """The type of the ``request`` fixture in a test function.""" - - def __init__(self, pyfuncitem: Function, *, _ispytest: bool = False) -> None: - super().__init__( - fixturename=None, - pyfuncitem=pyfuncitem, - arg2fixturedefs=pyfuncitem._fixtureinfo.name2fixturedefs.copy(), - fixture_defs={}, - _ispytest=_ispytest, - ) - - @property - def _scope(self) -> Scope: - return Scope.Function - - def _check_scope( - self, - requested_fixturedef: FixtureDef[object] | PseudoFixtureDef[object], - requested_scope: Scope, - ) -> None: - # TopRequest always has function scope so always valid. - pass - - @property - def node(self): - return self._pyfuncitem - - def __repr__(self) -> str: - return f"" - - def _fillfixtures(self) -> None: - item = self._pyfuncitem - for argname in item.fixturenames: - if argname not in item.funcargs: - item.funcargs[argname] = self.getfixturevalue(argname) - - def addfinalizer(self, finalizer: Callable[[], object]) -> None: - self.node.addfinalizer(finalizer) - - -@final -class SubRequest(FixtureRequest): - """The type of the ``request`` fixture in a fixture function requested - (transitively) by a test function.""" - - def __init__( - self, - request: FixtureRequest, - scope: Scope, - param: Any, - param_index: int, - fixturedef: FixtureDef[object], - *, - _ispytest: bool = False, - ) -> None: - super().__init__( - pyfuncitem=request._pyfuncitem, - fixturename=fixturedef.argname, - fixture_defs=request._fixture_defs, - arg2fixturedefs=request._arg2fixturedefs, - _ispytest=_ispytest, - ) - self._parent_request: Final[FixtureRequest] = request - self._scope_field: Final = scope - self._fixturedef: Final[FixtureDef[object]] = fixturedef - if param is not NOTSET: - self.param = param - self.param_index: Final = param_index - - def __repr__(self) -> str: - return f"" - - @property - def _scope(self) -> Scope: - return self._scope_field - - @property - def node(self): - scope = self._scope - if scope is Scope.Function: - # This might also be a non-function Item despite its attribute name. - node: nodes.Node | None = self._pyfuncitem - elif scope is Scope.Package: - node = get_scope_package(self._pyfuncitem, self._fixturedef) - else: - node = get_scope_node(self._pyfuncitem, scope) - if node is None and scope is Scope.Class: - # Fallback to function item itself. - node = self._pyfuncitem - assert node, f'Could not obtain a node for scope "{scope}" for function {self._pyfuncitem!r}' - return node - - def _check_scope( - self, - requested_fixturedef: FixtureDef[object] | PseudoFixtureDef[object], - requested_scope: Scope, - ) -> None: - if isinstance(requested_fixturedef, PseudoFixtureDef): - return - if self._scope > requested_scope: - # Try to report something helpful. - argname = requested_fixturedef.argname - fixture_stack = "\n".join( - self._format_fixturedef_line(fixturedef) - for fixturedef in self._get_fixturestack() - ) - requested_fixture = self._format_fixturedef_line(requested_fixturedef) - fail( - f"ScopeMismatch: You tried to access the {requested_scope.value} scoped " - f"fixture {argname} with a {self._scope.value} scoped request object. " - f"Requesting fixture stack:\n{fixture_stack}\n" - f"Requested fixture:\n{requested_fixture}", - pytrace=False, - ) - - def _format_fixturedef_line(self, fixturedef: FixtureDef[object]) -> str: - factory = fixturedef.func - path, lineno = getfslineno(factory) - if isinstance(path, Path): - path = bestrelpath(self._pyfuncitem.session.path, path) - signature = inspect.signature(factory) - return f"{path}:{lineno + 1}: def {factory.__name__}{signature}" - - def addfinalizer(self, finalizer: Callable[[], object]) -> None: - self._fixturedef.addfinalizer(finalizer) - - -@final -class FixtureLookupError(LookupError): - """Could not return a requested fixture (missing or invalid).""" - - def __init__( - self, argname: str | None, request: FixtureRequest, msg: str | None = None - ) -> None: - self.argname = argname - self.request = request - self.fixturestack = request._get_fixturestack() - self.msg = msg - - def formatrepr(self) -> FixtureLookupErrorRepr: - tblines: list[str] = [] - addline = tblines.append - stack = [self.request._pyfuncitem.obj] - stack.extend(map(lambda x: x.func, self.fixturestack)) - msg = self.msg - if msg is not None: - # The last fixture raise an error, let's present - # it at the requesting side. - stack = stack[:-1] - for function in stack: - fspath, lineno = getfslineno(function) - try: - lines, _ = inspect.getsourcelines(get_real_func(function)) - except (OSError, IndexError, TypeError): - error_msg = "file %s, line %s: source code not available" - addline(error_msg % (fspath, lineno + 1)) - else: - addline(f"file {fspath}, line {lineno + 1}") - for i, line in enumerate(lines): - line = line.rstrip() - addline(" " + line) - if line.lstrip().startswith("def"): - break - - if msg is None: - fm = self.request._fixturemanager - available = set() - parent = self.request._pyfuncitem.parent - assert parent is not None - for name, fixturedefs in fm._arg2fixturedefs.items(): - faclist = list(fm._matchfactories(fixturedefs, parent)) - if faclist: - available.add(name) - if self.argname in available: - msg = ( - f" recursive dependency involving fixture '{self.argname}' detected" - ) - else: - msg = f"fixture '{self.argname}' not found" - msg += "\n available fixtures: {}".format(", ".join(sorted(available))) - msg += "\n use 'pytest --fixtures [testpath]' for help on them." - - return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname) - - -class FixtureLookupErrorRepr(TerminalRepr): - def __init__( - self, - filename: str | os.PathLike[str], - firstlineno: int, - tblines: Sequence[str], - errorstring: str, - argname: str | None, - ) -> None: - self.tblines = tblines - self.errorstring = errorstring - self.filename = filename - self.firstlineno = firstlineno - self.argname = argname - - def toterminal(self, tw: TerminalWriter) -> None: - # tw.line("FixtureLookupError: %s" %(self.argname), red=True) - for tbline in self.tblines: - tw.line(tbline.rstrip()) - lines = self.errorstring.split("\n") - if lines: - tw.line( - f"{FormattedExcinfo.fail_marker} {lines[0].strip()}", - red=True, - ) - for line in lines[1:]: - tw.line( - f"{FormattedExcinfo.flow_marker} {line.strip()}", - red=True, - ) - tw.line() - tw.line("%s:%d" % (os.fspath(self.filename), self.firstlineno + 1)) - - -def call_fixture_func( - fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs -) -> FixtureValue: - if is_generator(fixturefunc): - fixturefunc = cast( - Callable[..., Generator[FixtureValue, None, None]], fixturefunc - ) - generator = fixturefunc(**kwargs) - try: - fixture_result = next(generator) - except StopIteration: - raise ValueError(f"{request.fixturename} did not yield a value") from None - finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator) - request.addfinalizer(finalizer) - else: - fixturefunc = cast(Callable[..., FixtureValue], fixturefunc) - fixture_result = fixturefunc(**kwargs) - return fixture_result - - -def _teardown_yield_fixture(fixturefunc, it) -> None: - """Execute the teardown of a fixture function by advancing the iterator - after the yield and ensure the iteration ends (if not it means there is - more than one yield in the function).""" - try: - next(it) - except StopIteration: - pass - else: - fs, lineno = getfslineno(fixturefunc) - fail( - f"fixture function has more than one 'yield':\n\n" - f"{Source(fixturefunc).indent()}\n" - f"{fs}:{lineno + 1}", - pytrace=False, - ) - - -def _eval_scope_callable( - scope_callable: Callable[[str, Config], _ScopeName], - fixture_name: str, - config: Config, -) -> _ScopeName: - try: - # Type ignored because there is no typing mechanism to specify - # keyword arguments, currently. - result = scope_callable(fixture_name=fixture_name, config=config) # type: ignore[call-arg] - except Exception as e: - raise TypeError( - f"Error evaluating {scope_callable} while defining fixture '{fixture_name}'.\n" - "Expected a function with the signature (*, fixture_name, config)" - ) from e - if not isinstance(result, str): - fail( - f"Expected {scope_callable} to return a 'str' while defining fixture '{fixture_name}', but it returned:\n" - f"{result!r}", - pytrace=False, - ) - return result - - -@final -class FixtureDef(Generic[FixtureValue]): - """A container for a fixture definition. - - Note: At this time, only explicitly documented fields and methods are - considered public stable API. - """ - - def __init__( - self, - config: Config, - baseid: str | None, - argname: str, - func: _FixtureFunc[FixtureValue], - scope: Scope | _ScopeName | Callable[[str, Config], _ScopeName] | None, - params: Sequence[object] | None, - ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None, - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - # The "base" node ID for the fixture. - # - # This is a node ID prefix. A fixture is only available to a node (e.g. - # a `Function` item) if the fixture's baseid is a nodeid of a parent of - # node. - # - # For a fixture found in a Collector's object (e.g. a `Module`s module, - # a `Class`'s class), the baseid is the Collector's nodeid. - # - # For a fixture found in a conftest plugin, the baseid is the conftest's - # directory path relative to the rootdir. - # - # For other plugins, the baseid is the empty string (always matches). - self.baseid: Final = baseid or "" - # Whether the fixture was found from a node or a conftest in the - # collection tree. Will be false for fixtures defined in non-conftest - # plugins. - self.has_location: Final = baseid is not None - # The fixture factory function. - self.func: Final = func - # The name by which the fixture may be requested. - self.argname: Final = argname - if scope is None: - scope = Scope.Function - elif callable(scope): - scope = _eval_scope_callable(scope, argname, config) - if isinstance(scope, str): - scope = Scope.from_user( - scope, descr=f"Fixture '{func.__name__}'", where=baseid - ) - self._scope: Final = scope - # If the fixture is directly parametrized, the parameter values. - self.params: Final = params - # If the fixture is directly parametrized, a tuple of explicit IDs to - # assign to the parameter values, or a callable to generate an ID given - # a parameter value. - self.ids: Final = ids - # The names requested by the fixtures. - self.argnames: Final = getfuncargnames(func, name=argname) - # If the fixture was executed, the current value of the fixture. - # Can change if the fixture is executed with different parameters. - self.cached_result: _FixtureCachedResult[FixtureValue] | None = None - self._finalizers: Final[list[Callable[[], object]]] = [] - - @property - def scope(self) -> _ScopeName: - """Scope string, one of "function", "class", "module", "package", "session".""" - return self._scope.value - - def addfinalizer(self, finalizer: Callable[[], object]) -> None: - self._finalizers.append(finalizer) - - def finish(self, request: SubRequest) -> None: - exceptions: list[BaseException] = [] - while self._finalizers: - fin = self._finalizers.pop() - try: - fin() - except BaseException as e: - exceptions.append(e) - node = request.node - node.ihook.pytest_fixture_post_finalizer(fixturedef=self, request=request) - # Even if finalization fails, we invalidate the cached fixture - # value and remove all finalizers because they may be bound methods - # which will keep instances alive. - self.cached_result = None - self._finalizers.clear() - if len(exceptions) == 1: - raise exceptions[0] - elif len(exceptions) > 1: - msg = f'errors while tearing down fixture "{self.argname}" of {node}' - raise BaseExceptionGroup(msg, exceptions[::-1]) - - def execute(self, request: SubRequest) -> FixtureValue: - """Return the value of this fixture, executing it if not cached.""" - # Ensure that the dependent fixtures requested by this fixture are loaded. - # This needs to be done before checking if we have a cached value, since - # if a dependent fixture has their cache invalidated, e.g. due to - # parametrization, they finalize themselves and fixtures depending on it - # (which will likely include this fixture) setting `self.cached_result = None`. - # See #4871 - requested_fixtures_that_should_finalize_us = [] - for argname in self.argnames: - fixturedef = request._get_active_fixturedef(argname) - # Saves requested fixtures in a list so we later can add our finalizer - # to them, ensuring that if a requested fixture gets torn down we get torn - # down first. This is generally handled by SetupState, but still currently - # needed when this fixture is not parametrized but depends on a parametrized - # fixture. - if not isinstance(fixturedef, PseudoFixtureDef): - requested_fixtures_that_should_finalize_us.append(fixturedef) - - # Check for (and return) cached value/exception. - if self.cached_result is not None: - request_cache_key = self.cache_key(request) - cache_key = self.cached_result[1] - try: - # Attempt to make a normal == check: this might fail for objects - # which do not implement the standard comparison (like numpy arrays -- #6497). - cache_hit = bool(request_cache_key == cache_key) - except (ValueError, RuntimeError): - # If the comparison raises, use 'is' as fallback. - cache_hit = request_cache_key is cache_key - - if cache_hit: - if self.cached_result[2] is not None: - exc, exc_tb = self.cached_result[2] - raise exc.with_traceback(exc_tb) - else: - result = self.cached_result[0] - return result - # We have a previous but differently parametrized fixture instance - # so we need to tear it down before creating a new one. - self.finish(request) - assert self.cached_result is None - - # Add finalizer to requested fixtures we saved previously. - # We make sure to do this after checking for cached value to avoid - # adding our finalizer multiple times. (#12135) - finalizer = functools.partial(self.finish, request=request) - for parent_fixture in requested_fixtures_that_should_finalize_us: - parent_fixture.addfinalizer(finalizer) - - ihook = request.node.ihook - try: - # Setup the fixture, run the code in it, and cache the value - # in self.cached_result - result = ihook.pytest_fixture_setup(fixturedef=self, request=request) - finally: - # schedule our finalizer, even if the setup failed - request.node.addfinalizer(finalizer) - - return result - - def cache_key(self, request: SubRequest) -> object: - return getattr(request, "param", None) - - def __repr__(self) -> str: - return f"" - - -def resolve_fixture_function( - fixturedef: FixtureDef[FixtureValue], request: FixtureRequest -) -> _FixtureFunc[FixtureValue]: - """Get the actual callable that can be called to obtain the fixture - value.""" - fixturefunc = fixturedef.func - # The fixture function needs to be bound to the actual - # request.instance so that code working with "fixturedef" behaves - # as expected. - instance = request.instance - if instance is not None: - # Handle the case where fixture is defined not in a test class, but some other class - # (for example a plugin class with a fixture), see #2270. - if hasattr(fixturefunc, "__self__") and not isinstance( - instance, - fixturefunc.__self__.__class__, - ): - return fixturefunc - fixturefunc = getimfunc(fixturedef.func) - if fixturefunc != fixturedef.func: - fixturefunc = fixturefunc.__get__(instance) - return fixturefunc - - -def pytest_fixture_setup( - fixturedef: FixtureDef[FixtureValue], request: SubRequest -) -> FixtureValue: - """Execution of fixture setup.""" - kwargs = {} - for argname in fixturedef.argnames: - kwargs[argname] = request.getfixturevalue(argname) - - fixturefunc = resolve_fixture_function(fixturedef, request) - my_cache_key = fixturedef.cache_key(request) - try: - result = call_fixture_func(fixturefunc, request, kwargs) - except TEST_OUTCOME as e: - if isinstance(e, skip.Exception): - # The test requested a fixture which caused a skip. - # Don't show the fixture as the skip location, as then the user - # wouldn't know which test skipped. - e._use_item_location = True - fixturedef.cached_result = (None, my_cache_key, (e, e.__traceback__)) - raise - fixturedef.cached_result = (result, my_cache_key, None) - return result - - -def wrap_function_to_error_out_if_called_directly( - function: FixtureFunction, - fixture_marker: FixtureFunctionMarker, -) -> FixtureFunction: - """Wrap the given fixture function so we can raise an error about it being called directly, - instead of used as an argument in a test function.""" - name = fixture_marker.name or function.__name__ - message = ( - f'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n' - "but are created automatically when test functions request them as parameters.\n" - "See https://docs.pytest.org/en/stable/explanation/fixtures.html for more information about fixtures, and\n" - "https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly about how to update your code." - ) - - @functools.wraps(function) - def result(*args, **kwargs): - fail(message, pytrace=False) - - # Keep reference to the original function in our own custom attribute so we don't unwrap - # further than this point and lose useful wrappings like @mock.patch (#3774). - result.__pytest_wrapped__ = _PytestWrapper(function) # type: ignore[attr-defined] - - return cast(FixtureFunction, result) - - -@final -@dataclasses.dataclass(frozen=True) -class FixtureFunctionMarker: - scope: _ScopeName | Callable[[str, Config], _ScopeName] - params: tuple[object, ...] | None - autouse: bool = False - ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None - name: str | None = None - - _ispytest: dataclasses.InitVar[bool] = False - - def __post_init__(self, _ispytest: bool) -> None: - check_ispytest(_ispytest) - - def __call__(self, function: FixtureFunction) -> FixtureFunction: - if inspect.isclass(function): - raise ValueError("class fixtures not supported (maybe in the future)") - - if getattr(function, "_pytestfixturefunction", False): - raise ValueError( - f"@pytest.fixture is being applied more than once to the same function {function.__name__!r}" - ) - - if hasattr(function, "pytestmark"): - warnings.warn(MARKED_FIXTURE, stacklevel=2) - - function = wrap_function_to_error_out_if_called_directly(function, self) - - name = self.name or function.__name__ - if name == "request": - location = getlocation(function) - fail( - f"'request' is a reserved word for fixtures, use another name:\n {location}", - pytrace=False, - ) - - # Type ignored because https://github.com/python/mypy/issues/2087. - function._pytestfixturefunction = self # type: ignore[attr-defined] - return function - - -@overload -def fixture( - fixture_function: FixtureFunction, - *, - scope: _ScopeName | Callable[[str, Config], _ScopeName] = ..., - params: Iterable[object] | None = ..., - autouse: bool = ..., - ids: Sequence[object | None] | Callable[[Any], object | None] | None = ..., - name: str | None = ..., -) -> FixtureFunction: ... - - -@overload -def fixture( - fixture_function: None = ..., - *, - scope: _ScopeName | Callable[[str, Config], _ScopeName] = ..., - params: Iterable[object] | None = ..., - autouse: bool = ..., - ids: Sequence[object | None] | Callable[[Any], object | None] | None = ..., - name: str | None = None, -) -> FixtureFunctionMarker: ... - - -def fixture( - fixture_function: FixtureFunction | None = None, - *, - scope: _ScopeName | Callable[[str, Config], _ScopeName] = "function", - params: Iterable[object] | None = None, - autouse: bool = False, - ids: Sequence[object | None] | Callable[[Any], object | None] | None = None, - name: str | None = None, -) -> FixtureFunctionMarker | FixtureFunction: - """Decorator to mark a fixture factory function. - - This decorator can be used, with or without parameters, to define a - fixture function. - - The name of the fixture function can later be referenced to cause its - invocation ahead of running tests: test modules or classes can use the - ``pytest.mark.usefixtures(fixturename)`` marker. - - Test functions can directly use fixture names as input arguments in which - case the fixture instance returned from the fixture function will be - injected. - - Fixtures can provide their values to test functions using ``return`` or - ``yield`` statements. When using ``yield`` the code block after the - ``yield`` statement is executed as teardown code regardless of the test - outcome, and must yield exactly once. - - :param scope: - The scope for which this fixture is shared; one of ``"function"`` - (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"``. - - This parameter may also be a callable which receives ``(fixture_name, config)`` - as parameters, and must return a ``str`` with one of the values mentioned above. - - See :ref:`dynamic scope` in the docs for more information. - - :param params: - An optional list of parameters which will cause multiple invocations - of the fixture function and all of the tests using it. The current - parameter is available in ``request.param``. - - :param autouse: - If True, the fixture func is activated for all tests that can see it. - If False (the default), an explicit reference is needed to activate - the fixture. - - :param ids: - Sequence of ids each corresponding to the params so that they are - part of the test id. If no ids are provided they will be generated - automatically from the params. - - :param name: - The name of the fixture. This defaults to the name of the decorated - function. If a fixture is used in the same module in which it is - defined, the function name of the fixture will be shadowed by the - function arg that requests the fixture; one way to resolve this is to - name the decorated function ``fixture_`` and then use - ``@pytest.fixture(name='')``. - """ - fixture_marker = FixtureFunctionMarker( - scope=scope, - params=tuple(params) if params is not None else None, - autouse=autouse, - ids=None if ids is None else ids if callable(ids) else tuple(ids), - name=name, - _ispytest=True, - ) - - # Direct decoration. - if fixture_function: - return fixture_marker(fixture_function) - - return fixture_marker - - -def yield_fixture( - fixture_function=None, - *args, - scope="function", - params=None, - autouse=False, - ids=None, - name=None, -): - """(Return a) decorator to mark a yield-fixture factory function. - - .. deprecated:: 3.0 - Use :py:func:`pytest.fixture` directly instead. - """ - warnings.warn(YIELD_FIXTURE, stacklevel=2) - return fixture( - fixture_function, - *args, - scope=scope, - params=params, - autouse=autouse, - ids=ids, - name=name, - ) - - -@fixture(scope="session") -def pytestconfig(request: FixtureRequest) -> Config: - """Session-scoped fixture that returns the session's :class:`pytest.Config` - object. - - Example:: - - def test_foo(pytestconfig): - if pytestconfig.get_verbosity() > 0: - ... - - """ - return request.config - - -def pytest_addoption(parser: Parser) -> None: - parser.addini( - "usefixtures", - type="args", - default=[], - help="List of default fixtures to be used with this project", - ) - group = parser.getgroup("general") - group.addoption( - "--fixtures", - "--funcargs", - action="store_true", - dest="showfixtures", - default=False, - help="Show available fixtures, sorted by plugin appearance " - "(fixtures with leading '_' are only shown with '-v')", - ) - group.addoption( - "--fixtures-per-test", - action="store_true", - dest="show_fixtures_per_test", - default=False, - help="Show fixtures per test", - ) - - -def pytest_cmdline_main(config: Config) -> int | ExitCode | None: - if config.option.showfixtures: - showfixtures(config) - return 0 - if config.option.show_fixtures_per_test: - show_fixtures_per_test(config) - return 0 - return None - - -def _get_direct_parametrize_args(node: nodes.Node) -> set[str]: - """Return all direct parametrization arguments of a node, so we don't - mistake them for fixtures. - - Check https://github.com/pytest-dev/pytest/issues/5036. - - These things are done later as well when dealing with parametrization - so this could be improved. - """ - parametrize_argnames: set[str] = set() - for marker in node.iter_markers(name="parametrize"): - if not marker.kwargs.get("indirect", False): - p_argnames, _ = ParameterSet._parse_parametrize_args( - *marker.args, **marker.kwargs - ) - parametrize_argnames.update(p_argnames) - return parametrize_argnames - - -def deduplicate_names(*seqs: Iterable[str]) -> tuple[str, ...]: - """De-duplicate the sequence of names while keeping the original order.""" - # Ideally we would use a set, but it does not preserve insertion order. - return tuple(dict.fromkeys(name for seq in seqs for name in seq)) - - -class FixtureManager: - """pytest fixture definitions and information is stored and managed - from this class. - - During collection fm.parsefactories() is called multiple times to parse - fixture function definitions into FixtureDef objects and internal - data structures. - - During collection of test functions, metafunc-mechanics instantiate - a FuncFixtureInfo object which is cached per node/func-name. - This FuncFixtureInfo object is later retrieved by Function nodes - which themselves offer a fixturenames attribute. - - The FuncFixtureInfo object holds information about fixtures and FixtureDefs - relevant for a particular function. An initial list of fixtures is - assembled like this: - - - ini-defined usefixtures - - autouse-marked fixtures along the collection chain up from the function - - usefixtures markers at module/class/function level - - test function funcargs - - Subsequently the funcfixtureinfo.fixturenames attribute is computed - as the closure of the fixtures needed to setup the initial fixtures, - i.e. fixtures needed by fixture functions themselves are appended - to the fixturenames list. - - Upon the test-setup phases all fixturenames are instantiated, retrieved - by a lookup of their FuncFixtureInfo. - """ - - def __init__(self, session: Session) -> None: - self.session = session - self.config: Config = session.config - # Maps a fixture name (argname) to all of the FixtureDefs in the test - # suite/plugins defined with this name. Populated by parsefactories(). - # TODO: The order of the FixtureDefs list of each arg is significant, - # explain. - self._arg2fixturedefs: Final[dict[str, list[FixtureDef[Any]]]] = {} - self._holderobjseen: Final[set[object]] = set() - # A mapping from a nodeid to a list of autouse fixtures it defines. - self._nodeid_autousenames: Final[dict[str, list[str]]] = { - "": self.config.getini("usefixtures"), - } - session.config.pluginmanager.register(self, "funcmanage") - - def getfixtureinfo( - self, - node: nodes.Item, - func: Callable[..., object] | None, - cls: type | None, - ) -> FuncFixtureInfo: - """Calculate the :class:`FuncFixtureInfo` for an item. - - If ``func`` is None, or if the item sets an attribute - ``nofuncargs = True``, then ``func`` is not examined at all. - - :param node: - The item requesting the fixtures. - :param func: - The item's function. - :param cls: - If the function is a method, the method's class. - """ - if func is not None and not getattr(node, "nofuncargs", False): - argnames = getfuncargnames(func, name=node.name, cls=cls) - else: - argnames = () - usefixturesnames = self._getusefixturesnames(node) - autousenames = self._getautousenames(node) - initialnames = deduplicate_names(autousenames, usefixturesnames, argnames) - - direct_parametrize_args = _get_direct_parametrize_args(node) - - names_closure, arg2fixturedefs = self.getfixtureclosure( - parentnode=node, - initialnames=initialnames, - ignore_args=direct_parametrize_args, - ) - - return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs) - - def pytest_plugin_registered(self, plugin: _PluggyPlugin, plugin_name: str) -> None: - # Fixtures defined in conftest plugins are only visible to within the - # conftest's directory. This is unlike fixtures in non-conftest plugins - # which have global visibility. So for conftests, construct the base - # nodeid from the plugin name (which is the conftest path). - if plugin_name and plugin_name.endswith("conftest.py"): - # Note: we explicitly do *not* use `plugin.__file__` here -- The - # difference is that plugin_name has the correct capitalization on - # case-insensitive systems (Windows) and other normalization issues - # (issue #11816). - conftestpath = absolutepath(plugin_name) - try: - nodeid = str(conftestpath.parent.relative_to(self.config.rootpath)) - except ValueError: - nodeid = "" - if nodeid == ".": - nodeid = "" - if os.sep != nodes.SEP: - nodeid = nodeid.replace(os.sep, nodes.SEP) - else: - nodeid = None - - self.parsefactories(plugin, nodeid) - - def _getautousenames(self, node: nodes.Node) -> Iterator[str]: - """Return the names of autouse fixtures applicable to node.""" - for parentnode in node.listchain(): - basenames = self._nodeid_autousenames.get(parentnode.nodeid) - if basenames: - yield from basenames - - def _getusefixturesnames(self, node: nodes.Item) -> Iterator[str]: - """Return the names of usefixtures fixtures applicable to node.""" - for mark in node.iter_markers(name="usefixtures"): - yield from mark.args - - def getfixtureclosure( - self, - parentnode: nodes.Node, - initialnames: tuple[str, ...], - ignore_args: AbstractSet[str], - ) -> tuple[list[str], dict[str, Sequence[FixtureDef[Any]]]]: - # Collect the closure of all fixtures, starting with the given - # fixturenames as the initial set. As we have to visit all - # factory definitions anyway, we also return an arg2fixturedefs - # mapping so that the caller can reuse it and does not have - # to re-discover fixturedefs again for each fixturename - # (discovering matching fixtures for a given name/node is expensive). - - fixturenames_closure = list(initialnames) - - arg2fixturedefs: dict[str, Sequence[FixtureDef[Any]]] = {} - lastlen = -1 - while lastlen != len(fixturenames_closure): - lastlen = len(fixturenames_closure) - for argname in fixturenames_closure: - if argname in ignore_args: - continue - if argname in arg2fixturedefs: - continue - fixturedefs = self.getfixturedefs(argname, parentnode) - if fixturedefs: - arg2fixturedefs[argname] = fixturedefs - for arg in fixturedefs[-1].argnames: - if arg not in fixturenames_closure: - fixturenames_closure.append(arg) - - def sort_by_scope(arg_name: str) -> Scope: - try: - fixturedefs = arg2fixturedefs[arg_name] - except KeyError: - return Scope.Function - else: - return fixturedefs[-1]._scope - - fixturenames_closure.sort(key=sort_by_scope, reverse=True) - return fixturenames_closure, arg2fixturedefs - - def pytest_generate_tests(self, metafunc: Metafunc) -> None: - """Generate new tests based on parametrized fixtures used by the given metafunc""" - - def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]: - args, _ = ParameterSet._parse_parametrize_args(*mark.args, **mark.kwargs) - return args - - for argname in metafunc.fixturenames: - # Get the FixtureDefs for the argname. - fixture_defs = metafunc._arg2fixturedefs.get(argname) - if not fixture_defs: - # Will raise FixtureLookupError at setup time if not parametrized somewhere - # else (e.g @pytest.mark.parametrize) - continue - - # If the test itself parametrizes using this argname, give it - # precedence. - if any( - argname in get_parametrize_mark_argnames(mark) - for mark in metafunc.definition.iter_markers("parametrize") - ): - continue - - # In the common case we only look at the fixture def with the - # closest scope (last in the list). But if the fixture overrides - # another fixture, while requesting the super fixture, keep going - # in case the super fixture is parametrized (#1953). - for fixturedef in reversed(fixture_defs): - # Fixture is parametrized, apply it and stop. - if fixturedef.params is not None: - metafunc.parametrize( - argname, - fixturedef.params, - indirect=True, - scope=fixturedef.scope, - ids=fixturedef.ids, - ) - break - - # Not requesting the overridden super fixture, stop. - if argname not in fixturedef.argnames: - break - - # Try next super fixture, if any. - - def pytest_collection_modifyitems(self, items: list[nodes.Item]) -> None: - # Separate parametrized setups. - items[:] = reorder_items(items) - - def _register_fixture( - self, - *, - name: str, - func: _FixtureFunc[object], - nodeid: str | None, - scope: Scope | _ScopeName | Callable[[str, Config], _ScopeName] = "function", - params: Sequence[object] | None = None, - ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None, - autouse: bool = False, - ) -> None: - """Register a fixture - - :param name: - The fixture's name. - :param func: - The fixture's implementation function. - :param nodeid: - The visibility of the fixture. The fixture will be available to the - node with this nodeid and its children in the collection tree. - None means that the fixture is visible to the entire collection tree, - e.g. a fixture defined for general use in a plugin. - :param scope: - The fixture's scope. - :param params: - The fixture's parametrization params. - :param ids: - The fixture's IDs. - :param autouse: - Whether this is an autouse fixture. - """ - fixture_def = FixtureDef( - config=self.config, - baseid=nodeid, - argname=name, - func=func, - scope=scope, - params=params, - ids=ids, - _ispytest=True, - ) - - faclist = self._arg2fixturedefs.setdefault(name, []) - if fixture_def.has_location: - faclist.append(fixture_def) - else: - # fixturedefs with no location are at the front - # so this inserts the current fixturedef after the - # existing fixturedefs from external plugins but - # before the fixturedefs provided in conftests. - i = len([f for f in faclist if not f.has_location]) - faclist.insert(i, fixture_def) - if autouse: - self._nodeid_autousenames.setdefault(nodeid or "", []).append(name) - - @overload - def parsefactories( - self, - node_or_obj: nodes.Node, - ) -> None: - raise NotImplementedError() - - @overload - def parsefactories( - self, - node_or_obj: object, - nodeid: str | None, - ) -> None: - raise NotImplementedError() - - def parsefactories( - self, - node_or_obj: nodes.Node | object, - nodeid: str | NotSetType | None = NOTSET, - ) -> None: - """Collect fixtures from a collection node or object. - - Found fixtures are parsed into `FixtureDef`s and saved. - - If `node_or_object` is a collection node (with an underlying Python - object), the node's object is traversed and the node's nodeid is used to - determine the fixtures' visibility. `nodeid` must not be specified in - this case. - - If `node_or_object` is an object (e.g. a plugin), the object is - traversed and the given `nodeid` is used to determine the fixtures' - visibility. `nodeid` must be specified in this case; None and "" mean - total visibility. - """ - if nodeid is not NOTSET: - holderobj = node_or_obj - else: - assert isinstance(node_or_obj, nodes.Node) - holderobj = cast(object, node_or_obj.obj) # type: ignore[attr-defined] - assert isinstance(node_or_obj.nodeid, str) - nodeid = node_or_obj.nodeid - if holderobj in self._holderobjseen: - return - - # Avoid accessing `@property` (and other descriptors) when iterating fixtures. - if not safe_isclass(holderobj) and not isinstance(holderobj, types.ModuleType): - holderobj_tp: object = type(holderobj) - else: - holderobj_tp = holderobj - - self._holderobjseen.add(holderobj) - for name in dir(holderobj): - # The attribute can be an arbitrary descriptor, so the attribute - # access below can raise. safe_getattr() ignores such exceptions. - obj_ub = safe_getattr(holderobj_tp, name, None) - marker = getfixturemarker(obj_ub) - if not isinstance(marker, FixtureFunctionMarker): - # Magic globals with __getattr__ might have got us a wrong - # fixture attribute. - continue - - # OK we know it is a fixture -- now safe to look up on the _instance_. - obj = getattr(holderobj, name) - - if marker.name: - name = marker.name - - # During fixture definition we wrap the original fixture function - # to issue a warning if called directly, so here we unwrap it in - # order to not emit the warning when pytest itself calls the - # fixture function. - func = get_real_method(obj, holderobj) - - self._register_fixture( - name=name, - nodeid=nodeid, - func=func, - scope=marker.scope, - params=marker.params, - ids=marker.ids, - autouse=marker.autouse, - ) - - def getfixturedefs( - self, argname: str, node: nodes.Node - ) -> Sequence[FixtureDef[Any]] | None: - """Get FixtureDefs for a fixture name which are applicable - to a given node. - - Returns None if there are no fixtures at all defined with the given - name. (This is different from the case in which there are fixtures - with the given name, but none applicable to the node. In this case, - an empty result is returned). - - :param argname: Name of the fixture to search for. - :param node: The requesting Node. - """ - try: - fixturedefs = self._arg2fixturedefs[argname] - except KeyError: - return None - return tuple(self._matchfactories(fixturedefs, node)) - - def _matchfactories( - self, fixturedefs: Iterable[FixtureDef[Any]], node: nodes.Node - ) -> Iterator[FixtureDef[Any]]: - parentnodeids = {n.nodeid for n in node.iter_parents()} - for fixturedef in fixturedefs: - if fixturedef.baseid in parentnodeids: - yield fixturedef - - -def show_fixtures_per_test(config: Config) -> int | ExitCode: - from _pytest.main import wrap_session - - return wrap_session(config, _show_fixtures_per_test) - - -_PYTEST_DIR = Path(_pytest.__file__).parent - - -def _pretty_fixture_path(invocation_dir: Path, func) -> str: - loc = Path(getlocation(func, invocation_dir)) - prefix = Path("...", "_pytest") - try: - return str(prefix / loc.relative_to(_PYTEST_DIR)) - except ValueError: - return bestrelpath(invocation_dir, loc) - - -def _show_fixtures_per_test(config: Config, session: Session) -> None: - import _pytest.config - - session.perform_collect() - invocation_dir = config.invocation_params.dir - tw = _pytest.config.create_terminal_writer(config) - verbose = config.get_verbosity() - - def get_best_relpath(func) -> str: - loc = getlocation(func, invocation_dir) - return bestrelpath(invocation_dir, Path(loc)) - - def write_fixture(fixture_def: FixtureDef[object]) -> None: - argname = fixture_def.argname - if verbose <= 0 and argname.startswith("_"): - return - prettypath = _pretty_fixture_path(invocation_dir, fixture_def.func) - tw.write(f"{argname}", green=True) - tw.write(f" -- {prettypath}", yellow=True) - tw.write("\n") - fixture_doc = inspect.getdoc(fixture_def.func) - if fixture_doc: - write_docstring( - tw, - fixture_doc.split("\n\n", maxsplit=1)[0] - if verbose <= 0 - else fixture_doc, - ) - else: - tw.line(" no docstring available", red=True) - - def write_item(item: nodes.Item) -> None: - # Not all items have _fixtureinfo attribute. - info: FuncFixtureInfo | None = getattr(item, "_fixtureinfo", None) - if info is None or not info.name2fixturedefs: - # This test item does not use any fixtures. - return - tw.line() - tw.sep("-", f"fixtures used by {item.name}") - # TODO: Fix this type ignore. - tw.sep("-", f"({get_best_relpath(item.function)})") # type: ignore[attr-defined] - # dict key not used in loop but needed for sorting. - for _, fixturedefs in sorted(info.name2fixturedefs.items()): - assert fixturedefs is not None - if not fixturedefs: - continue - # Last item is expected to be the one used by the test item. - write_fixture(fixturedefs[-1]) - - for session_item in session.items: - write_item(session_item) - - -def showfixtures(config: Config) -> int | ExitCode: - from _pytest.main import wrap_session - - return wrap_session(config, _showfixtures_main) - - -def _showfixtures_main(config: Config, session: Session) -> None: - import _pytest.config - - session.perform_collect() - invocation_dir = config.invocation_params.dir - tw = _pytest.config.create_terminal_writer(config) - verbose = config.get_verbosity() - - fm = session._fixturemanager - - available = [] - seen: set[tuple[str, str]] = set() - - for argname, fixturedefs in fm._arg2fixturedefs.items(): - assert fixturedefs is not None - if not fixturedefs: - continue - for fixturedef in fixturedefs: - loc = getlocation(fixturedef.func, invocation_dir) - if (fixturedef.argname, loc) in seen: - continue - seen.add((fixturedef.argname, loc)) - available.append( - ( - len(fixturedef.baseid), - fixturedef.func.__module__, - _pretty_fixture_path(invocation_dir, fixturedef.func), - fixturedef.argname, - fixturedef, - ) - ) - - available.sort() - currentmodule = None - for baseid, module, prettypath, argname, fixturedef in available: - if currentmodule != module: - if not module.startswith("_pytest."): - tw.line() - tw.sep("-", f"fixtures defined from {module}") - currentmodule = module - if verbose <= 0 and argname.startswith("_"): - continue - tw.write(f"{argname}", green=True) - if fixturedef.scope != "function": - tw.write(f" [{fixturedef.scope} scope]", cyan=True) - tw.write(f" -- {prettypath}", yellow=True) - tw.write("\n") - doc = inspect.getdoc(fixturedef.func) - if doc: - write_docstring( - tw, doc.split("\n\n", maxsplit=1)[0] if verbose <= 0 else doc - ) - else: - tw.line(" no docstring available", red=True) - tw.line() - - -def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None: - for line in doc.split("\n"): - tw.line(indent + line) diff --git a/.venv/lib/python3.12/site-packages/_pytest/freeze_support.py b/.venv/lib/python3.12/site-packages/_pytest/freeze_support.py deleted file mode 100644 index 2ba6f9b8..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/freeze_support.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Provides a function to report all internal modules for using freezing -tools.""" - -from __future__ import annotations - -import types -from typing import Iterator - - -def freeze_includes() -> list[str]: - """Return a list of module names used by pytest that should be - included by cx_freeze.""" - import _pytest - - result = list(_iter_all_modules(_pytest)) - return result - - -def _iter_all_modules( - package: str | types.ModuleType, - prefix: str = "", -) -> Iterator[str]: - """Iterate over the names of all modules that can be found in the given - package, recursively. - - >>> import _pytest - >>> list(_iter_all_modules(_pytest)) - ['_pytest._argcomplete', '_pytest._code.code', ...] - """ - import os - import pkgutil - - if isinstance(package, str): - path = package - else: - # Type ignored because typeshed doesn't define ModuleType.__path__ - # (only defined on packages). - package_path = package.__path__ - path, prefix = package_path[0], package.__name__ + "." - for _, name, is_package in pkgutil.iter_modules([path]): - if is_package: - for m in _iter_all_modules(os.path.join(path, name), prefix=name + "."): - yield prefix + m - else: - yield prefix + name diff --git a/.venv/lib/python3.12/site-packages/_pytest/helpconfig.py b/.venv/lib/python3.12/site-packages/_pytest/helpconfig.py deleted file mode 100644 index 1886d5c9..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/helpconfig.py +++ /dev/null @@ -1,276 +0,0 @@ -# mypy: allow-untyped-defs -"""Version info, help messages, tracing configuration.""" - -from __future__ import annotations - -from argparse import Action -import os -import sys -from typing import Generator - -from _pytest.config import Config -from _pytest.config import ExitCode -from _pytest.config import PrintHelp -from _pytest.config.argparsing import Parser -from _pytest.terminal import TerminalReporter -import pytest - - -class HelpAction(Action): - """An argparse Action that will raise an exception in order to skip the - rest of the argument parsing when --help is passed. - - This prevents argparse from quitting due to missing required arguments - when any are defined, for example by ``pytest_addoption``. - This is similar to the way that the builtin argparse --help option is - implemented by raising SystemExit. - """ - - def __init__(self, option_strings, dest=None, default=False, help=None): - super().__init__( - option_strings=option_strings, - dest=dest, - const=True, - default=default, - nargs=0, - help=help, - ) - - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, self.const) - - # We should only skip the rest of the parsing after preparse is done. - if getattr(parser._parser, "after_preparse", False): - raise PrintHelp - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("debugconfig") - group.addoption( - "--version", - "-V", - action="count", - default=0, - dest="version", - help="Display pytest version and information about plugins. " - "When given twice, also display information about plugins.", - ) - group._addoption( - "-h", - "--help", - action=HelpAction, - dest="help", - help="Show help message and configuration info", - ) - group._addoption( - "-p", - action="append", - dest="plugins", - default=[], - metavar="name", - help="Early-load given plugin module name or entry point (multi-allowed). " - "To avoid loading of plugins, use the `no:` prefix, e.g. " - "`no:doctest`.", - ) - group.addoption( - "--traceconfig", - "--trace-config", - action="store_true", - default=False, - help="Trace considerations of conftest.py files", - ) - group.addoption( - "--debug", - action="store", - nargs="?", - const="pytestdebug.log", - dest="debug", - metavar="DEBUG_FILE_NAME", - help="Store internal tracing debug information in this log file. " - "This file is opened with 'w' and truncated as a result, care advised. " - "Default: pytestdebug.log.", - ) - group._addoption( - "-o", - "--override-ini", - dest="override_ini", - action="append", - help='Override ini option with "option=value" style, ' - "e.g. `-o xfail_strict=True -o cache_dir=cache`.", - ) - - -@pytest.hookimpl(wrapper=True) -def pytest_cmdline_parse() -> Generator[None, Config, Config]: - config = yield - - if config.option.debug: - # --debug | --debug was provided. - path = config.option.debug - debugfile = open(path, "w", encoding="utf-8") - debugfile.write( - "versions pytest-{}, " - "python-{}\ninvocation_dir={}\ncwd={}\nargs={}\n\n".format( - pytest.__version__, - ".".join(map(str, sys.version_info)), - config.invocation_params.dir, - os.getcwd(), - config.invocation_params.args, - ) - ) - config.trace.root.setwriter(debugfile.write) - undo_tracing = config.pluginmanager.enable_tracing() - sys.stderr.write(f"writing pytest debug information to {path}\n") - - def unset_tracing() -> None: - debugfile.close() - sys.stderr.write(f"wrote pytest debug information to {debugfile.name}\n") - config.trace.root.setwriter(None) - undo_tracing() - - config.add_cleanup(unset_tracing) - - return config - - -def showversion(config: Config) -> None: - if config.option.version > 1: - sys.stdout.write( - f"This is pytest version {pytest.__version__}, imported from {pytest.__file__}\n" - ) - plugininfo = getpluginversioninfo(config) - if plugininfo: - for line in plugininfo: - sys.stdout.write(line + "\n") - else: - sys.stdout.write(f"pytest {pytest.__version__}\n") - - -def pytest_cmdline_main(config: Config) -> int | ExitCode | None: - if config.option.version > 0: - showversion(config) - return 0 - elif config.option.help: - config._do_configure() - showhelp(config) - config._ensure_unconfigure() - return 0 - return None - - -def showhelp(config: Config) -> None: - import textwrap - - reporter: TerminalReporter | None = config.pluginmanager.get_plugin( - "terminalreporter" - ) - assert reporter is not None - tw = reporter._tw - tw.write(config._parser.optparser.format_help()) - tw.line() - tw.line( - "[pytest] ini-options in the first " - "pytest.ini|tox.ini|setup.cfg|pyproject.toml file found:" - ) - tw.line() - - columns = tw.fullwidth # costly call - indent_len = 24 # based on argparse's max_help_position=24 - indent = " " * indent_len - for name in config._parser._ininames: - help, type, default = config._parser._inidict[name] - if type is None: - type = "string" - if help is None: - raise TypeError(f"help argument cannot be None for {name}") - spec = f"{name} ({type}):" - tw.write(f" {spec}") - spec_len = len(spec) - if spec_len > (indent_len - 3): - # Display help starting at a new line. - tw.line() - helplines = textwrap.wrap( - help, - columns, - initial_indent=indent, - subsequent_indent=indent, - break_on_hyphens=False, - ) - - for line in helplines: - tw.line(line) - else: - # Display help starting after the spec, following lines indented. - tw.write(" " * (indent_len - spec_len - 2)) - wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False) - - if wrapped: - tw.line(wrapped[0]) - for line in wrapped[1:]: - tw.line(indent + line) - - tw.line() - tw.line("Environment variables:") - vars = [ - ( - "CI", - "When set (regardless of value), pytest knows it is running in a " - "CI process and does not truncate summary info", - ), - ("BUILD_NUMBER", "Equivalent to CI"), - ("PYTEST_ADDOPTS", "Extra command line options"), - ("PYTEST_PLUGINS", "Comma-separated plugins to load during startup"), - ("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "Set to disable plugin auto-loading"), - ("PYTEST_DEBUG", "Set to enable debug tracing of pytest's internals"), - ] - for name, help in vars: - tw.line(f" {name:<24} {help}") - tw.line() - tw.line() - - tw.line("to see available markers type: pytest --markers") - tw.line("to see available fixtures type: pytest --fixtures") - tw.line( - "(shown according to specified file_or_dir or current dir " - "if not specified; fixtures with leading '_' are only shown " - "with the '-v' option" - ) - - for warningreport in reporter.stats.get("warnings", []): - tw.line("warning : " + warningreport.message, red=True) - - -conftest_options = [("pytest_plugins", "list of plugin names to load")] - - -def getpluginversioninfo(config: Config) -> list[str]: - lines = [] - plugininfo = config.pluginmanager.list_plugin_distinfo() - if plugininfo: - lines.append("registered third-party plugins:") - for plugin, dist in plugininfo: - loc = getattr(plugin, "__file__", repr(plugin)) - content = f"{dist.project_name}-{dist.version} at {loc}" - lines.append(" " + content) - return lines - - -def pytest_report_header(config: Config) -> list[str]: - lines = [] - if config.option.debug or config.option.traceconfig: - lines.append(f"using: pytest-{pytest.__version__}") - - verinfo = getpluginversioninfo(config) - if verinfo: - lines.extend(verinfo) - - if config.option.traceconfig: - lines.append("active plugins:") - items = config.pluginmanager.list_name_plugin() - for name, plugin in items: - if hasattr(plugin, "__file__"): - r = plugin.__file__ - else: - r = repr(plugin) - lines.append(f" {name:<20}: {r}") - return lines diff --git a/.venv/lib/python3.12/site-packages/_pytest/hookspec.py b/.venv/lib/python3.12/site-packages/_pytest/hookspec.py deleted file mode 100644 index 0a41b0ac..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/hookspec.py +++ /dev/null @@ -1,1333 +0,0 @@ -# mypy: allow-untyped-defs -# ruff: noqa: T100 -"""Hook specifications for pytest plugins which are invoked by pytest itself -and by builtin plugins.""" - -from __future__ import annotations - -from pathlib import Path -from typing import Any -from typing import Mapping -from typing import Sequence -from typing import TYPE_CHECKING - -from pluggy import HookspecMarker - -from .deprecated import HOOK_LEGACY_PATH_ARG - - -if TYPE_CHECKING: - import pdb - from typing import Literal - import warnings - - from _pytest._code.code import ExceptionInfo - from _pytest._code.code import ExceptionRepr - from _pytest.compat import LEGACY_PATH - from _pytest.config import _PluggyPlugin - from _pytest.config import Config - from _pytest.config import ExitCode - from _pytest.config import PytestPluginManager - from _pytest.config.argparsing import Parser - from _pytest.fixtures import FixtureDef - from _pytest.fixtures import SubRequest - from _pytest.main import Session - from _pytest.nodes import Collector - from _pytest.nodes import Item - from _pytest.outcomes import Exit - from _pytest.python import Class - from _pytest.python import Function - from _pytest.python import Metafunc - from _pytest.python import Module - from _pytest.reports import CollectReport - from _pytest.reports import TestReport - from _pytest.runner import CallInfo - from _pytest.terminal import TerminalReporter - from _pytest.terminal import TestShortLogReport - - -hookspec = HookspecMarker("pytest") - -# ------------------------------------------------------------------------- -# Initialization hooks called for every plugin -# ------------------------------------------------------------------------- - - -@hookspec(historic=True) -def pytest_addhooks(pluginmanager: PytestPluginManager) -> None: - """Called at plugin registration time to allow adding new hooks via a call to - :func:`pluginmanager.add_hookspecs(module_or_class, prefix) `. - - :param pluginmanager: The pytest plugin manager. - - .. note:: - This hook is incompatible with hook wrappers. - - Use in conftest plugins - ======================= - - If a conftest plugin implements this hook, it will be called immediately - when the conftest is registered. - """ - - -@hookspec(historic=True) -def pytest_plugin_registered( - plugin: _PluggyPlugin, - plugin_name: str, - manager: PytestPluginManager, -) -> None: - """A new pytest plugin got registered. - - :param plugin: The plugin module or instance. - :param plugin_name: The name by which the plugin is registered. - :param manager: The pytest plugin manager. - - .. note:: - This hook is incompatible with hook wrappers. - - Use in conftest plugins - ======================= - - If a conftest plugin implements this hook, it will be called immediately - when the conftest is registered, once for each plugin registered thus far - (including itself!), and for all plugins thereafter when they are - registered. - """ - - -@hookspec(historic=True) -def pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> None: - """Register argparse-style options and ini-style config values, - called once at the beginning of a test run. - - :param parser: - To add command line options, call - :py:func:`parser.addoption(...) `. - To add ini-file values call :py:func:`parser.addini(...) - `. - - :param pluginmanager: - The pytest plugin manager, which can be used to install :py:func:`~pytest.hookspec`'s - or :py:func:`~pytest.hookimpl`'s and allow one plugin to call another plugin's hooks - to change how command line options are added. - - Options can later be accessed through the - :py:class:`config ` object, respectively: - - - :py:func:`config.getoption(name) ` to - retrieve the value of a command line option. - - - :py:func:`config.getini(name) ` to retrieve - a value read from an ini-style file. - - The config object is passed around on many internal objects via the ``.config`` - attribute or can be retrieved as the ``pytestconfig`` fixture. - - .. note:: - This hook is incompatible with hook wrappers. - - Use in conftest plugins - ======================= - - If a conftest plugin implements this hook, it will be called immediately - when the conftest is registered. - - This hook is only called for :ref:`initial conftests `. - """ - - -@hookspec(historic=True) -def pytest_configure(config: Config) -> None: - """Allow plugins and conftest files to perform initial configuration. - - .. note:: - This hook is incompatible with hook wrappers. - - :param config: The pytest config object. - - Use in conftest plugins - ======================= - - This hook is called for every :ref:`initial conftest ` file - after command line options have been parsed. After that, the hook is called - for other conftest files as they are registered. - """ - - -# ------------------------------------------------------------------------- -# Bootstrapping hooks called for plugins registered early enough: -# internal and 3rd party plugins. -# ------------------------------------------------------------------------- - - -@hookspec(firstresult=True) -def pytest_cmdline_parse( - pluginmanager: PytestPluginManager, args: list[str] -) -> Config | None: - """Return an initialized :class:`~pytest.Config`, parsing the specified args. - - Stops at first non-None result, see :ref:`firstresult`. - - .. note:: - This hook is only called for plugin classes passed to the - ``plugins`` arg when using `pytest.main`_ to perform an in-process - test run. - - :param pluginmanager: The pytest plugin manager. - :param args: List of arguments passed on the command line. - :returns: A pytest config object. - - Use in conftest plugins - ======================= - - This hook is not called for conftest files. - """ - - -def pytest_load_initial_conftests( - early_config: Config, parser: Parser, args: list[str] -) -> None: - """Called to implement the loading of :ref:`initial conftest files - ` ahead of command line option parsing. - - :param early_config: The pytest config object. - :param args: Arguments passed on the command line. - :param parser: To add command line options. - - Use in conftest plugins - ======================= - - This hook is not called for conftest files. - """ - - -@hookspec(firstresult=True) -def pytest_cmdline_main(config: Config) -> ExitCode | int | None: - """Called for performing the main command line action. - - The default implementation will invoke the configure hooks and - :hook:`pytest_runtestloop`. - - Stops at first non-None result, see :ref:`firstresult`. - - :param config: The pytest config object. - :returns: The exit code. - - Use in conftest plugins - ======================= - - This hook is only called for :ref:`initial conftests `. - """ - - -# ------------------------------------------------------------------------- -# collection hooks -# ------------------------------------------------------------------------- - - -@hookspec(firstresult=True) -def pytest_collection(session: Session) -> object | None: - """Perform the collection phase for the given session. - - Stops at first non-None result, see :ref:`firstresult`. - The return value is not used, but only stops further processing. - - The default collection phase is this (see individual hooks for full details): - - 1. Starting from ``session`` as the initial collector: - - 1. ``pytest_collectstart(collector)`` - 2. ``report = pytest_make_collect_report(collector)`` - 3. ``pytest_exception_interact(collector, call, report)`` if an interactive exception occurred - 4. For each collected node: - - 1. If an item, ``pytest_itemcollected(item)`` - 2. If a collector, recurse into it. - - 5. ``pytest_collectreport(report)`` - - 2. ``pytest_collection_modifyitems(session, config, items)`` - - 1. ``pytest_deselected(items)`` for any deselected items (may be called multiple times) - - 3. ``pytest_collection_finish(session)`` - 4. Set ``session.items`` to the list of collected items - 5. Set ``session.testscollected`` to the number of collected items - - You can implement this hook to only perform some action before collection, - for example the terminal plugin uses it to start displaying the collection - counter (and returns `None`). - - :param session: The pytest session object. - - Use in conftest plugins - ======================= - - This hook is only called for :ref:`initial conftests `. - """ - - -def pytest_collection_modifyitems( - session: Session, config: Config, items: list[Item] -) -> None: - """Called after collection has been performed. May filter or re-order - the items in-place. - - When items are deselected (filtered out from ``items``), - the hook :hook:`pytest_deselected` must be called explicitly - with the deselected items to properly notify other plugins, - e.g. with ``config.hook.pytest_deselected(deselected_items)``. - - :param session: The pytest session object. - :param config: The pytest config object. - :param items: List of item objects. - - Use in conftest plugins - ======================= - - Any conftest plugin can implement this hook. - """ - - -def pytest_collection_finish(session: Session) -> None: - """Called after collection has been performed and modified. - - :param session: The pytest session object. - - Use in conftest plugins - ======================= - - Any conftest plugin can implement this hook. - """ - - -@hookspec( - firstresult=True, - warn_on_impl_args={ - "path": HOOK_LEGACY_PATH_ARG.format( - pylib_path_arg="path", pathlib_path_arg="collection_path" - ), - }, -) -def pytest_ignore_collect( - collection_path: Path, path: LEGACY_PATH, config: Config -) -> bool | None: - """Return ``True`` to ignore this path for collection. - - Return ``None`` to let other plugins ignore the path for collection. - - Returning ``False`` will forcefully *not* ignore this path for collection, - without giving a chance for other plugins to ignore this path. - - This hook is consulted for all files and directories prior to calling - more specific hooks. - - Stops at first non-None result, see :ref:`firstresult`. - - :param collection_path: The path to analyze. - :type collection_path: pathlib.Path - :param path: The path to analyze (deprecated). - :param config: The pytest config object. - - .. versionchanged:: 7.0.0 - The ``collection_path`` parameter was added as a :class:`pathlib.Path` - equivalent of the ``path`` parameter. The ``path`` parameter - has been deprecated. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given collection path, only - conftest files in parent directories of the collection path are consulted - (if the path is a directory, its own conftest file is *not* consulted - a - directory cannot ignore itself!). - """ - - -@hookspec(firstresult=True) -def pytest_collect_directory(path: Path, parent: Collector) -> Collector | None: - """Create a :class:`~pytest.Collector` for the given directory, or None if - not relevant. - - .. versionadded:: 8.0 - - For best results, the returned collector should be a subclass of - :class:`~pytest.Directory`, but this is not required. - - The new node needs to have the specified ``parent`` as a parent. - - Stops at first non-None result, see :ref:`firstresult`. - - :param path: The path to analyze. - :type path: pathlib.Path - - See :ref:`custom directory collectors` for a simple example of use of this - hook. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given collection path, only - conftest files in parent directories of the collection path are consulted - (if the path is a directory, its own conftest file is *not* consulted - a - directory cannot collect itself!). - """ - - -@hookspec( - warn_on_impl_args={ - "path": HOOK_LEGACY_PATH_ARG.format( - pylib_path_arg="path", pathlib_path_arg="file_path" - ), - }, -) -def pytest_collect_file( - file_path: Path, path: LEGACY_PATH, parent: Collector -) -> Collector | None: - """Create a :class:`~pytest.Collector` for the given path, or None if not relevant. - - For best results, the returned collector should be a subclass of - :class:`~pytest.File`, but this is not required. - - The new node needs to have the specified ``parent`` as a parent. - - :param file_path: The path to analyze. - :type file_path: pathlib.Path - :param path: The path to collect (deprecated). - - .. versionchanged:: 7.0.0 - The ``file_path`` parameter was added as a :class:`pathlib.Path` - equivalent of the ``path`` parameter. The ``path`` parameter - has been deprecated. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given file path, only - conftest files in parent directories of the file path are consulted. - """ - - -# logging hooks for collection - - -def pytest_collectstart(collector: Collector) -> None: - """Collector starts collecting. - - :param collector: - The collector. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given collector, only - conftest files in the collector's directory and its parent directories are - consulted. - """ - - -def pytest_itemcollected(item: Item) -> None: - """We just collected a test item. - - :param item: - The item. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in the item's directory and its parent directories are consulted. - """ - - -def pytest_collectreport(report: CollectReport) -> None: - """Collector finished collecting. - - :param report: - The collect report. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given collector, only - conftest files in the collector's directory and its parent directories are - consulted. - """ - - -def pytest_deselected(items: Sequence[Item]) -> None: - """Called for deselected test items, e.g. by keyword. - - Note that this hook has two integration aspects for plugins: - - - it can be *implemented* to be notified of deselected items - - it must be *called* from :hook:`pytest_collection_modifyitems` - implementations when items are deselected (to properly notify other plugins). - - May be called multiple times. - - :param items: - The items. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. - """ - - -@hookspec(firstresult=True) -def pytest_make_collect_report(collector: Collector) -> CollectReport | None: - """Perform :func:`collector.collect() ` and return - a :class:`~pytest.CollectReport`. - - Stops at first non-None result, see :ref:`firstresult`. - - :param collector: - The collector. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given collector, only - conftest files in the collector's directory and its parent directories are - consulted. - """ - - -# ------------------------------------------------------------------------- -# Python test function related hooks -# ------------------------------------------------------------------------- - - -@hookspec( - firstresult=True, - warn_on_impl_args={ - "path": HOOK_LEGACY_PATH_ARG.format( - pylib_path_arg="path", pathlib_path_arg="module_path" - ), - }, -) -def pytest_pycollect_makemodule( - module_path: Path, path: LEGACY_PATH, parent -) -> Module | None: - """Return a :class:`pytest.Module` collector or None for the given path. - - This hook will be called for each matching test module path. - The :hook:`pytest_collect_file` hook needs to be used if you want to - create test modules for files that do not match as a test module. - - Stops at first non-None result, see :ref:`firstresult`. - - :param module_path: The path of the module to collect. - :type module_path: pathlib.Path - :param path: The path of the module to collect (deprecated). - - .. versionchanged:: 7.0.0 - The ``module_path`` parameter was added as a :class:`pathlib.Path` - equivalent of the ``path`` parameter. - - The ``path`` parameter has been deprecated in favor of ``fspath``. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given parent collector, - only conftest files in the collector's directory and its parent directories - are consulted. - """ - - -@hookspec(firstresult=True) -def pytest_pycollect_makeitem( - collector: Module | Class, name: str, obj: object -) -> None | Item | Collector | list[Item | Collector]: - """Return a custom item/collector for a Python object in a module, or None. - - Stops at first non-None result, see :ref:`firstresult`. - - :param collector: - The module/class collector. - :param name: - The name of the object in the module/class. - :param obj: - The object. - :returns: - The created items/collectors. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given collector, only - conftest files in the collector's directory and its parent directories - are consulted. - """ - - -@hookspec(firstresult=True) -def pytest_pyfunc_call(pyfuncitem: Function) -> object | None: - """Call underlying test function. - - Stops at first non-None result, see :ref:`firstresult`. - - :param pyfuncitem: - The function item. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only - conftest files in the item's directory and its parent directories - are consulted. - """ - - -def pytest_generate_tests(metafunc: Metafunc) -> None: - """Generate (multiple) parametrized calls to a test function. - - :param metafunc: - The :class:`~pytest.Metafunc` helper for the test function. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given function definition, - only conftest files in the functions's directory and its parent directories - are consulted. - """ - - -@hookspec(firstresult=True) -def pytest_make_parametrize_id(config: Config, val: object, argname: str) -> str | None: - """Return a user-friendly string representation of the given ``val`` - that will be used by @pytest.mark.parametrize calls, or None if the hook - doesn't know about ``val``. - - The parameter name is available as ``argname``, if required. - - Stops at first non-None result, see :ref:`firstresult`. - - :param config: The pytest config object. - :param val: The parametrized value. - :param argname: The automatic parameter name produced by pytest. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. - """ - - -# ------------------------------------------------------------------------- -# runtest related hooks -# ------------------------------------------------------------------------- - - -@hookspec(firstresult=True) -def pytest_runtestloop(session: Session) -> object | None: - """Perform the main runtest loop (after collection finished). - - The default hook implementation performs the runtest protocol for all items - collected in the session (``session.items``), unless the collection failed - or the ``collectonly`` pytest option is set. - - If at any point :py:func:`pytest.exit` is called, the loop is - terminated immediately. - - If at any point ``session.shouldfail`` or ``session.shouldstop`` are set, the - loop is terminated after the runtest protocol for the current item is finished. - - :param session: The pytest session object. - - Stops at first non-None result, see :ref:`firstresult`. - The return value is not used, but only stops further processing. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. - """ - - -@hookspec(firstresult=True) -def pytest_runtest_protocol(item: Item, nextitem: Item | None) -> object | None: - """Perform the runtest protocol for a single test item. - - The default runtest protocol is this (see individual hooks for full details): - - - ``pytest_runtest_logstart(nodeid, location)`` - - - Setup phase: - - ``call = pytest_runtest_setup(item)`` (wrapped in ``CallInfo(when="setup")``) - - ``report = pytest_runtest_makereport(item, call)`` - - ``pytest_runtest_logreport(report)`` - - ``pytest_exception_interact(call, report)`` if an interactive exception occurred - - - Call phase, if the setup passed and the ``setuponly`` pytest option is not set: - - ``call = pytest_runtest_call(item)`` (wrapped in ``CallInfo(when="call")``) - - ``report = pytest_runtest_makereport(item, call)`` - - ``pytest_runtest_logreport(report)`` - - ``pytest_exception_interact(call, report)`` if an interactive exception occurred - - - Teardown phase: - - ``call = pytest_runtest_teardown(item, nextitem)`` (wrapped in ``CallInfo(when="teardown")``) - - ``report = pytest_runtest_makereport(item, call)`` - - ``pytest_runtest_logreport(report)`` - - ``pytest_exception_interact(call, report)`` if an interactive exception occurred - - - ``pytest_runtest_logfinish(nodeid, location)`` - - :param item: Test item for which the runtest protocol is performed. - :param nextitem: The scheduled-to-be-next test item (or None if this is the end my friend). - - Stops at first non-None result, see :ref:`firstresult`. - The return value is not used, but only stops further processing. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. - """ - - -def pytest_runtest_logstart(nodeid: str, location: tuple[str, int | None, str]) -> None: - """Called at the start of running the runtest protocol for a single item. - - See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. - - :param nodeid: Full node ID of the item. - :param location: A tuple of ``(filename, lineno, testname)`` - where ``filename`` is a file path relative to ``config.rootpath`` - and ``lineno`` is 0-based. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in the item's directory and its parent directories are consulted. - """ - - -def pytest_runtest_logfinish( - nodeid: str, location: tuple[str, int | None, str] -) -> None: - """Called at the end of running the runtest protocol for a single item. - - See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. - - :param nodeid: Full node ID of the item. - :param location: A tuple of ``(filename, lineno, testname)`` - where ``filename`` is a file path relative to ``config.rootpath`` - and ``lineno`` is 0-based. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in the item's directory and its parent directories are consulted. - """ - - -def pytest_runtest_setup(item: Item) -> None: - """Called to perform the setup phase for a test item. - - The default implementation runs ``setup()`` on ``item`` and all of its - parents (which haven't been setup yet). This includes obtaining the - values of fixtures required by the item (which haven't been obtained - yet). - - :param item: - The item. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in the item's directory and its parent directories are consulted. - """ - - -def pytest_runtest_call(item: Item) -> None: - """Called to run the test for test item (the call phase). - - The default implementation calls ``item.runtest()``. - - :param item: - The item. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in the item's directory and its parent directories are consulted. - """ - - -def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None: - """Called to perform the teardown phase for a test item. - - The default implementation runs the finalizers and calls ``teardown()`` - on ``item`` and all of its parents (which need to be torn down). This - includes running the teardown phase of fixtures required by the item (if - they go out of scope). - - :param item: - The item. - :param nextitem: - The scheduled-to-be-next test item (None if no further test item is - scheduled). This argument is used to perform exact teardowns, i.e. - calling just enough finalizers so that nextitem only needs to call - setup functions. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in the item's directory and its parent directories are consulted. - """ - - -@hookspec(firstresult=True) -def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport | None: - """Called to create a :class:`~pytest.TestReport` for each of - the setup, call and teardown runtest phases of a test item. - - See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. - - :param item: The item. - :param call: The :class:`~pytest.CallInfo` for the phase. - - Stops at first non-None result, see :ref:`firstresult`. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in the item's directory and its parent directories are consulted. - """ - - -def pytest_runtest_logreport(report: TestReport) -> None: - """Process the :class:`~pytest.TestReport` produced for each - of the setup, call and teardown runtest phases of an item. - - See :hook:`pytest_runtest_protocol` for a description of the runtest protocol. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in the item's directory and its parent directories are consulted. - """ - - -@hookspec(firstresult=True) -def pytest_report_to_serializable( - config: Config, - report: CollectReport | TestReport, -) -> dict[str, Any] | None: - """Serialize the given report object into a data structure suitable for - sending over the wire, e.g. converted to JSON. - - :param config: The pytest config object. - :param report: The report. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. The exact details may depend - on the plugin which calls the hook. - """ - - -@hookspec(firstresult=True) -def pytest_report_from_serializable( - config: Config, - data: dict[str, Any], -) -> CollectReport | TestReport | None: - """Restore a report object previously serialized with - :hook:`pytest_report_to_serializable`. - - :param config: The pytest config object. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. The exact details may depend - on the plugin which calls the hook. - """ - - -# ------------------------------------------------------------------------- -# Fixture related hooks -# ------------------------------------------------------------------------- - - -@hookspec(firstresult=True) -def pytest_fixture_setup( - fixturedef: FixtureDef[Any], request: SubRequest -) -> object | None: - """Perform fixture setup execution. - - :param fixturedef: - The fixture definition object. - :param request: - The fixture request object. - :returns: - The return value of the call to the fixture function. - - Stops at first non-None result, see :ref:`firstresult`. - - .. note:: - If the fixture function returns None, other implementations of - this hook function will continue to be called, according to the - behavior of the :ref:`firstresult` option. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given fixture, only - conftest files in the fixture scope's directory and its parent directories - are consulted. - """ - - -def pytest_fixture_post_finalizer( - fixturedef: FixtureDef[Any], request: SubRequest -) -> None: - """Called after fixture teardown, but before the cache is cleared, so - the fixture result ``fixturedef.cached_result`` is still available (not - ``None``). - - :param fixturedef: - The fixture definition object. - :param request: - The fixture request object. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given fixture, only - conftest files in the fixture scope's directory and its parent directories - are consulted. - """ - - -# ------------------------------------------------------------------------- -# test session related hooks -# ------------------------------------------------------------------------- - - -def pytest_sessionstart(session: Session) -> None: - """Called after the ``Session`` object has been created and before performing collection - and entering the run test loop. - - :param session: The pytest session object. - - Use in conftest plugins - ======================= - - This hook is only called for :ref:`initial conftests `. - """ - - -def pytest_sessionfinish( - session: Session, - exitstatus: int | ExitCode, -) -> None: - """Called after whole test run finished, right before returning the exit status to the system. - - :param session: The pytest session object. - :param exitstatus: The status which pytest will return to the system. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. - """ - - -def pytest_unconfigure(config: Config) -> None: - """Called before test process is exited. - - :param config: The pytest config object. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. - """ - - -# ------------------------------------------------------------------------- -# hooks for customizing the assert methods -# ------------------------------------------------------------------------- - - -def pytest_assertrepr_compare( - config: Config, op: str, left: object, right: object -) -> list[str] | None: - """Return explanation for comparisons in failing assert expressions. - - Return None for no custom explanation, otherwise return a list - of strings. The strings will be joined by newlines but any newlines - *in* a string will be escaped. Note that all but the first line will - be indented slightly, the intention is for the first line to be a summary. - - :param config: The pytest config object. - :param op: The operator, e.g. `"=="`, `"!="`, `"not in"`. - :param left: The left operand. - :param right: The right operand. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in the item's directory and its parent directories are consulted. - """ - - -def pytest_assertion_pass(item: Item, lineno: int, orig: str, expl: str) -> None: - """Called whenever an assertion passes. - - .. versionadded:: 5.0 - - Use this hook to do some processing after a passing assertion. - The original assertion information is available in the `orig` string - and the pytest introspected assertion information is available in the - `expl` string. - - This hook must be explicitly enabled by the ``enable_assertion_pass_hook`` - ini-file option: - - .. code-block:: ini - - [pytest] - enable_assertion_pass_hook=true - - You need to **clean the .pyc** files in your project directory and interpreter libraries - when enabling this option, as assertions will require to be re-written. - - :param item: pytest item object of current test. - :param lineno: Line number of the assert statement. - :param orig: String with the original assertion. - :param expl: String with the assert explanation. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in the item's directory and its parent directories are consulted. - """ - - -# ------------------------------------------------------------------------- -# Hooks for influencing reporting (invoked from _pytest_terminal). -# ------------------------------------------------------------------------- - - -@hookspec( - warn_on_impl_args={ - "startdir": HOOK_LEGACY_PATH_ARG.format( - pylib_path_arg="startdir", pathlib_path_arg="start_path" - ), - }, -) -def pytest_report_header( # type:ignore[empty-body] - config: Config, start_path: Path, startdir: LEGACY_PATH -) -> str | list[str]: - """Return a string or list of strings to be displayed as header info for terminal reporting. - - :param config: The pytest config object. - :param start_path: The starting dir. - :type start_path: pathlib.Path - :param startdir: The starting dir (deprecated). - - .. note:: - - Lines returned by a plugin are displayed before those of plugins which - ran before it. - If you want to have your line(s) displayed first, use - :ref:`trylast=True `. - - .. versionchanged:: 7.0.0 - The ``start_path`` parameter was added as a :class:`pathlib.Path` - equivalent of the ``startdir`` parameter. The ``startdir`` parameter - has been deprecated. - - Use in conftest plugins - ======================= - - This hook is only called for :ref:`initial conftests `. - """ - - -@hookspec( - warn_on_impl_args={ - "startdir": HOOK_LEGACY_PATH_ARG.format( - pylib_path_arg="startdir", pathlib_path_arg="start_path" - ), - }, -) -def pytest_report_collectionfinish( # type:ignore[empty-body] - config: Config, - start_path: Path, - startdir: LEGACY_PATH, - items: Sequence[Item], -) -> str | list[str]: - """Return a string or list of strings to be displayed after collection - has finished successfully. - - These strings will be displayed after the standard "collected X items" message. - - .. versionadded:: 3.2 - - :param config: The pytest config object. - :param start_path: The starting dir. - :type start_path: pathlib.Path - :param startdir: The starting dir (deprecated). - :param items: List of pytest items that are going to be executed; this list should not be modified. - - .. note:: - - Lines returned by a plugin are displayed before those of plugins which - ran before it. - If you want to have your line(s) displayed first, use - :ref:`trylast=True `. - - .. versionchanged:: 7.0.0 - The ``start_path`` parameter was added as a :class:`pathlib.Path` - equivalent of the ``startdir`` parameter. The ``startdir`` parameter - has been deprecated. - - Use in conftest plugins - ======================= - - Any conftest plugin can implement this hook. - """ - - -@hookspec(firstresult=True) -def pytest_report_teststatus( # type:ignore[empty-body] - report: CollectReport | TestReport, config: Config -) -> TestShortLogReport | tuple[str, str, str | tuple[str, Mapping[str, bool]]]: - """Return result-category, shortletter and verbose word for status - reporting. - - The result-category is a category in which to count the result, for - example "passed", "skipped", "error" or the empty string. - - The shortletter is shown as testing progresses, for example ".", "s", - "E" or the empty string. - - The verbose word is shown as testing progresses in verbose mode, for - example "PASSED", "SKIPPED", "ERROR" or the empty string. - - pytest may style these implicitly according to the report outcome. - To provide explicit styling, return a tuple for the verbose word, - for example ``"rerun", "R", ("RERUN", {"yellow": True})``. - - :param report: The report object whose status is to be returned. - :param config: The pytest config object. - :returns: The test status. - - Stops at first non-None result, see :ref:`firstresult`. - - Use in conftest plugins - ======================= - - Any conftest plugin can implement this hook. - """ - - -def pytest_terminal_summary( - terminalreporter: TerminalReporter, - exitstatus: ExitCode, - config: Config, -) -> None: - """Add a section to terminal summary reporting. - - :param terminalreporter: The internal terminal reporter object. - :param exitstatus: The exit status that will be reported back to the OS. - :param config: The pytest config object. - - .. versionadded:: 4.2 - The ``config`` parameter. - - Use in conftest plugins - ======================= - - Any conftest plugin can implement this hook. - """ - - -@hookspec(historic=True) -def pytest_warning_recorded( - warning_message: warnings.WarningMessage, - when: Literal["config", "collect", "runtest"], - nodeid: str, - location: tuple[str, int, str] | None, -) -> None: - """Process a warning captured by the internal pytest warnings plugin. - - :param warning_message: - The captured warning. This is the same object produced by :class:`warnings.catch_warnings`, - and contains the same attributes as the parameters of :py:func:`warnings.showwarning`. - - :param when: - Indicates when the warning was captured. Possible values: - - * ``"config"``: during pytest configuration/initialization stage. - * ``"collect"``: during test collection. - * ``"runtest"``: during test execution. - - :param nodeid: - Full id of the item. Empty string for warnings that are not specific to - a particular node. - - :param location: - When available, holds information about the execution context of the captured - warning (filename, linenumber, function). ``function`` evaluates to - when the execution context is at the module level. - - .. versionadded:: 6.0 - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. If the warning is specific to a - particular node, only conftest files in parent directories of the node are - consulted. - """ - - -# ------------------------------------------------------------------------- -# Hooks for influencing skipping -# ------------------------------------------------------------------------- - - -def pytest_markeval_namespace( # type:ignore[empty-body] - config: Config, -) -> dict[str, Any]: - """Called when constructing the globals dictionary used for - evaluating string conditions in xfail/skipif markers. - - This is useful when the condition for a marker requires - objects that are expensive or impossible to obtain during - collection time, which is required by normal boolean - conditions. - - .. versionadded:: 6.2 - - :param config: The pytest config object. - :returns: A dictionary of additional globals to add. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given item, only conftest - files in parent directories of the item are consulted. - """ - - -# ------------------------------------------------------------------------- -# error handling and internal debugging hooks -# ------------------------------------------------------------------------- - - -def pytest_internalerror( - excrepr: ExceptionRepr, - excinfo: ExceptionInfo[BaseException], -) -> bool | None: - """Called for internal errors. - - Return True to suppress the fallback handling of printing an - INTERNALERROR message directly to sys.stderr. - - :param excrepr: The exception repr object. - :param excinfo: The exception info. - - Use in conftest plugins - ======================= - - Any conftest plugin can implement this hook. - """ - - -def pytest_keyboard_interrupt( - excinfo: ExceptionInfo[KeyboardInterrupt | Exit], -) -> None: - """Called for keyboard interrupt. - - :param excinfo: The exception info. - - Use in conftest plugins - ======================= - - Any conftest plugin can implement this hook. - """ - - -def pytest_exception_interact( - node: Item | Collector, - call: CallInfo[Any], - report: CollectReport | TestReport, -) -> None: - """Called when an exception was raised which can potentially be - interactively handled. - - May be called during collection (see :hook:`pytest_make_collect_report`), - in which case ``report`` is a :class:`~pytest.CollectReport`. - - May be called during runtest of an item (see :hook:`pytest_runtest_protocol`), - in which case ``report`` is a :class:`~pytest.TestReport`. - - This hook is not called if the exception that was raised is an internal - exception like ``skip.Exception``. - - :param node: - The item or collector. - :param call: - The call information. Contains the exception. - :param report: - The collection or test report. - - Use in conftest plugins - ======================= - - Any conftest file can implement this hook. For a given node, only conftest - files in parent directories of the node are consulted. - """ - - -def pytest_enter_pdb(config: Config, pdb: pdb.Pdb) -> None: - """Called upon pdb.set_trace(). - - Can be used by plugins to take special action just before the python - debugger enters interactive mode. - - :param config: The pytest config object. - :param pdb: The Pdb instance. - - Use in conftest plugins - ======================= - - Any conftest plugin can implement this hook. - """ - - -def pytest_leave_pdb(config: Config, pdb: pdb.Pdb) -> None: - """Called when leaving pdb (e.g. with continue after pdb.set_trace()). - - Can be used by plugins to take special action just after the python - debugger leaves interactive mode. - - :param config: The pytest config object. - :param pdb: The Pdb instance. - - Use in conftest plugins - ======================= - - Any conftest plugin can implement this hook. - """ diff --git a/.venv/lib/python3.12/site-packages/_pytest/junitxml.py b/.venv/lib/python3.12/site-packages/_pytest/junitxml.py deleted file mode 100644 index 3a2cb59a..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/junitxml.py +++ /dev/null @@ -1,697 +0,0 @@ -# mypy: allow-untyped-defs -"""Report test results in JUnit-XML format, for use with Jenkins and build -integration servers. - -Based on initial code from Ross Lawley. - -Output conforms to -https://github.com/jenkinsci/xunit-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd -""" - -from __future__ import annotations - -from datetime import datetime -from datetime import timezone -import functools -import os -import platform -import re -from typing import Callable -from typing import Match -import xml.etree.ElementTree as ET - -from _pytest import nodes -from _pytest import timing -from _pytest._code.code import ExceptionRepr -from _pytest._code.code import ReprFileLocation -from _pytest.config import Config -from _pytest.config import filename_arg -from _pytest.config.argparsing import Parser -from _pytest.fixtures import FixtureRequest -from _pytest.reports import TestReport -from _pytest.stash import StashKey -from _pytest.terminal import TerminalReporter -import pytest - - -xml_key = StashKey["LogXML"]() - - -def bin_xml_escape(arg: object) -> str: - r"""Visually escape invalid XML characters. - - For example, transforms - 'hello\aworld\b' - into - 'hello#x07world#x08' - Note that the #xABs are *not* XML escapes - missing the ampersand «. - The idea is to escape visually for the user rather than for XML itself. - """ - - def repl(matchobj: Match[str]) -> str: - i = ord(matchobj.group()) - if i <= 0xFF: - return f"#x{i:02X}" - else: - return f"#x{i:04X}" - - # The spec range of valid chars is: - # Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] - # For an unknown(?) reason, we disallow #x7F (DEL) as well. - illegal_xml_re = ( - "[^\u0009\u000a\u000d\u0020-\u007e\u0080-\ud7ff\ue000-\ufffd\u10000-\u10ffff]" - ) - return re.sub(illegal_xml_re, repl, str(arg)) - - -def merge_family(left, right) -> None: - result = {} - for kl, vl in left.items(): - for kr, vr in right.items(): - if not isinstance(vl, list): - raise TypeError(type(vl)) - result[kl] = vl + vr - left.update(result) - - -families = {} -families["_base"] = {"testcase": ["classname", "name"]} -families["_base_legacy"] = {"testcase": ["file", "line", "url"]} - -# xUnit 1.x inherits legacy attributes. -families["xunit1"] = families["_base"].copy() -merge_family(families["xunit1"], families["_base_legacy"]) - -# xUnit 2.x uses strict base attributes. -families["xunit2"] = families["_base"] - - -class _NodeReporter: - def __init__(self, nodeid: str | TestReport, xml: LogXML) -> None: - self.id = nodeid - self.xml = xml - self.add_stats = self.xml.add_stats - self.family = self.xml.family - self.duration = 0.0 - self.properties: list[tuple[str, str]] = [] - self.nodes: list[ET.Element] = [] - self.attrs: dict[str, str] = {} - - def append(self, node: ET.Element) -> None: - self.xml.add_stats(node.tag) - self.nodes.append(node) - - def add_property(self, name: str, value: object) -> None: - self.properties.append((str(name), bin_xml_escape(value))) - - def add_attribute(self, name: str, value: object) -> None: - self.attrs[str(name)] = bin_xml_escape(value) - - def make_properties_node(self) -> ET.Element | None: - """Return a Junit node containing custom properties, if any.""" - if self.properties: - properties = ET.Element("properties") - for name, value in self.properties: - properties.append(ET.Element("property", name=name, value=value)) - return properties - return None - - def record_testreport(self, testreport: TestReport) -> None: - names = mangle_test_address(testreport.nodeid) - existing_attrs = self.attrs - classnames = names[:-1] - if self.xml.prefix: - classnames.insert(0, self.xml.prefix) - attrs: dict[str, str] = { - "classname": ".".join(classnames), - "name": bin_xml_escape(names[-1]), - "file": testreport.location[0], - } - if testreport.location[1] is not None: - attrs["line"] = str(testreport.location[1]) - if hasattr(testreport, "url"): - attrs["url"] = testreport.url - self.attrs = attrs - self.attrs.update(existing_attrs) # Restore any user-defined attributes. - - # Preserve legacy testcase behavior. - if self.family == "xunit1": - return - - # Filter out attributes not permitted by this test family. - # Including custom attributes because they are not valid here. - temp_attrs = {} - for key in self.attrs: - if key in families[self.family]["testcase"]: - temp_attrs[key] = self.attrs[key] - self.attrs = temp_attrs - - def to_xml(self) -> ET.Element: - testcase = ET.Element("testcase", self.attrs, time=f"{self.duration:.3f}") - properties = self.make_properties_node() - if properties is not None: - testcase.append(properties) - testcase.extend(self.nodes) - return testcase - - def _add_simple(self, tag: str, message: str, data: str | None = None) -> None: - node = ET.Element(tag, message=message) - node.text = bin_xml_escape(data) - self.append(node) - - def write_captured_output(self, report: TestReport) -> None: - if not self.xml.log_passing_tests and report.passed: - return - - content_out = report.capstdout - content_log = report.caplog - content_err = report.capstderr - if self.xml.logging == "no": - return - content_all = "" - if self.xml.logging in ["log", "all"]: - content_all = self._prepare_content(content_log, " Captured Log ") - if self.xml.logging in ["system-out", "out-err", "all"]: - content_all += self._prepare_content(content_out, " Captured Out ") - self._write_content(report, content_all, "system-out") - content_all = "" - if self.xml.logging in ["system-err", "out-err", "all"]: - content_all += self._prepare_content(content_err, " Captured Err ") - self._write_content(report, content_all, "system-err") - content_all = "" - if content_all: - self._write_content(report, content_all, "system-out") - - def _prepare_content(self, content: str, header: str) -> str: - return "\n".join([header.center(80, "-"), content, ""]) - - def _write_content(self, report: TestReport, content: str, jheader: str) -> None: - tag = ET.Element(jheader) - tag.text = bin_xml_escape(content) - self.append(tag) - - def append_pass(self, report: TestReport) -> None: - self.add_stats("passed") - - def append_failure(self, report: TestReport) -> None: - # msg = str(report.longrepr.reprtraceback.extraline) - if hasattr(report, "wasxfail"): - self._add_simple("skipped", "xfail-marked test passes unexpectedly") - else: - assert report.longrepr is not None - reprcrash: ReprFileLocation | None = getattr( - report.longrepr, "reprcrash", None - ) - if reprcrash is not None: - message = reprcrash.message - else: - message = str(report.longrepr) - message = bin_xml_escape(message) - self._add_simple("failure", message, str(report.longrepr)) - - def append_collect_error(self, report: TestReport) -> None: - # msg = str(report.longrepr.reprtraceback.extraline) - assert report.longrepr is not None - self._add_simple("error", "collection failure", str(report.longrepr)) - - def append_collect_skipped(self, report: TestReport) -> None: - self._add_simple("skipped", "collection skipped", str(report.longrepr)) - - def append_error(self, report: TestReport) -> None: - assert report.longrepr is not None - reprcrash: ReprFileLocation | None = getattr(report.longrepr, "reprcrash", None) - if reprcrash is not None: - reason = reprcrash.message - else: - reason = str(report.longrepr) - - if report.when == "teardown": - msg = f'failed on teardown with "{reason}"' - else: - msg = f'failed on setup with "{reason}"' - self._add_simple("error", bin_xml_escape(msg), str(report.longrepr)) - - def append_skipped(self, report: TestReport) -> None: - if hasattr(report, "wasxfail"): - xfailreason = report.wasxfail - if xfailreason.startswith("reason: "): - xfailreason = xfailreason[8:] - xfailreason = bin_xml_escape(xfailreason) - skipped = ET.Element("skipped", type="pytest.xfail", message=xfailreason) - self.append(skipped) - else: - assert isinstance(report.longrepr, tuple) - filename, lineno, skipreason = report.longrepr - if skipreason.startswith("Skipped: "): - skipreason = skipreason[9:] - details = f"{filename}:{lineno}: {skipreason}" - - skipped = ET.Element( - "skipped", type="pytest.skip", message=bin_xml_escape(skipreason) - ) - skipped.text = bin_xml_escape(details) - self.append(skipped) - self.write_captured_output(report) - - def finalize(self) -> None: - data = self.to_xml() - self.__dict__.clear() - # Type ignored because mypy doesn't like overriding a method. - # Also the return value doesn't match... - self.to_xml = lambda: data # type: ignore[method-assign] - - -def _warn_incompatibility_with_xunit2( - request: FixtureRequest, fixture_name: str -) -> None: - """Emit a PytestWarning about the given fixture being incompatible with newer xunit revisions.""" - from _pytest.warning_types import PytestWarning - - xml = request.config.stash.get(xml_key, None) - if xml is not None and xml.family not in ("xunit1", "legacy"): - request.node.warn( - PytestWarning( - f"{fixture_name} is incompatible with junit_family '{xml.family}' (use 'legacy' or 'xunit1')" - ) - ) - - -@pytest.fixture -def record_property(request: FixtureRequest) -> Callable[[str, object], None]: - """Add extra properties to the calling test. - - User properties become part of the test report and are available to the - configured reporters, like JUnit XML. - - The fixture is callable with ``name, value``. The value is automatically - XML-encoded. - - Example:: - - def test_function(record_property): - record_property("example_key", 1) - """ - _warn_incompatibility_with_xunit2(request, "record_property") - - def append_property(name: str, value: object) -> None: - request.node.user_properties.append((name, value)) - - return append_property - - -@pytest.fixture -def record_xml_attribute(request: FixtureRequest) -> Callable[[str, object], None]: - """Add extra xml attributes to the tag for the calling test. - - The fixture is callable with ``name, value``. The value is - automatically XML-encoded. - """ - from _pytest.warning_types import PytestExperimentalApiWarning - - request.node.warn( - PytestExperimentalApiWarning("record_xml_attribute is an experimental feature") - ) - - _warn_incompatibility_with_xunit2(request, "record_xml_attribute") - - # Declare noop - def add_attr_noop(name: str, value: object) -> None: - pass - - attr_func = add_attr_noop - - xml = request.config.stash.get(xml_key, None) - if xml is not None: - node_reporter = xml.node_reporter(request.node.nodeid) - attr_func = node_reporter.add_attribute - - return attr_func - - -def _check_record_param_type(param: str, v: str) -> None: - """Used by record_testsuite_property to check that the given parameter name is of the proper - type.""" - __tracebackhide__ = True - if not isinstance(v, str): - msg = "{param} parameter needs to be a string, but {g} given" # type: ignore[unreachable] - raise TypeError(msg.format(param=param, g=type(v).__name__)) - - -@pytest.fixture(scope="session") -def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object], None]: - """Record a new ```` tag as child of the root ````. - - This is suitable to writing global information regarding the entire test - suite, and is compatible with ``xunit2`` JUnit family. - - This is a ``session``-scoped fixture which is called with ``(name, value)``. Example: - - .. code-block:: python - - def test_foo(record_testsuite_property): - record_testsuite_property("ARCH", "PPC") - record_testsuite_property("STORAGE_TYPE", "CEPH") - - :param name: - The property name. - :param value: - The property value. Will be converted to a string. - - .. warning:: - - Currently this fixture **does not work** with the - `pytest-xdist `__ plugin. See - :issue:`7767` for details. - """ - __tracebackhide__ = True - - def record_func(name: str, value: object) -> None: - """No-op function in case --junit-xml was not passed in the command-line.""" - __tracebackhide__ = True - _check_record_param_type("name", name) - - xml = request.config.stash.get(xml_key, None) - if xml is not None: - record_func = xml.add_global_property - return record_func - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("terminal reporting") - group.addoption( - "--junitxml", - "--junit-xml", - action="store", - dest="xmlpath", - metavar="path", - type=functools.partial(filename_arg, optname="--junitxml"), - default=None, - help="Create junit-xml style report file at given path", - ) - group.addoption( - "--junitprefix", - "--junit-prefix", - action="store", - metavar="str", - default=None, - help="Prepend prefix to classnames in junit-xml output", - ) - parser.addini( - "junit_suite_name", "Test suite name for JUnit report", default="pytest" - ) - parser.addini( - "junit_logging", - "Write captured log messages to JUnit report: " - "one of no|log|system-out|system-err|out-err|all", - default="no", - ) - parser.addini( - "junit_log_passing_tests", - "Capture log information for passing tests to JUnit report: ", - type="bool", - default=True, - ) - parser.addini( - "junit_duration_report", - "Duration time to report: one of total|call", - default="total", - ) # choices=['total', 'call']) - parser.addini( - "junit_family", - "Emit XML for schema: one of legacy|xunit1|xunit2", - default="xunit2", - ) - - -def pytest_configure(config: Config) -> None: - xmlpath = config.option.xmlpath - # Prevent opening xmllog on worker nodes (xdist). - if xmlpath and not hasattr(config, "workerinput"): - junit_family = config.getini("junit_family") - config.stash[xml_key] = LogXML( - xmlpath, - config.option.junitprefix, - config.getini("junit_suite_name"), - config.getini("junit_logging"), - config.getini("junit_duration_report"), - junit_family, - config.getini("junit_log_passing_tests"), - ) - config.pluginmanager.register(config.stash[xml_key]) - - -def pytest_unconfigure(config: Config) -> None: - xml = config.stash.get(xml_key, None) - if xml: - del config.stash[xml_key] - config.pluginmanager.unregister(xml) - - -def mangle_test_address(address: str) -> list[str]: - path, possible_open_bracket, params = address.partition("[") - names = path.split("::") - # Convert file path to dotted path. - names[0] = names[0].replace(nodes.SEP, ".") - names[0] = re.sub(r"\.py$", "", names[0]) - # Put any params back. - names[-1] += possible_open_bracket + params - return names - - -class LogXML: - def __init__( - self, - logfile, - prefix: str | None, - suite_name: str = "pytest", - logging: str = "no", - report_duration: str = "total", - family="xunit1", - log_passing_tests: bool = True, - ) -> None: - logfile = os.path.expanduser(os.path.expandvars(logfile)) - self.logfile = os.path.normpath(os.path.abspath(logfile)) - self.prefix = prefix - self.suite_name = suite_name - self.logging = logging - self.log_passing_tests = log_passing_tests - self.report_duration = report_duration - self.family = family - self.stats: dict[str, int] = dict.fromkeys( - ["error", "passed", "failure", "skipped"], 0 - ) - self.node_reporters: dict[tuple[str | TestReport, object], _NodeReporter] = {} - self.node_reporters_ordered: list[_NodeReporter] = [] - self.global_properties: list[tuple[str, str]] = [] - - # List of reports that failed on call but teardown is pending. - self.open_reports: list[TestReport] = [] - self.cnt_double_fail_tests = 0 - - # Replaces convenience family with real family. - if self.family == "legacy": - self.family = "xunit1" - - def finalize(self, report: TestReport) -> None: - nodeid = getattr(report, "nodeid", report) - # Local hack to handle xdist report order. - workernode = getattr(report, "node", None) - reporter = self.node_reporters.pop((nodeid, workernode)) - - for propname, propvalue in report.user_properties: - reporter.add_property(propname, str(propvalue)) - - if reporter is not None: - reporter.finalize() - - def node_reporter(self, report: TestReport | str) -> _NodeReporter: - nodeid: str | TestReport = getattr(report, "nodeid", report) - # Local hack to handle xdist report order. - workernode = getattr(report, "node", None) - - key = nodeid, workernode - - if key in self.node_reporters: - # TODO: breaks for --dist=each - return self.node_reporters[key] - - reporter = _NodeReporter(nodeid, self) - - self.node_reporters[key] = reporter - self.node_reporters_ordered.append(reporter) - - return reporter - - def add_stats(self, key: str) -> None: - if key in self.stats: - self.stats[key] += 1 - - def _opentestcase(self, report: TestReport) -> _NodeReporter: - reporter = self.node_reporter(report) - reporter.record_testreport(report) - return reporter - - def pytest_runtest_logreport(self, report: TestReport) -> None: - """Handle a setup/call/teardown report, generating the appropriate - XML tags as necessary. - - Note: due to plugins like xdist, this hook may be called in interlaced - order with reports from other nodes. For example: - - Usual call order: - -> setup node1 - -> call node1 - -> teardown node1 - -> setup node2 - -> call node2 - -> teardown node2 - - Possible call order in xdist: - -> setup node1 - -> call node1 - -> setup node2 - -> call node2 - -> teardown node2 - -> teardown node1 - """ - close_report = None - if report.passed: - if report.when == "call": # ignore setup/teardown - reporter = self._opentestcase(report) - reporter.append_pass(report) - elif report.failed: - if report.when == "teardown": - # The following vars are needed when xdist plugin is used. - report_wid = getattr(report, "worker_id", None) - report_ii = getattr(report, "item_index", None) - close_report = next( - ( - rep - for rep in self.open_reports - if ( - rep.nodeid == report.nodeid - and getattr(rep, "item_index", None) == report_ii - and getattr(rep, "worker_id", None) == report_wid - ) - ), - None, - ) - if close_report: - # We need to open new testcase in case we have failure in - # call and error in teardown in order to follow junit - # schema. - self.finalize(close_report) - self.cnt_double_fail_tests += 1 - reporter = self._opentestcase(report) - if report.when == "call": - reporter.append_failure(report) - self.open_reports.append(report) - if not self.log_passing_tests: - reporter.write_captured_output(report) - else: - reporter.append_error(report) - elif report.skipped: - reporter = self._opentestcase(report) - reporter.append_skipped(report) - self.update_testcase_duration(report) - if report.when == "teardown": - reporter = self._opentestcase(report) - reporter.write_captured_output(report) - - self.finalize(report) - report_wid = getattr(report, "worker_id", None) - report_ii = getattr(report, "item_index", None) - close_report = next( - ( - rep - for rep in self.open_reports - if ( - rep.nodeid == report.nodeid - and getattr(rep, "item_index", None) == report_ii - and getattr(rep, "worker_id", None) == report_wid - ) - ), - None, - ) - if close_report: - self.open_reports.remove(close_report) - - def update_testcase_duration(self, report: TestReport) -> None: - """Accumulate total duration for nodeid from given report and update - the Junit.testcase with the new total if already created.""" - if self.report_duration in {"total", report.when}: - reporter = self.node_reporter(report) - reporter.duration += getattr(report, "duration", 0.0) - - def pytest_collectreport(self, report: TestReport) -> None: - if not report.passed: - reporter = self._opentestcase(report) - if report.failed: - reporter.append_collect_error(report) - else: - reporter.append_collect_skipped(report) - - def pytest_internalerror(self, excrepr: ExceptionRepr) -> None: - reporter = self.node_reporter("internal") - reporter.attrs.update(classname="pytest", name="internal") - reporter._add_simple("error", "internal error", str(excrepr)) - - def pytest_sessionstart(self) -> None: - self.suite_start_time = timing.time() - - def pytest_sessionfinish(self) -> None: - dirname = os.path.dirname(os.path.abspath(self.logfile)) - # exist_ok avoids filesystem race conditions between checking path existence and requesting creation - os.makedirs(dirname, exist_ok=True) - - with open(self.logfile, "w", encoding="utf-8") as logfile: - suite_stop_time = timing.time() - suite_time_delta = suite_stop_time - self.suite_start_time - - numtests = ( - self.stats["passed"] - + self.stats["failure"] - + self.stats["skipped"] - + self.stats["error"] - - self.cnt_double_fail_tests - ) - logfile.write('') - - suite_node = ET.Element( - "testsuite", - name=self.suite_name, - errors=str(self.stats["error"]), - failures=str(self.stats["failure"]), - skipped=str(self.stats["skipped"]), - tests=str(numtests), - time=f"{suite_time_delta:.3f}", - timestamp=datetime.fromtimestamp(self.suite_start_time, timezone.utc) - .astimezone() - .isoformat(), - hostname=platform.node(), - ) - global_properties = self._get_global_properties_node() - if global_properties is not None: - suite_node.append(global_properties) - for node_reporter in self.node_reporters_ordered: - suite_node.append(node_reporter.to_xml()) - testsuites = ET.Element("testsuites") - testsuites.append(suite_node) - logfile.write(ET.tostring(testsuites, encoding="unicode")) - - def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None: - terminalreporter.write_sep("-", f"generated xml file: {self.logfile}") - - def add_global_property(self, name: str, value: object) -> None: - __tracebackhide__ = True - _check_record_param_type("name", name) - self.global_properties.append((name, bin_xml_escape(value))) - - def _get_global_properties_node(self) -> ET.Element | None: - """Return a Junit node containing custom properties, if any.""" - if self.global_properties: - properties = ET.Element("properties") - for name, value in self.global_properties: - properties.append(ET.Element("property", name=name, value=value)) - return properties - return None diff --git a/.venv/lib/python3.12/site-packages/_pytest/legacypath.py b/.venv/lib/python3.12/site-packages/_pytest/legacypath.py deleted file mode 100644 index 59e8ef6e..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/legacypath.py +++ /dev/null @@ -1,468 +0,0 @@ -# mypy: allow-untyped-defs -"""Add backward compatibility support for the legacy py path type.""" - -from __future__ import annotations - -import dataclasses -from pathlib import Path -import shlex -import subprocess -from typing import Final -from typing import final -from typing import TYPE_CHECKING - -from iniconfig import SectionWrapper - -from _pytest.cacheprovider import Cache -from _pytest.compat import LEGACY_PATH -from _pytest.compat import legacy_path -from _pytest.config import Config -from _pytest.config import hookimpl -from _pytest.config import PytestPluginManager -from _pytest.deprecated import check_ispytest -from _pytest.fixtures import fixture -from _pytest.fixtures import FixtureRequest -from _pytest.main import Session -from _pytest.monkeypatch import MonkeyPatch -from _pytest.nodes import Collector -from _pytest.nodes import Item -from _pytest.nodes import Node -from _pytest.pytester import HookRecorder -from _pytest.pytester import Pytester -from _pytest.pytester import RunResult -from _pytest.terminal import TerminalReporter -from _pytest.tmpdir import TempPathFactory - - -if TYPE_CHECKING: - import pexpect - - -@final -class Testdir: - """ - Similar to :class:`Pytester`, but this class works with legacy legacy_path objects instead. - - All methods just forward to an internal :class:`Pytester` instance, converting results - to `legacy_path` objects as necessary. - """ - - __test__ = False - - CLOSE_STDIN: Final = Pytester.CLOSE_STDIN - TimeoutExpired: Final = Pytester.TimeoutExpired - - def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None: - check_ispytest(_ispytest) - self._pytester = pytester - - @property - def tmpdir(self) -> LEGACY_PATH: - """Temporary directory where tests are executed.""" - return legacy_path(self._pytester.path) - - @property - def test_tmproot(self) -> LEGACY_PATH: - return legacy_path(self._pytester._test_tmproot) - - @property - def request(self): - return self._pytester._request - - @property - def plugins(self): - return self._pytester.plugins - - @plugins.setter - def plugins(self, plugins): - self._pytester.plugins = plugins - - @property - def monkeypatch(self) -> MonkeyPatch: - return self._pytester._monkeypatch - - def make_hook_recorder(self, pluginmanager) -> HookRecorder: - """See :meth:`Pytester.make_hook_recorder`.""" - return self._pytester.make_hook_recorder(pluginmanager) - - def chdir(self) -> None: - """See :meth:`Pytester.chdir`.""" - return self._pytester.chdir() - - def finalize(self) -> None: - return self._pytester._finalize() - - def makefile(self, ext, *args, **kwargs) -> LEGACY_PATH: - """See :meth:`Pytester.makefile`.""" - if ext and not ext.startswith("."): - # pytester.makefile is going to throw a ValueError in a way that - # testdir.makefile did not, because - # pathlib.Path is stricter suffixes than py.path - # This ext arguments is likely user error, but since testdir has - # allowed this, we will prepend "." as a workaround to avoid breaking - # testdir usage that worked before - ext = "." + ext - return legacy_path(self._pytester.makefile(ext, *args, **kwargs)) - - def makeconftest(self, source) -> LEGACY_PATH: - """See :meth:`Pytester.makeconftest`.""" - return legacy_path(self._pytester.makeconftest(source)) - - def makeini(self, source) -> LEGACY_PATH: - """See :meth:`Pytester.makeini`.""" - return legacy_path(self._pytester.makeini(source)) - - def getinicfg(self, source: str) -> SectionWrapper: - """See :meth:`Pytester.getinicfg`.""" - return self._pytester.getinicfg(source) - - def makepyprojecttoml(self, source) -> LEGACY_PATH: - """See :meth:`Pytester.makepyprojecttoml`.""" - return legacy_path(self._pytester.makepyprojecttoml(source)) - - def makepyfile(self, *args, **kwargs) -> LEGACY_PATH: - """See :meth:`Pytester.makepyfile`.""" - return legacy_path(self._pytester.makepyfile(*args, **kwargs)) - - def maketxtfile(self, *args, **kwargs) -> LEGACY_PATH: - """See :meth:`Pytester.maketxtfile`.""" - return legacy_path(self._pytester.maketxtfile(*args, **kwargs)) - - def syspathinsert(self, path=None) -> None: - """See :meth:`Pytester.syspathinsert`.""" - return self._pytester.syspathinsert(path) - - def mkdir(self, name) -> LEGACY_PATH: - """See :meth:`Pytester.mkdir`.""" - return legacy_path(self._pytester.mkdir(name)) - - def mkpydir(self, name) -> LEGACY_PATH: - """See :meth:`Pytester.mkpydir`.""" - return legacy_path(self._pytester.mkpydir(name)) - - def copy_example(self, name=None) -> LEGACY_PATH: - """See :meth:`Pytester.copy_example`.""" - return legacy_path(self._pytester.copy_example(name)) - - def getnode(self, config: Config, arg) -> Item | Collector | None: - """See :meth:`Pytester.getnode`.""" - return self._pytester.getnode(config, arg) - - def getpathnode(self, path): - """See :meth:`Pytester.getpathnode`.""" - return self._pytester.getpathnode(path) - - def genitems(self, colitems: list[Item | Collector]) -> list[Item]: - """See :meth:`Pytester.genitems`.""" - return self._pytester.genitems(colitems) - - def runitem(self, source): - """See :meth:`Pytester.runitem`.""" - return self._pytester.runitem(source) - - def inline_runsource(self, source, *cmdlineargs): - """See :meth:`Pytester.inline_runsource`.""" - return self._pytester.inline_runsource(source, *cmdlineargs) - - def inline_genitems(self, *args): - """See :meth:`Pytester.inline_genitems`.""" - return self._pytester.inline_genitems(*args) - - def inline_run(self, *args, plugins=(), no_reraise_ctrlc: bool = False): - """See :meth:`Pytester.inline_run`.""" - return self._pytester.inline_run( - *args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc - ) - - def runpytest_inprocess(self, *args, **kwargs) -> RunResult: - """See :meth:`Pytester.runpytest_inprocess`.""" - return self._pytester.runpytest_inprocess(*args, **kwargs) - - def runpytest(self, *args, **kwargs) -> RunResult: - """See :meth:`Pytester.runpytest`.""" - return self._pytester.runpytest(*args, **kwargs) - - def parseconfig(self, *args) -> Config: - """See :meth:`Pytester.parseconfig`.""" - return self._pytester.parseconfig(*args) - - def parseconfigure(self, *args) -> Config: - """See :meth:`Pytester.parseconfigure`.""" - return self._pytester.parseconfigure(*args) - - def getitem(self, source, funcname="test_func"): - """See :meth:`Pytester.getitem`.""" - return self._pytester.getitem(source, funcname) - - def getitems(self, source): - """See :meth:`Pytester.getitems`.""" - return self._pytester.getitems(source) - - def getmodulecol(self, source, configargs=(), withinit=False): - """See :meth:`Pytester.getmodulecol`.""" - return self._pytester.getmodulecol( - source, configargs=configargs, withinit=withinit - ) - - def collect_by_name(self, modcol: Collector, name: str) -> Item | Collector | None: - """See :meth:`Pytester.collect_by_name`.""" - return self._pytester.collect_by_name(modcol, name) - - def popen( - self, - cmdargs, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - stdin=CLOSE_STDIN, - **kw, - ): - """See :meth:`Pytester.popen`.""" - return self._pytester.popen(cmdargs, stdout, stderr, stdin, **kw) - - def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult: - """See :meth:`Pytester.run`.""" - return self._pytester.run(*cmdargs, timeout=timeout, stdin=stdin) - - def runpython(self, script) -> RunResult: - """See :meth:`Pytester.runpython`.""" - return self._pytester.runpython(script) - - def runpython_c(self, command): - """See :meth:`Pytester.runpython_c`.""" - return self._pytester.runpython_c(command) - - def runpytest_subprocess(self, *args, timeout=None) -> RunResult: - """See :meth:`Pytester.runpytest_subprocess`.""" - return self._pytester.runpytest_subprocess(*args, timeout=timeout) - - def spawn_pytest(self, string: str, expect_timeout: float = 10.0) -> pexpect.spawn: - """See :meth:`Pytester.spawn_pytest`.""" - return self._pytester.spawn_pytest(string, expect_timeout=expect_timeout) - - def spawn(self, cmd: str, expect_timeout: float = 10.0) -> pexpect.spawn: - """See :meth:`Pytester.spawn`.""" - return self._pytester.spawn(cmd, expect_timeout=expect_timeout) - - def __repr__(self) -> str: - return f"" - - def __str__(self) -> str: - return str(self.tmpdir) - - -class LegacyTestdirPlugin: - @staticmethod - @fixture - def testdir(pytester: Pytester) -> Testdir: - """ - Identical to :fixture:`pytester`, and provides an instance whose methods return - legacy ``LEGACY_PATH`` objects instead when applicable. - - New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`. - """ - return Testdir(pytester, _ispytest=True) - - -@final -@dataclasses.dataclass -class TempdirFactory: - """Backward compatibility wrapper that implements ``py.path.local`` - for :class:`TempPathFactory`. - - .. note:: - These days, it is preferred to use ``tmp_path_factory``. - - :ref:`About the tmpdir and tmpdir_factory fixtures`. - - """ - - _tmppath_factory: TempPathFactory - - def __init__( - self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False - ) -> None: - check_ispytest(_ispytest) - self._tmppath_factory = tmppath_factory - - def mktemp(self, basename: str, numbered: bool = True) -> LEGACY_PATH: - """Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object.""" - return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve()) - - def getbasetemp(self) -> LEGACY_PATH: - """Same as :meth:`TempPathFactory.getbasetemp`, but returns a ``py.path.local`` object.""" - return legacy_path(self._tmppath_factory.getbasetemp().resolve()) - - -class LegacyTmpdirPlugin: - @staticmethod - @fixture(scope="session") - def tmpdir_factory(request: FixtureRequest) -> TempdirFactory: - """Return a :class:`pytest.TempdirFactory` instance for the test session.""" - # Set dynamically by pytest_configure(). - return request.config._tmpdirhandler # type: ignore - - @staticmethod - @fixture - def tmpdir(tmp_path: Path) -> LEGACY_PATH: - """Return a temporary directory (as `legacy_path`_ object) - which is unique to each test function invocation. - The temporary directory is created as a subdirectory - of the base temporary directory, with configurable retention, - as discussed in :ref:`temporary directory location and retention`. - - .. note:: - These days, it is preferred to use ``tmp_path``. - - :ref:`About the tmpdir and tmpdir_factory fixtures`. - - .. _legacy_path: https://py.readthedocs.io/en/latest/path.html - """ - return legacy_path(tmp_path) - - -def Cache_makedir(self: Cache, name: str) -> LEGACY_PATH: - """Return a directory path object with the given name. - - Same as :func:`mkdir`, but returns a legacy py path instance. - """ - return legacy_path(self.mkdir(name)) - - -def FixtureRequest_fspath(self: FixtureRequest) -> LEGACY_PATH: - """(deprecated) The file system path of the test module which collected this test.""" - return legacy_path(self.path) - - -def TerminalReporter_startdir(self: TerminalReporter) -> LEGACY_PATH: - """The directory from which pytest was invoked. - - Prefer to use ``startpath`` which is a :class:`pathlib.Path`. - - :type: LEGACY_PATH - """ - return legacy_path(self.startpath) - - -def Config_invocation_dir(self: Config) -> LEGACY_PATH: - """The directory from which pytest was invoked. - - Prefer to use :attr:`invocation_params.dir `, - which is a :class:`pathlib.Path`. - - :type: LEGACY_PATH - """ - return legacy_path(str(self.invocation_params.dir)) - - -def Config_rootdir(self: Config) -> LEGACY_PATH: - """The path to the :ref:`rootdir `. - - Prefer to use :attr:`rootpath`, which is a :class:`pathlib.Path`. - - :type: LEGACY_PATH - """ - return legacy_path(str(self.rootpath)) - - -def Config_inifile(self: Config) -> LEGACY_PATH | None: - """The path to the :ref:`configfile `. - - Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`. - - :type: Optional[LEGACY_PATH] - """ - return legacy_path(str(self.inipath)) if self.inipath else None - - -def Session_startdir(self: Session) -> LEGACY_PATH: - """The path from which pytest was invoked. - - Prefer to use ``startpath`` which is a :class:`pathlib.Path`. - - :type: LEGACY_PATH - """ - return legacy_path(self.startpath) - - -def Config__getini_unknown_type(self, name: str, type: str, value: str | list[str]): - if type == "pathlist": - # TODO: This assert is probably not valid in all cases. - assert self.inipath is not None - dp = self.inipath.parent - input_values = shlex.split(value) if isinstance(value, str) else value - return [legacy_path(str(dp / x)) for x in input_values] - else: - raise ValueError(f"unknown configuration type: {type}", value) - - -def Node_fspath(self: Node) -> LEGACY_PATH: - """(deprecated) returns a legacy_path copy of self.path""" - return legacy_path(self.path) - - -def Node_fspath_set(self: Node, value: LEGACY_PATH) -> None: - self.path = Path(value) - - -@hookimpl(tryfirst=True) -def pytest_load_initial_conftests(early_config: Config) -> None: - """Monkeypatch legacy path attributes in several classes, as early as possible.""" - mp = MonkeyPatch() - early_config.add_cleanup(mp.undo) - - # Add Cache.makedir(). - mp.setattr(Cache, "makedir", Cache_makedir, raising=False) - - # Add FixtureRequest.fspath property. - mp.setattr(FixtureRequest, "fspath", property(FixtureRequest_fspath), raising=False) - - # Add TerminalReporter.startdir property. - mp.setattr( - TerminalReporter, "startdir", property(TerminalReporter_startdir), raising=False - ) - - # Add Config.{invocation_dir,rootdir,inifile} properties. - mp.setattr(Config, "invocation_dir", property(Config_invocation_dir), raising=False) - mp.setattr(Config, "rootdir", property(Config_rootdir), raising=False) - mp.setattr(Config, "inifile", property(Config_inifile), raising=False) - - # Add Session.startdir property. - mp.setattr(Session, "startdir", property(Session_startdir), raising=False) - - # Add pathlist configuration type. - mp.setattr(Config, "_getini_unknown_type", Config__getini_unknown_type) - - # Add Node.fspath property. - mp.setattr(Node, "fspath", property(Node_fspath, Node_fspath_set), raising=False) - - -@hookimpl -def pytest_configure(config: Config) -> None: - """Installs the LegacyTmpdirPlugin if the ``tmpdir`` plugin is also installed.""" - if config.pluginmanager.has_plugin("tmpdir"): - mp = MonkeyPatch() - config.add_cleanup(mp.undo) - # Create TmpdirFactory and attach it to the config object. - # - # This is to comply with existing plugins which expect the handler to be - # available at pytest_configure time, but ideally should be moved entirely - # to the tmpdir_factory session fixture. - try: - tmp_path_factory = config._tmp_path_factory # type: ignore[attr-defined] - except AttributeError: - # tmpdir plugin is blocked. - pass - else: - _tmpdirhandler = TempdirFactory(tmp_path_factory, _ispytest=True) - mp.setattr(config, "_tmpdirhandler", _tmpdirhandler, raising=False) - - config.pluginmanager.register(LegacyTmpdirPlugin, "legacypath-tmpdir") - - -@hookimpl -def pytest_plugin_registered(plugin: object, manager: PytestPluginManager) -> None: - # pytester is not loaded by default and is commonly loaded from a conftest, - # so checking for it in `pytest_configure` is not enough. - is_pytester = plugin is manager.get_plugin("pytester") - if is_pytester and not manager.is_registered(LegacyTestdirPlugin): - manager.register(LegacyTestdirPlugin, "legacypath-pytester") diff --git a/.venv/lib/python3.12/site-packages/_pytest/logging.py b/.venv/lib/python3.12/site-packages/_pytest/logging.py deleted file mode 100644 index 08c826ff..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/logging.py +++ /dev/null @@ -1,955 +0,0 @@ -# mypy: allow-untyped-defs -"""Access and control log capturing.""" - -from __future__ import annotations - -from contextlib import contextmanager -from contextlib import nullcontext -from datetime import datetime -from datetime import timedelta -from datetime import timezone -import io -from io import StringIO -import logging -from logging import LogRecord -import os -from pathlib import Path -import re -from types import TracebackType -from typing import AbstractSet -from typing import Dict -from typing import final -from typing import Generator -from typing import Generic -from typing import List -from typing import Literal -from typing import Mapping -from typing import TYPE_CHECKING -from typing import TypeVar - -from _pytest import nodes -from _pytest._io import TerminalWriter -from _pytest.capture import CaptureManager -from _pytest.config import _strtobool -from _pytest.config import Config -from _pytest.config import create_terminal_writer -from _pytest.config import hookimpl -from _pytest.config import UsageError -from _pytest.config.argparsing import Parser -from _pytest.deprecated import check_ispytest -from _pytest.fixtures import fixture -from _pytest.fixtures import FixtureRequest -from _pytest.main import Session -from _pytest.stash import StashKey -from _pytest.terminal import TerminalReporter - - -if TYPE_CHECKING: - logging_StreamHandler = logging.StreamHandler[StringIO] -else: - logging_StreamHandler = logging.StreamHandler - -DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" -DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" -_ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") -caplog_handler_key = StashKey["LogCaptureHandler"]() -caplog_records_key = StashKey[Dict[str, List[logging.LogRecord]]]() - - -def _remove_ansi_escape_sequences(text: str) -> str: - return _ANSI_ESCAPE_SEQ.sub("", text) - - -class DatetimeFormatter(logging.Formatter): - """A logging formatter which formats record with - :func:`datetime.datetime.strftime` formatter instead of - :func:`time.strftime` in case of microseconds in format string. - """ - - def formatTime(self, record: LogRecord, datefmt: str | None = None) -> str: - if datefmt and "%f" in datefmt: - ct = self.converter(record.created) - tz = timezone(timedelta(seconds=ct.tm_gmtoff), ct.tm_zone) - # Construct `datetime.datetime` object from `struct_time` - # and msecs information from `record` - # Using int() instead of round() to avoid it exceeding 1_000_000 and causing a ValueError (#11861). - dt = datetime(*ct[0:6], microsecond=int(record.msecs * 1000), tzinfo=tz) - return dt.strftime(datefmt) - # Use `logging.Formatter` for non-microsecond formats - return super().formatTime(record, datefmt) - - -class ColoredLevelFormatter(DatetimeFormatter): - """A logging formatter which colorizes the %(levelname)..s part of the - log format passed to __init__.""" - - LOGLEVEL_COLOROPTS: Mapping[int, AbstractSet[str]] = { - logging.CRITICAL: {"red"}, - logging.ERROR: {"red", "bold"}, - logging.WARNING: {"yellow"}, - logging.WARN: {"yellow"}, - logging.INFO: {"green"}, - logging.DEBUG: {"purple"}, - logging.NOTSET: set(), - } - LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*(?:\.\d+)?s)") - - def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - self._terminalwriter = terminalwriter - self._original_fmt = self._style._fmt - self._level_to_fmt_mapping: dict[int, str] = {} - - for level, color_opts in self.LOGLEVEL_COLOROPTS.items(): - self.add_color_level(level, *color_opts) - - def add_color_level(self, level: int, *color_opts: str) -> None: - """Add or update color opts for a log level. - - :param level: - Log level to apply a style to, e.g. ``logging.INFO``. - :param color_opts: - ANSI escape sequence color options. Capitalized colors indicates - background color, i.e. ``'green', 'Yellow', 'bold'`` will give bold - green text on yellow background. - - .. warning:: - This is an experimental API. - """ - assert self._fmt is not None - levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt) - if not levelname_fmt_match: - return - levelname_fmt = levelname_fmt_match.group() - - formatted_levelname = levelname_fmt % {"levelname": logging.getLevelName(level)} - - # add ANSI escape sequences around the formatted levelname - color_kwargs = {name: True for name in color_opts} - colorized_formatted_levelname = self._terminalwriter.markup( - formatted_levelname, **color_kwargs - ) - self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub( - colorized_formatted_levelname, self._fmt - ) - - def format(self, record: logging.LogRecord) -> str: - fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt) - self._style._fmt = fmt - return super().format(record) - - -class PercentStyleMultiline(logging.PercentStyle): - """A logging style with special support for multiline messages. - - If the message of a record consists of multiple lines, this style - formats the message as if each line were logged separately. - """ - - def __init__(self, fmt: str, auto_indent: int | str | bool | None) -> None: - super().__init__(fmt) - self._auto_indent = self._get_auto_indent(auto_indent) - - @staticmethod - def _get_auto_indent(auto_indent_option: int | str | bool | None) -> int: - """Determine the current auto indentation setting. - - Specify auto indent behavior (on/off/fixed) by passing in - extra={"auto_indent": [value]} to the call to logging.log() or - using a --log-auto-indent [value] command line or the - log_auto_indent [value] config option. - - Default behavior is auto-indent off. - - Using the string "True" or "on" or the boolean True as the value - turns auto indent on, using the string "False" or "off" or the - boolean False or the int 0 turns it off, and specifying a - positive integer fixes the indentation position to the value - specified. - - Any other values for the option are invalid, and will silently be - converted to the default. - - :param None|bool|int|str auto_indent_option: - User specified option for indentation from command line, config - or extra kwarg. Accepts int, bool or str. str option accepts the - same range of values as boolean config options, as well as - positive integers represented in str form. - - :returns: - Indentation value, which can be - -1 (automatically determine indentation) or - 0 (auto-indent turned off) or - >0 (explicitly set indentation position). - """ - if auto_indent_option is None: - return 0 - elif isinstance(auto_indent_option, bool): - if auto_indent_option: - return -1 - else: - return 0 - elif isinstance(auto_indent_option, int): - return int(auto_indent_option) - elif isinstance(auto_indent_option, str): - try: - return int(auto_indent_option) - except ValueError: - pass - try: - if _strtobool(auto_indent_option): - return -1 - except ValueError: - return 0 - - return 0 - - def format(self, record: logging.LogRecord) -> str: - if "\n" in record.message: - if hasattr(record, "auto_indent"): - # Passed in from the "extra={}" kwarg on the call to logging.log(). - auto_indent = self._get_auto_indent(record.auto_indent) - else: - auto_indent = self._auto_indent - - if auto_indent: - lines = record.message.splitlines() - formatted = self._fmt % {**record.__dict__, "message": lines[0]} - - if auto_indent < 0: - indentation = _remove_ansi_escape_sequences(formatted).find( - lines[0] - ) - else: - # Optimizes logging by allowing a fixed indentation. - indentation = auto_indent - lines[0] = formatted - return ("\n" + " " * indentation).join(lines) - return self._fmt % record.__dict__ - - -def get_option_ini(config: Config, *names: str): - for name in names: - ret = config.getoption(name) # 'default' arg won't work as expected - if ret is None: - ret = config.getini(name) - if ret: - return ret - - -def pytest_addoption(parser: Parser) -> None: - """Add options to control log capturing.""" - group = parser.getgroup("logging") - - def add_option_ini(option, dest, default=None, type=None, **kwargs): - parser.addini( - dest, default=default, type=type, help="Default value for " + option - ) - group.addoption(option, dest=dest, **kwargs) - - add_option_ini( - "--log-level", - dest="log_level", - default=None, - metavar="LEVEL", - help=( - "Level of messages to catch/display." - " Not set by default, so it depends on the root/parent log handler's" - ' effective level, where it is "WARNING" by default.' - ), - ) - add_option_ini( - "--log-format", - dest="log_format", - default=DEFAULT_LOG_FORMAT, - help="Log format used by the logging module", - ) - add_option_ini( - "--log-date-format", - dest="log_date_format", - default=DEFAULT_LOG_DATE_FORMAT, - help="Log date format used by the logging module", - ) - parser.addini( - "log_cli", - default=False, - type="bool", - help='Enable log display during test run (also known as "live logging")', - ) - add_option_ini( - "--log-cli-level", dest="log_cli_level", default=None, help="CLI logging level" - ) - add_option_ini( - "--log-cli-format", - dest="log_cli_format", - default=None, - help="Log format used by the logging module", - ) - add_option_ini( - "--log-cli-date-format", - dest="log_cli_date_format", - default=None, - help="Log date format used by the logging module", - ) - add_option_ini( - "--log-file", - dest="log_file", - default=None, - help="Path to a file when logging will be written to", - ) - add_option_ini( - "--log-file-mode", - dest="log_file_mode", - default="w", - choices=["w", "a"], - help="Log file open mode", - ) - add_option_ini( - "--log-file-level", - dest="log_file_level", - default=None, - help="Log file logging level", - ) - add_option_ini( - "--log-file-format", - dest="log_file_format", - default=None, - help="Log format used by the logging module", - ) - add_option_ini( - "--log-file-date-format", - dest="log_file_date_format", - default=None, - help="Log date format used by the logging module", - ) - add_option_ini( - "--log-auto-indent", - dest="log_auto_indent", - default=None, - help="Auto-indent multiline messages passed to the logging module. Accepts true|on, false|off or an integer.", - ) - group.addoption( - "--log-disable", - action="append", - default=[], - dest="logger_disable", - help="Disable a logger by name. Can be passed multiple times.", - ) - - -_HandlerType = TypeVar("_HandlerType", bound=logging.Handler) - - -# Not using @contextmanager for performance reasons. -class catching_logs(Generic[_HandlerType]): - """Context manager that prepares the whole logging machinery properly.""" - - __slots__ = ("handler", "level", "orig_level") - - def __init__(self, handler: _HandlerType, level: int | None = None) -> None: - self.handler = handler - self.level = level - - def __enter__(self) -> _HandlerType: - root_logger = logging.getLogger() - if self.level is not None: - self.handler.setLevel(self.level) - root_logger.addHandler(self.handler) - if self.level is not None: - self.orig_level = root_logger.level - root_logger.setLevel(min(self.orig_level, self.level)) - return self.handler - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - root_logger = logging.getLogger() - if self.level is not None: - root_logger.setLevel(self.orig_level) - root_logger.removeHandler(self.handler) - - -class LogCaptureHandler(logging_StreamHandler): - """A logging handler that stores log records and the log text.""" - - def __init__(self) -> None: - """Create a new log handler.""" - super().__init__(StringIO()) - self.records: list[logging.LogRecord] = [] - - def emit(self, record: logging.LogRecord) -> None: - """Keep the log records in a list in addition to the log text.""" - self.records.append(record) - super().emit(record) - - def reset(self) -> None: - self.records = [] - self.stream = StringIO() - - def clear(self) -> None: - self.records.clear() - self.stream = StringIO() - - def handleError(self, record: logging.LogRecord) -> None: - if logging.raiseExceptions: - # Fail the test if the log message is bad (emit failed). - # The default behavior of logging is to print "Logging error" - # to stderr with the call stack and some extra details. - # pytest wants to make such mistakes visible during testing. - raise # noqa: PLE0704 - - -@final -class LogCaptureFixture: - """Provides access and control of log capturing.""" - - def __init__(self, item: nodes.Node, *, _ispytest: bool = False) -> None: - check_ispytest(_ispytest) - self._item = item - self._initial_handler_level: int | None = None - # Dict of log name -> log level. - self._initial_logger_levels: dict[str | None, int] = {} - self._initial_disabled_logging_level: int | None = None - - def _finalize(self) -> None: - """Finalize the fixture. - - This restores the log levels and the disabled logging levels changed by :meth:`set_level`. - """ - # Restore log levels. - if self._initial_handler_level is not None: - self.handler.setLevel(self._initial_handler_level) - for logger_name, level in self._initial_logger_levels.items(): - logger = logging.getLogger(logger_name) - logger.setLevel(level) - # Disable logging at the original disabled logging level. - if self._initial_disabled_logging_level is not None: - logging.disable(self._initial_disabled_logging_level) - self._initial_disabled_logging_level = None - - @property - def handler(self) -> LogCaptureHandler: - """Get the logging handler used by the fixture.""" - return self._item.stash[caplog_handler_key] - - def get_records( - self, when: Literal["setup", "call", "teardown"] - ) -> list[logging.LogRecord]: - """Get the logging records for one of the possible test phases. - - :param when: - Which test phase to obtain the records from. - Valid values are: "setup", "call" and "teardown". - - :returns: The list of captured records at the given stage. - - .. versionadded:: 3.4 - """ - return self._item.stash[caplog_records_key].get(when, []) - - @property - def text(self) -> str: - """The formatted log text.""" - return _remove_ansi_escape_sequences(self.handler.stream.getvalue()) - - @property - def records(self) -> list[logging.LogRecord]: - """The list of log records.""" - return self.handler.records - - @property - def record_tuples(self) -> list[tuple[str, int, str]]: - """A list of a stripped down version of log records intended - for use in assertion comparison. - - The format of the tuple is: - - (logger_name, log_level, message) - """ - return [(r.name, r.levelno, r.getMessage()) for r in self.records] - - @property - def messages(self) -> list[str]: - """A list of format-interpolated log messages. - - Unlike 'records', which contains the format string and parameters for - interpolation, log messages in this list are all interpolated. - - Unlike 'text', which contains the output from the handler, log - messages in this list are unadorned with levels, timestamps, etc, - making exact comparisons more reliable. - - Note that traceback or stack info (from :func:`logging.exception` or - the `exc_info` or `stack_info` arguments to the logging functions) is - not included, as this is added by the formatter in the handler. - - .. versionadded:: 3.7 - """ - return [r.getMessage() for r in self.records] - - def clear(self) -> None: - """Reset the list of log records and the captured log text.""" - self.handler.clear() - - def _force_enable_logging( - self, level: int | str, logger_obj: logging.Logger - ) -> int: - """Enable the desired logging level if the global level was disabled via ``logging.disabled``. - - Only enables logging levels greater than or equal to the requested ``level``. - - Does nothing if the desired ``level`` wasn't disabled. - - :param level: - The logger level caplog should capture. - All logging is enabled if a non-standard logging level string is supplied. - Valid level strings are in :data:`logging._nameToLevel`. - :param logger_obj: The logger object to check. - - :return: The original disabled logging level. - """ - original_disable_level: int = logger_obj.manager.disable - - if isinstance(level, str): - # Try to translate the level string to an int for `logging.disable()` - level = logging.getLevelName(level) - - if not isinstance(level, int): - # The level provided was not valid, so just un-disable all logging. - logging.disable(logging.NOTSET) - elif not logger_obj.isEnabledFor(level): - # Each level is `10` away from other levels. - # https://docs.python.org/3/library/logging.html#logging-levels - disable_level = max(level - 10, logging.NOTSET) - logging.disable(disable_level) - - return original_disable_level - - def set_level(self, level: int | str, logger: str | None = None) -> None: - """Set the threshold level of a logger for the duration of a test. - - Logging messages which are less severe than this level will not be captured. - - .. versionchanged:: 3.4 - The levels of the loggers changed by this function will be - restored to their initial values at the end of the test. - - Will enable the requested logging level if it was disabled via :func:`logging.disable`. - - :param level: The level. - :param logger: The logger to update. If not given, the root logger. - """ - logger_obj = logging.getLogger(logger) - # Save the original log-level to restore it during teardown. - self._initial_logger_levels.setdefault(logger, logger_obj.level) - logger_obj.setLevel(level) - if self._initial_handler_level is None: - self._initial_handler_level = self.handler.level - self.handler.setLevel(level) - initial_disabled_logging_level = self._force_enable_logging(level, logger_obj) - if self._initial_disabled_logging_level is None: - self._initial_disabled_logging_level = initial_disabled_logging_level - - @contextmanager - def at_level(self, level: int | str, logger: str | None = None) -> Generator[None]: - """Context manager that sets the level for capturing of logs. After - the end of the 'with' statement the level is restored to its original - value. - - Will enable the requested logging level if it was disabled via :func:`logging.disable`. - - :param level: The level. - :param logger: The logger to update. If not given, the root logger. - """ - logger_obj = logging.getLogger(logger) - orig_level = logger_obj.level - logger_obj.setLevel(level) - handler_orig_level = self.handler.level - self.handler.setLevel(level) - original_disable_level = self._force_enable_logging(level, logger_obj) - try: - yield - finally: - logger_obj.setLevel(orig_level) - self.handler.setLevel(handler_orig_level) - logging.disable(original_disable_level) - - @contextmanager - def filtering(self, filter_: logging.Filter) -> Generator[None]: - """Context manager that temporarily adds the given filter to the caplog's - :meth:`handler` for the 'with' statement block, and removes that filter at the - end of the block. - - :param filter_: A custom :class:`logging.Filter` object. - - .. versionadded:: 7.5 - """ - self.handler.addFilter(filter_) - try: - yield - finally: - self.handler.removeFilter(filter_) - - -@fixture -def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture]: - """Access and control log capturing. - - Captured logs are available through the following properties/methods:: - - * caplog.messages -> list of format-interpolated log messages - * caplog.text -> string containing formatted log output - * caplog.records -> list of logging.LogRecord instances - * caplog.record_tuples -> list of (logger_name, level, message) tuples - * caplog.clear() -> clear captured records and formatted log output string - """ - result = LogCaptureFixture(request.node, _ispytest=True) - yield result - result._finalize() - - -def get_log_level_for_setting(config: Config, *setting_names: str) -> int | None: - for setting_name in setting_names: - log_level = config.getoption(setting_name) - if log_level is None: - log_level = config.getini(setting_name) - if log_level: - break - else: - return None - - if isinstance(log_level, str): - log_level = log_level.upper() - try: - return int(getattr(logging, log_level, log_level)) - except ValueError as e: - # Python logging does not recognise this as a logging level - raise UsageError( - f"'{log_level}' is not recognized as a logging level name for " - f"'{setting_name}'. Please consider passing the " - "logging level num instead." - ) from e - - -# run after terminalreporter/capturemanager are configured -@hookimpl(trylast=True) -def pytest_configure(config: Config) -> None: - config.pluginmanager.register(LoggingPlugin(config), "logging-plugin") - - -class LoggingPlugin: - """Attaches to the logging module and captures log messages for each test.""" - - def __init__(self, config: Config) -> None: - """Create a new plugin to capture log messages. - - The formatter can be safely shared across all handlers so - create a single one for the entire test session here. - """ - self._config = config - - # Report logging. - self.formatter = self._create_formatter( - get_option_ini(config, "log_format"), - get_option_ini(config, "log_date_format"), - get_option_ini(config, "log_auto_indent"), - ) - self.log_level = get_log_level_for_setting(config, "log_level") - self.caplog_handler = LogCaptureHandler() - self.caplog_handler.setFormatter(self.formatter) - self.report_handler = LogCaptureHandler() - self.report_handler.setFormatter(self.formatter) - - # File logging. - self.log_file_level = get_log_level_for_setting( - config, "log_file_level", "log_level" - ) - log_file = get_option_ini(config, "log_file") or os.devnull - if log_file != os.devnull: - directory = os.path.dirname(os.path.abspath(log_file)) - if not os.path.isdir(directory): - os.makedirs(directory) - - self.log_file_mode = get_option_ini(config, "log_file_mode") or "w" - self.log_file_handler = _FileHandler( - log_file, mode=self.log_file_mode, encoding="UTF-8" - ) - log_file_format = get_option_ini(config, "log_file_format", "log_format") - log_file_date_format = get_option_ini( - config, "log_file_date_format", "log_date_format" - ) - - log_file_formatter = DatetimeFormatter( - log_file_format, datefmt=log_file_date_format - ) - self.log_file_handler.setFormatter(log_file_formatter) - - # CLI/live logging. - self.log_cli_level = get_log_level_for_setting( - config, "log_cli_level", "log_level" - ) - if self._log_cli_enabled(): - terminal_reporter = config.pluginmanager.get_plugin("terminalreporter") - # Guaranteed by `_log_cli_enabled()`. - assert terminal_reporter is not None - capture_manager = config.pluginmanager.get_plugin("capturemanager") - # if capturemanager plugin is disabled, live logging still works. - self.log_cli_handler: ( - _LiveLoggingStreamHandler | _LiveLoggingNullHandler - ) = _LiveLoggingStreamHandler(terminal_reporter, capture_manager) - else: - self.log_cli_handler = _LiveLoggingNullHandler() - log_cli_formatter = self._create_formatter( - get_option_ini(config, "log_cli_format", "log_format"), - get_option_ini(config, "log_cli_date_format", "log_date_format"), - get_option_ini(config, "log_auto_indent"), - ) - self.log_cli_handler.setFormatter(log_cli_formatter) - self._disable_loggers(loggers_to_disable=config.option.logger_disable) - - def _disable_loggers(self, loggers_to_disable: list[str]) -> None: - if not loggers_to_disable: - return - - for name in loggers_to_disable: - logger = logging.getLogger(name) - logger.disabled = True - - def _create_formatter(self, log_format, log_date_format, auto_indent): - # Color option doesn't exist if terminal plugin is disabled. - color = getattr(self._config.option, "color", "no") - if color != "no" and ColoredLevelFormatter.LEVELNAME_FMT_REGEX.search( - log_format - ): - formatter: logging.Formatter = ColoredLevelFormatter( - create_terminal_writer(self._config), log_format, log_date_format - ) - else: - formatter = DatetimeFormatter(log_format, log_date_format) - - formatter._style = PercentStyleMultiline( - formatter._style._fmt, auto_indent=auto_indent - ) - - return formatter - - def set_log_path(self, fname: str) -> None: - """Set the filename parameter for Logging.FileHandler(). - - Creates parent directory if it does not exist. - - .. warning:: - This is an experimental API. - """ - fpath = Path(fname) - - if not fpath.is_absolute(): - fpath = self._config.rootpath / fpath - - if not fpath.parent.exists(): - fpath.parent.mkdir(exist_ok=True, parents=True) - - # https://github.com/python/mypy/issues/11193 - stream: io.TextIOWrapper = fpath.open(mode=self.log_file_mode, encoding="UTF-8") # type: ignore[assignment] - old_stream = self.log_file_handler.setStream(stream) - if old_stream: - old_stream.close() - - def _log_cli_enabled(self) -> bool: - """Return whether live logging is enabled.""" - enabled = self._config.getoption( - "--log-cli-level" - ) is not None or self._config.getini("log_cli") - if not enabled: - return False - - terminal_reporter = self._config.pluginmanager.get_plugin("terminalreporter") - if terminal_reporter is None: - # terminal reporter is disabled e.g. by pytest-xdist. - return False - - return True - - @hookimpl(wrapper=True, tryfirst=True) - def pytest_sessionstart(self) -> Generator[None]: - self.log_cli_handler.set_when("sessionstart") - - with catching_logs(self.log_cli_handler, level=self.log_cli_level): - with catching_logs(self.log_file_handler, level=self.log_file_level): - return (yield) - - @hookimpl(wrapper=True, tryfirst=True) - def pytest_collection(self) -> Generator[None]: - self.log_cli_handler.set_when("collection") - - with catching_logs(self.log_cli_handler, level=self.log_cli_level): - with catching_logs(self.log_file_handler, level=self.log_file_level): - return (yield) - - @hookimpl(wrapper=True) - def pytest_runtestloop(self, session: Session) -> Generator[None, object, object]: - if session.config.option.collectonly: - return (yield) - - if self._log_cli_enabled() and self._config.get_verbosity() < 1: - # The verbose flag is needed to avoid messy test progress output. - self._config.option.verbose = 1 - - with catching_logs(self.log_cli_handler, level=self.log_cli_level): - with catching_logs(self.log_file_handler, level=self.log_file_level): - return (yield) # Run all the tests. - - @hookimpl - def pytest_runtest_logstart(self) -> None: - self.log_cli_handler.reset() - self.log_cli_handler.set_when("start") - - @hookimpl - def pytest_runtest_logreport(self) -> None: - self.log_cli_handler.set_when("logreport") - - def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None]: - """Implement the internals of the pytest_runtest_xxx() hooks.""" - with catching_logs( - self.caplog_handler, - level=self.log_level, - ) as caplog_handler, catching_logs( - self.report_handler, - level=self.log_level, - ) as report_handler: - caplog_handler.reset() - report_handler.reset() - item.stash[caplog_records_key][when] = caplog_handler.records - item.stash[caplog_handler_key] = caplog_handler - - try: - yield - finally: - log = report_handler.stream.getvalue().strip() - item.add_report_section(when, "log", log) - - @hookimpl(wrapper=True) - def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None]: - self.log_cli_handler.set_when("setup") - - empty: dict[str, list[logging.LogRecord]] = {} - item.stash[caplog_records_key] = empty - yield from self._runtest_for(item, "setup") - - @hookimpl(wrapper=True) - def pytest_runtest_call(self, item: nodes.Item) -> Generator[None]: - self.log_cli_handler.set_when("call") - - yield from self._runtest_for(item, "call") - - @hookimpl(wrapper=True) - def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None]: - self.log_cli_handler.set_when("teardown") - - try: - yield from self._runtest_for(item, "teardown") - finally: - del item.stash[caplog_records_key] - del item.stash[caplog_handler_key] - - @hookimpl - def pytest_runtest_logfinish(self) -> None: - self.log_cli_handler.set_when("finish") - - @hookimpl(wrapper=True, tryfirst=True) - def pytest_sessionfinish(self) -> Generator[None]: - self.log_cli_handler.set_when("sessionfinish") - - with catching_logs(self.log_cli_handler, level=self.log_cli_level): - with catching_logs(self.log_file_handler, level=self.log_file_level): - return (yield) - - @hookimpl - def pytest_unconfigure(self) -> None: - # Close the FileHandler explicitly. - # (logging.shutdown might have lost the weakref?!) - self.log_file_handler.close() - - -class _FileHandler(logging.FileHandler): - """A logging FileHandler with pytest tweaks.""" - - def handleError(self, record: logging.LogRecord) -> None: - # Handled by LogCaptureHandler. - pass - - -class _LiveLoggingStreamHandler(logging_StreamHandler): - """A logging StreamHandler used by the live logging feature: it will - write a newline before the first log message in each test. - - During live logging we must also explicitly disable stdout/stderr - capturing otherwise it will get captured and won't appear in the - terminal. - """ - - # Officially stream needs to be a IO[str], but TerminalReporter - # isn't. So force it. - stream: TerminalReporter = None # type: ignore - - def __init__( - self, - terminal_reporter: TerminalReporter, - capture_manager: CaptureManager | None, - ) -> None: - super().__init__(stream=terminal_reporter) # type: ignore[arg-type] - self.capture_manager = capture_manager - self.reset() - self.set_when(None) - self._test_outcome_written = False - - def reset(self) -> None: - """Reset the handler; should be called before the start of each test.""" - self._first_record_emitted = False - - def set_when(self, when: str | None) -> None: - """Prepare for the given test phase (setup/call/teardown).""" - self._when = when - self._section_name_shown = False - if when == "start": - self._test_outcome_written = False - - def emit(self, record: logging.LogRecord) -> None: - ctx_manager = ( - self.capture_manager.global_and_fixture_disabled() - if self.capture_manager - else nullcontext() - ) - with ctx_manager: - if not self._first_record_emitted: - self.stream.write("\n") - self._first_record_emitted = True - elif self._when in ("teardown", "finish"): - if not self._test_outcome_written: - self._test_outcome_written = True - self.stream.write("\n") - if not self._section_name_shown and self._when: - self.stream.section("live log " + self._when, sep="-", bold=True) - self._section_name_shown = True - super().emit(record) - - def handleError(self, record: logging.LogRecord) -> None: - # Handled by LogCaptureHandler. - pass - - -class _LiveLoggingNullHandler(logging.NullHandler): - """A logging handler used when live logging is disabled.""" - - def reset(self) -> None: - pass - - def set_when(self, when: str) -> None: - pass - - def handleError(self, record: logging.LogRecord) -> None: - # Handled by LogCaptureHandler. - pass diff --git a/.venv/lib/python3.12/site-packages/_pytest/main.py b/.venv/lib/python3.12/site-packages/_pytest/main.py deleted file mode 100644 index e5534e98..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/main.py +++ /dev/null @@ -1,1072 +0,0 @@ -"""Core implementation of the testing process: init, session, runtest loop.""" - -from __future__ import annotations - -import argparse -import dataclasses -import fnmatch -import functools -import importlib -import importlib.util -import os -from pathlib import Path -import sys -from typing import AbstractSet -from typing import Callable -from typing import Dict -from typing import final -from typing import Iterable -from typing import Iterator -from typing import Literal -from typing import overload -from typing import Sequence -from typing import TYPE_CHECKING -import warnings - -import pluggy - -from _pytest import nodes -import _pytest._code -from _pytest.config import Config -from _pytest.config import directory_arg -from _pytest.config import ExitCode -from _pytest.config import hookimpl -from _pytest.config import PytestPluginManager -from _pytest.config import UsageError -from _pytest.config.argparsing import Parser -from _pytest.config.compat import PathAwareHookProxy -from _pytest.outcomes import exit -from _pytest.pathlib import absolutepath -from _pytest.pathlib import bestrelpath -from _pytest.pathlib import fnmatch_ex -from _pytest.pathlib import safe_exists -from _pytest.pathlib import scandir -from _pytest.reports import CollectReport -from _pytest.reports import TestReport -from _pytest.runner import collect_one_node -from _pytest.runner import SetupState -from _pytest.warning_types import PytestWarning - - -if TYPE_CHECKING: - from typing_extensions import Self - - from _pytest.fixtures import FixtureManager - - -def pytest_addoption(parser: Parser) -> None: - parser.addini( - "norecursedirs", - "Directory patterns to avoid for recursion", - type="args", - default=[ - "*.egg", - ".*", - "_darcs", - "build", - "CVS", - "dist", - "node_modules", - "venv", - "{arch}", - ], - ) - parser.addini( - "testpaths", - "Directories to search for tests when no files or directories are given on the " - "command line", - type="args", - default=[], - ) - group = parser.getgroup("general", "Running and selection options") - group._addoption( - "-x", - "--exitfirst", - action="store_const", - dest="maxfail", - const=1, - help="Exit instantly on first error or failed test", - ) - group = parser.getgroup("pytest-warnings") - group.addoption( - "-W", - "--pythonwarnings", - action="append", - help="Set which warnings to report, see -W option of Python itself", - ) - parser.addini( - "filterwarnings", - type="linelist", - help="Each line specifies a pattern for " - "warnings.filterwarnings. " - "Processed after -W/--pythonwarnings.", - ) - group._addoption( - "--maxfail", - metavar="num", - action="store", - type=int, - dest="maxfail", - default=0, - help="Exit after first num failures or errors", - ) - group._addoption( - "--strict-config", - action="store_true", - help="Any warnings encountered while parsing the `pytest` section of the " - "configuration file raise errors", - ) - group._addoption( - "--strict-markers", - action="store_true", - help="Markers not registered in the `markers` section of the configuration " - "file raise errors", - ) - group._addoption( - "--strict", - action="store_true", - help="(Deprecated) alias to --strict-markers", - ) - group._addoption( - "-c", - "--config-file", - metavar="FILE", - type=str, - dest="inifilename", - help="Load configuration from `FILE` instead of trying to locate one of the " - "implicit configuration files.", - ) - group._addoption( - "--continue-on-collection-errors", - action="store_true", - default=False, - dest="continue_on_collection_errors", - help="Force test execution even if collection errors occur", - ) - group._addoption( - "--rootdir", - action="store", - dest="rootdir", - help="Define root directory for tests. Can be relative path: 'root_dir', './root_dir', " - "'root_dir/another_dir/'; absolute path: '/home/user/root_dir'; path with variables: " - "'$HOME/root_dir'.", - ) - - group = parser.getgroup("collect", "collection") - group.addoption( - "--collectonly", - "--collect-only", - "--co", - action="store_true", - help="Only collect tests, don't execute them", - ) - group.addoption( - "--pyargs", - action="store_true", - help="Try to interpret all arguments as Python packages", - ) - group.addoption( - "--ignore", - action="append", - metavar="path", - help="Ignore path during collection (multi-allowed)", - ) - group.addoption( - "--ignore-glob", - action="append", - metavar="path", - help="Ignore path pattern during collection (multi-allowed)", - ) - group.addoption( - "--deselect", - action="append", - metavar="nodeid_prefix", - help="Deselect item (via node id prefix) during collection (multi-allowed)", - ) - group.addoption( - "--confcutdir", - dest="confcutdir", - default=None, - metavar="dir", - type=functools.partial(directory_arg, optname="--confcutdir"), - help="Only load conftest.py's relative to specified dir", - ) - group.addoption( - "--noconftest", - action="store_true", - dest="noconftest", - default=False, - help="Don't load any conftest.py files", - ) - group.addoption( - "--keepduplicates", - "--keep-duplicates", - action="store_true", - dest="keepduplicates", - default=False, - help="Keep duplicate tests", - ) - group.addoption( - "--collect-in-virtualenv", - action="store_true", - dest="collect_in_virtualenv", - default=False, - help="Don't ignore tests in a local virtualenv directory", - ) - group.addoption( - "--import-mode", - default="prepend", - choices=["prepend", "append", "importlib"], - dest="importmode", - help="Prepend/append to sys.path when importing test modules and conftest " - "files. Default: prepend.", - ) - parser.addini( - "consider_namespace_packages", - type="bool", - default=False, - help="Consider namespace packages when resolving module names during import", - ) - - group = parser.getgroup("debugconfig", "test session debugging and configuration") - group.addoption( - "--basetemp", - dest="basetemp", - default=None, - type=validate_basetemp, - metavar="dir", - help=( - "Base temporary directory for this test run. " - "(Warning: this directory is removed if it exists.)" - ), - ) - - -def validate_basetemp(path: str) -> str: - # GH 7119 - msg = "basetemp must not be empty, the current working directory or any parent directory of it" - - # empty path - if not path: - raise argparse.ArgumentTypeError(msg) - - def is_ancestor(base: Path, query: Path) -> bool: - """Return whether query is an ancestor of base.""" - if base == query: - return True - return query in base.parents - - # check if path is an ancestor of cwd - if is_ancestor(Path.cwd(), Path(path).absolute()): - raise argparse.ArgumentTypeError(msg) - - # check symlinks for ancestors - if is_ancestor(Path.cwd().resolve(), Path(path).resolve()): - raise argparse.ArgumentTypeError(msg) - - return path - - -def wrap_session( - config: Config, doit: Callable[[Config, Session], int | ExitCode | None] -) -> int | ExitCode: - """Skeleton command line program.""" - session = Session.from_config(config) - session.exitstatus = ExitCode.OK - initstate = 0 - try: - try: - config._do_configure() - initstate = 1 - config.hook.pytest_sessionstart(session=session) - initstate = 2 - session.exitstatus = doit(config, session) or 0 - except UsageError: - session.exitstatus = ExitCode.USAGE_ERROR - raise - except Failed: - session.exitstatus = ExitCode.TESTS_FAILED - except (KeyboardInterrupt, exit.Exception): - excinfo = _pytest._code.ExceptionInfo.from_current() - exitstatus: int | ExitCode = ExitCode.INTERRUPTED - if isinstance(excinfo.value, exit.Exception): - if excinfo.value.returncode is not None: - exitstatus = excinfo.value.returncode - if initstate < 2: - sys.stderr.write(f"{excinfo.typename}: {excinfo.value.msg}\n") - config.hook.pytest_keyboard_interrupt(excinfo=excinfo) - session.exitstatus = exitstatus - except BaseException: - session.exitstatus = ExitCode.INTERNAL_ERROR - excinfo = _pytest._code.ExceptionInfo.from_current() - try: - config.notify_exception(excinfo, config.option) - except exit.Exception as exc: - if exc.returncode is not None: - session.exitstatus = exc.returncode - sys.stderr.write(f"{type(exc).__name__}: {exc}\n") - else: - if isinstance(excinfo.value, SystemExit): - sys.stderr.write("mainloop: caught unexpected SystemExit!\n") - - finally: - # Explicitly break reference cycle. - excinfo = None # type: ignore - os.chdir(session.startpath) - if initstate >= 2: - try: - config.hook.pytest_sessionfinish( - session=session, exitstatus=session.exitstatus - ) - except exit.Exception as exc: - if exc.returncode is not None: - session.exitstatus = exc.returncode - sys.stderr.write(f"{type(exc).__name__}: {exc}\n") - config._ensure_unconfigure() - return session.exitstatus - - -def pytest_cmdline_main(config: Config) -> int | ExitCode: - return wrap_session(config, _main) - - -def _main(config: Config, session: Session) -> int | ExitCode | None: - """Default command line protocol for initialization, session, - running tests and reporting.""" - config.hook.pytest_collection(session=session) - config.hook.pytest_runtestloop(session=session) - - if session.testsfailed: - return ExitCode.TESTS_FAILED - elif session.testscollected == 0: - return ExitCode.NO_TESTS_COLLECTED - return None - - -def pytest_collection(session: Session) -> None: - session.perform_collect() - - -def pytest_runtestloop(session: Session) -> bool: - if session.testsfailed and not session.config.option.continue_on_collection_errors: - raise session.Interrupted( - "%d error%s during collection" - % (session.testsfailed, "s" if session.testsfailed != 1 else "") - ) - - if session.config.option.collectonly: - return True - - for i, item in enumerate(session.items): - nextitem = session.items[i + 1] if i + 1 < len(session.items) else None - item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) - if session.shouldfail: - raise session.Failed(session.shouldfail) - if session.shouldstop: - raise session.Interrupted(session.shouldstop) - return True - - -def _in_venv(path: Path) -> bool: - """Attempt to detect if ``path`` is the root of a Virtual Environment by - checking for the existence of the pyvenv.cfg file. - - [https://peps.python.org/pep-0405/] - - For regression protection we also check for conda environments that do not include pyenv.cfg yet -- - https://github.com/conda/conda/issues/13337 is the conda issue tracking adding pyenv.cfg. - - Checking for the `conda-meta/history` file per https://github.com/pytest-dev/pytest/issues/12652#issuecomment-2246336902. - - """ - try: - return ( - path.joinpath("pyvenv.cfg").is_file() - or path.joinpath("conda-meta", "history").is_file() - ) - except OSError: - return False - - -def pytest_ignore_collect(collection_path: Path, config: Config) -> bool | None: - if collection_path.name == "__pycache__": - return True - - ignore_paths = config._getconftest_pathlist( - "collect_ignore", path=collection_path.parent - ) - ignore_paths = ignore_paths or [] - excludeopt = config.getoption("ignore") - if excludeopt: - ignore_paths.extend(absolutepath(x) for x in excludeopt) - - if collection_path in ignore_paths: - return True - - ignore_globs = config._getconftest_pathlist( - "collect_ignore_glob", path=collection_path.parent - ) - ignore_globs = ignore_globs or [] - excludeglobopt = config.getoption("ignore_glob") - if excludeglobopt: - ignore_globs.extend(absolutepath(x) for x in excludeglobopt) - - if any(fnmatch.fnmatch(str(collection_path), str(glob)) for glob in ignore_globs): - return True - - allow_in_venv = config.getoption("collect_in_virtualenv") - if not allow_in_venv and _in_venv(collection_path): - return True - - if collection_path.is_dir(): - norecursepatterns = config.getini("norecursedirs") - if any(fnmatch_ex(pat, collection_path) for pat in norecursepatterns): - return True - - return None - - -def pytest_collect_directory( - path: Path, parent: nodes.Collector -) -> nodes.Collector | None: - return Dir.from_parent(parent, path=path) - - -def pytest_collection_modifyitems(items: list[nodes.Item], config: Config) -> None: - deselect_prefixes = tuple(config.getoption("deselect") or []) - if not deselect_prefixes: - return - - remaining = [] - deselected = [] - for colitem in items: - if colitem.nodeid.startswith(deselect_prefixes): - deselected.append(colitem) - else: - remaining.append(colitem) - - if deselected: - config.hook.pytest_deselected(items=deselected) - items[:] = remaining - - -class FSHookProxy: - def __init__( - self, - pm: PytestPluginManager, - remove_mods: AbstractSet[object], - ) -> None: - self.pm = pm - self.remove_mods = remove_mods - - def __getattr__(self, name: str) -> pluggy.HookCaller: - x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods) - self.__dict__[name] = x - return x - - -class Interrupted(KeyboardInterrupt): - """Signals that the test run was interrupted.""" - - __module__ = "builtins" # For py3. - - -class Failed(Exception): - """Signals a stop as failed test run.""" - - -@dataclasses.dataclass -class _bestrelpath_cache(Dict[Path, str]): - __slots__ = ("path",) - - path: Path - - def __missing__(self, path: Path) -> str: - r = bestrelpath(self.path, path) - self[path] = r - return r - - -@final -class Dir(nodes.Directory): - """Collector of files in a file system directory. - - .. versionadded:: 8.0 - - .. note:: - - Python directories with an `__init__.py` file are instead collected by - :class:`~pytest.Package` by default. Both are :class:`~pytest.Directory` - collectors. - """ - - @classmethod - def from_parent( # type: ignore[override] - cls, - parent: nodes.Collector, - *, - path: Path, - ) -> Self: - """The public constructor. - - :param parent: The parent collector of this Dir. - :param path: The directory's path. - :type path: pathlib.Path - """ - return super().from_parent(parent=parent, path=path) - - def collect(self) -> Iterable[nodes.Item | nodes.Collector]: - config = self.config - col: nodes.Collector | None - cols: Sequence[nodes.Collector] - ihook = self.ihook - for direntry in scandir(self.path): - if direntry.is_dir(): - path = Path(direntry.path) - if not self.session.isinitpath(path, with_parents=True): - if ihook.pytest_ignore_collect(collection_path=path, config=config): - continue - col = ihook.pytest_collect_directory(path=path, parent=self) - if col is not None: - yield col - - elif direntry.is_file(): - path = Path(direntry.path) - if not self.session.isinitpath(path): - if ihook.pytest_ignore_collect(collection_path=path, config=config): - continue - cols = ihook.pytest_collect_file(file_path=path, parent=self) - yield from cols - - -@final -class Session(nodes.Collector): - """The root of the collection tree. - - ``Session`` collects the initial paths given as arguments to pytest. - """ - - Interrupted = Interrupted - Failed = Failed - # Set on the session by runner.pytest_sessionstart. - _setupstate: SetupState - # Set on the session by fixtures.pytest_sessionstart. - _fixturemanager: FixtureManager - exitstatus: int | ExitCode - - def __init__(self, config: Config) -> None: - super().__init__( - name="", - path=config.rootpath, - fspath=None, - parent=None, - config=config, - session=self, - nodeid="", - ) - self.testsfailed = 0 - self.testscollected = 0 - self._shouldstop: bool | str = False - self._shouldfail: bool | str = False - self.trace = config.trace.root.get("collection") - self._initialpaths: frozenset[Path] = frozenset() - self._initialpaths_with_parents: frozenset[Path] = frozenset() - self._notfound: list[tuple[str, Sequence[nodes.Collector]]] = [] - self._initial_parts: list[CollectionArgument] = [] - self._collection_cache: dict[nodes.Collector, CollectReport] = {} - self.items: list[nodes.Item] = [] - - self._bestrelpathcache: dict[Path, str] = _bestrelpath_cache(config.rootpath) - - self.config.pluginmanager.register(self, name="session") - - @classmethod - def from_config(cls, config: Config) -> Session: - session: Session = cls._create(config=config) - return session - - def __repr__(self) -> str: - return "<%s %s exitstatus=%r testsfailed=%d testscollected=%d>" % ( - self.__class__.__name__, - self.name, - getattr(self, "exitstatus", ""), - self.testsfailed, - self.testscollected, - ) - - @property - def shouldstop(self) -> bool | str: - return self._shouldstop - - @shouldstop.setter - def shouldstop(self, value: bool | str) -> None: - # The runner checks shouldfail and assumes that if it is set we are - # definitely stopping, so prevent unsetting it. - if value is False and self._shouldstop: - warnings.warn( - PytestWarning( - "session.shouldstop cannot be unset after it has been set; ignoring." - ), - stacklevel=2, - ) - return - self._shouldstop = value - - @property - def shouldfail(self) -> bool | str: - return self._shouldfail - - @shouldfail.setter - def shouldfail(self, value: bool | str) -> None: - # The runner checks shouldfail and assumes that if it is set we are - # definitely stopping, so prevent unsetting it. - if value is False and self._shouldfail: - warnings.warn( - PytestWarning( - "session.shouldfail cannot be unset after it has been set; ignoring." - ), - stacklevel=2, - ) - return - self._shouldfail = value - - @property - def startpath(self) -> Path: - """The path from which pytest was invoked. - - .. versionadded:: 7.0.0 - """ - return self.config.invocation_params.dir - - def _node_location_to_relpath(self, node_path: Path) -> str: - # bestrelpath is a quite slow function. - return self._bestrelpathcache[node_path] - - @hookimpl(tryfirst=True) - def pytest_collectstart(self) -> None: - if self.shouldfail: - raise self.Failed(self.shouldfail) - if self.shouldstop: - raise self.Interrupted(self.shouldstop) - - @hookimpl(tryfirst=True) - def pytest_runtest_logreport(self, report: TestReport | CollectReport) -> None: - if report.failed and not hasattr(report, "wasxfail"): - self.testsfailed += 1 - maxfail = self.config.getvalue("maxfail") - if maxfail and self.testsfailed >= maxfail: - self.shouldfail = "stopping after %d failures" % (self.testsfailed) - - pytest_collectreport = pytest_runtest_logreport - - def isinitpath( - self, - path: str | os.PathLike[str], - *, - with_parents: bool = False, - ) -> bool: - """Is path an initial path? - - An initial path is a path explicitly given to pytest on the command - line. - - :param with_parents: - If set, also return True if the path is a parent of an initial path. - - .. versionchanged:: 8.0 - Added the ``with_parents`` parameter. - """ - # Optimization: Path(Path(...)) is much slower than isinstance. - path_ = path if isinstance(path, Path) else Path(path) - if with_parents: - return path_ in self._initialpaths_with_parents - else: - return path_ in self._initialpaths - - def gethookproxy(self, fspath: os.PathLike[str]) -> pluggy.HookRelay: - # Optimization: Path(Path(...)) is much slower than isinstance. - path = fspath if isinstance(fspath, Path) else Path(fspath) - pm = self.config.pluginmanager - # Check if we have the common case of running - # hooks with all conftest.py files. - my_conftestmodules = pm._getconftestmodules(path) - remove_mods = pm._conftest_plugins.difference(my_conftestmodules) - proxy: pluggy.HookRelay - if remove_mods: - # One or more conftests are not in use at this path. - proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods)) # type: ignore[arg-type,assignment] - else: - # All plugins are active for this fspath. - proxy = self.config.hook - return proxy - - def _collect_path( - self, - path: Path, - path_cache: dict[Path, Sequence[nodes.Collector]], - ) -> Sequence[nodes.Collector]: - """Create a Collector for the given path. - - `path_cache` makes it so the same Collectors are returned for the same - path. - """ - if path in path_cache: - return path_cache[path] - - if path.is_dir(): - ihook = self.gethookproxy(path.parent) - col: nodes.Collector | None = ihook.pytest_collect_directory( - path=path, parent=self - ) - cols: Sequence[nodes.Collector] = (col,) if col is not None else () - - elif path.is_file(): - ihook = self.gethookproxy(path) - cols = ihook.pytest_collect_file(file_path=path, parent=self) - - else: - # Broken symlink or invalid/missing file. - cols = () - - path_cache[path] = cols - return cols - - @overload - def perform_collect( - self, args: Sequence[str] | None = ..., genitems: Literal[True] = ... - ) -> Sequence[nodes.Item]: ... - - @overload - def perform_collect( - self, args: Sequence[str] | None = ..., genitems: bool = ... - ) -> Sequence[nodes.Item | nodes.Collector]: ... - - def perform_collect( - self, args: Sequence[str] | None = None, genitems: bool = True - ) -> Sequence[nodes.Item | nodes.Collector]: - """Perform the collection phase for this session. - - This is called by the default :hook:`pytest_collection` hook - implementation; see the documentation of this hook for more details. - For testing purposes, it may also be called directly on a fresh - ``Session``. - - This function normally recursively expands any collectors collected - from the session to their items, and only items are returned. For - testing purposes, this may be suppressed by passing ``genitems=False``, - in which case the return value contains these collectors unexpanded, - and ``session.items`` is empty. - """ - if args is None: - args = self.config.args - - self.trace("perform_collect", self, args) - self.trace.root.indent += 1 - - hook = self.config.hook - - self._notfound = [] - self._initial_parts = [] - self._collection_cache = {} - self.items = [] - items: Sequence[nodes.Item | nodes.Collector] = self.items - try: - initialpaths: list[Path] = [] - initialpaths_with_parents: list[Path] = [] - for arg in args: - collection_argument = resolve_collection_argument( - self.config.invocation_params.dir, - arg, - as_pypath=self.config.option.pyargs, - ) - self._initial_parts.append(collection_argument) - initialpaths.append(collection_argument.path) - initialpaths_with_parents.append(collection_argument.path) - initialpaths_with_parents.extend(collection_argument.path.parents) - self._initialpaths = frozenset(initialpaths) - self._initialpaths_with_parents = frozenset(initialpaths_with_parents) - - rep = collect_one_node(self) - self.ihook.pytest_collectreport(report=rep) - self.trace.root.indent -= 1 - if self._notfound: - errors = [] - for arg, collectors in self._notfound: - if collectors: - errors.append( - f"not found: {arg}\n(no match in any of {collectors!r})" - ) - else: - errors.append(f"found no collectors for {arg}") - - raise UsageError(*errors) - - if not genitems: - items = rep.result - else: - if rep.passed: - for node in rep.result: - self.items.extend(self.genitems(node)) - - self.config.pluginmanager.check_pending() - hook.pytest_collection_modifyitems( - session=self, config=self.config, items=items - ) - finally: - self._notfound = [] - self._initial_parts = [] - self._collection_cache = {} - hook.pytest_collection_finish(session=self) - - if genitems: - self.testscollected = len(items) - - return items - - def _collect_one_node( - self, - node: nodes.Collector, - handle_dupes: bool = True, - ) -> tuple[CollectReport, bool]: - if node in self._collection_cache and handle_dupes: - rep = self._collection_cache[node] - return rep, True - else: - rep = collect_one_node(node) - self._collection_cache[node] = rep - return rep, False - - def collect(self) -> Iterator[nodes.Item | nodes.Collector]: - # This is a cache for the root directories of the initial paths. - # We can't use collection_cache for Session because of its special - # role as the bootstrapping collector. - path_cache: dict[Path, Sequence[nodes.Collector]] = {} - - pm = self.config.pluginmanager - - for collection_argument in self._initial_parts: - self.trace("processing argument", collection_argument) - self.trace.root.indent += 1 - - argpath = collection_argument.path - names = collection_argument.parts - module_name = collection_argument.module_name - - # resolve_collection_argument() ensures this. - if argpath.is_dir(): - assert not names, f"invalid arg {(argpath, names)!r}" - - paths = [argpath] - # Add relevant parents of the path, from the root, e.g. - # /a/b/c.py -> [/, /a, /a/b, /a/b/c.py] - if module_name is None: - # Paths outside of the confcutdir should not be considered. - for path in argpath.parents: - if not pm._is_in_confcutdir(path): - break - paths.insert(0, path) - else: - # For --pyargs arguments, only consider paths matching the module - # name. Paths beyond the package hierarchy are not included. - module_name_parts = module_name.split(".") - for i, path in enumerate(argpath.parents, 2): - if i > len(module_name_parts) or path.stem != module_name_parts[-i]: - break - paths.insert(0, path) - - # Start going over the parts from the root, collecting each level - # and discarding all nodes which don't match the level's part. - any_matched_in_initial_part = False - notfound_collectors = [] - work: list[tuple[nodes.Collector | nodes.Item, list[Path | str]]] = [ - (self, [*paths, *names]) - ] - while work: - matchnode, matchparts = work.pop() - - # Pop'd all of the parts, this is a match. - if not matchparts: - yield matchnode - any_matched_in_initial_part = True - continue - - # Should have been matched by now, discard. - if not isinstance(matchnode, nodes.Collector): - continue - - # Collect this level of matching. - # Collecting Session (self) is done directly to avoid endless - # recursion to this function. - subnodes: Sequence[nodes.Collector | nodes.Item] - if isinstance(matchnode, Session): - assert isinstance(matchparts[0], Path) - subnodes = matchnode._collect_path(matchparts[0], path_cache) - else: - # For backward compat, files given directly multiple - # times on the command line should not be deduplicated. - handle_dupes = not ( - len(matchparts) == 1 - and isinstance(matchparts[0], Path) - and matchparts[0].is_file() - ) - rep, duplicate = self._collect_one_node(matchnode, handle_dupes) - if not duplicate and not rep.passed: - # Report collection failures here to avoid failing to - # run some test specified in the command line because - # the module could not be imported (#134). - matchnode.ihook.pytest_collectreport(report=rep) - if not rep.passed: - continue - subnodes = rep.result - - # Prune this level. - any_matched_in_collector = False - for node in reversed(subnodes): - # Path part e.g. `/a/b/` in `/a/b/test_file.py::TestIt::test_it`. - if isinstance(matchparts[0], Path): - is_match = node.path == matchparts[0] - if sys.platform == "win32" and not is_match: - # In case the file paths do not match, fallback to samefile() to - # account for short-paths on Windows (#11895). - same_file = os.path.samefile(node.path, matchparts[0]) - # We don't want to match links to the current node, - # otherwise we would match the same file more than once (#12039). - is_match = same_file and ( - os.path.islink(node.path) - == os.path.islink(matchparts[0]) - ) - - # Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`. - else: - # TODO: Remove parametrized workaround once collection structure contains - # parametrization. - is_match = ( - node.name == matchparts[0] - or node.name.split("[")[0] == matchparts[0] - ) - if is_match: - work.append((node, matchparts[1:])) - any_matched_in_collector = True - - if not any_matched_in_collector: - notfound_collectors.append(matchnode) - - if not any_matched_in_initial_part: - report_arg = "::".join((str(argpath), *names)) - self._notfound.append((report_arg, notfound_collectors)) - - self.trace.root.indent -= 1 - - def genitems(self, node: nodes.Item | nodes.Collector) -> Iterator[nodes.Item]: - self.trace("genitems", node) - if isinstance(node, nodes.Item): - node.ihook.pytest_itemcollected(item=node) - yield node - else: - assert isinstance(node, nodes.Collector) - keepduplicates = self.config.getoption("keepduplicates") - # For backward compat, dedup only applies to files. - handle_dupes = not (keepduplicates and isinstance(node, nodes.File)) - rep, duplicate = self._collect_one_node(node, handle_dupes) - if duplicate and not keepduplicates: - return - if rep.passed: - for subnode in rep.result: - yield from self.genitems(subnode) - if not duplicate: - node.ihook.pytest_collectreport(report=rep) - - -def search_pypath(module_name: str) -> str | None: - """Search sys.path for the given a dotted module name, and return its file - system path if found.""" - try: - spec = importlib.util.find_spec(module_name) - # AttributeError: looks like package module, but actually filename - # ImportError: module does not exist - # ValueError: not a module name - except (AttributeError, ImportError, ValueError): - return None - if spec is None or spec.origin is None or spec.origin == "namespace": - return None - elif spec.submodule_search_locations: - return os.path.dirname(spec.origin) - else: - return spec.origin - - -@dataclasses.dataclass(frozen=True) -class CollectionArgument: - """A resolved collection argument.""" - - path: Path - parts: Sequence[str] - module_name: str | None - - -def resolve_collection_argument( - invocation_path: Path, arg: str, *, as_pypath: bool = False -) -> CollectionArgument: - """Parse path arguments optionally containing selection parts and return (fspath, names). - - Command-line arguments can point to files and/or directories, and optionally contain - parts for specific tests selection, for example: - - "pkg/tests/test_foo.py::TestClass::test_foo" - - This function ensures the path exists, and returns a resolved `CollectionArgument`: - - CollectionArgument( - path=Path("/full/path/to/pkg/tests/test_foo.py"), - parts=["TestClass", "test_foo"], - module_name=None, - ) - - When as_pypath is True, expects that the command-line argument actually contains - module paths instead of file-system paths: - - "pkg.tests.test_foo::TestClass::test_foo" - - In which case we search sys.path for a matching module, and then return the *path* to the - found module, which may look like this: - - CollectionArgument( - path=Path("/home/u/myvenv/lib/site-packages/pkg/tests/test_foo.py"), - parts=["TestClass", "test_foo"], - module_name="pkg.tests.test_foo", - ) - - If the path doesn't exist, raise UsageError. - If the path is a directory and selection parts are present, raise UsageError. - """ - base, squacket, rest = str(arg).partition("[") - strpath, *parts = base.split("::") - if parts: - parts[-1] = f"{parts[-1]}{squacket}{rest}" - module_name = None - if as_pypath: - pyarg_strpath = search_pypath(strpath) - if pyarg_strpath is not None: - module_name = strpath - strpath = pyarg_strpath - fspath = invocation_path / strpath - fspath = absolutepath(fspath) - if not safe_exists(fspath): - msg = ( - "module or package not found: {arg} (missing __init__.py?)" - if as_pypath - else "file or directory not found: {arg}" - ) - raise UsageError(msg.format(arg=arg)) - if parts and fspath.is_dir(): - msg = ( - "package argument cannot contain :: selection parts: {arg}" - if as_pypath - else "directory argument cannot contain :: selection parts: {arg}" - ) - raise UsageError(msg.format(arg=arg)) - return CollectionArgument( - path=fspath, - parts=parts, - module_name=module_name, - ) diff --git a/.venv/lib/python3.12/site-packages/_pytest/mark/__init__.py b/.venv/lib/python3.12/site-packages/_pytest/mark/__init__.py deleted file mode 100644 index a4f942c5..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/mark/__init__.py +++ /dev/null @@ -1,292 +0,0 @@ -"""Generic mechanism for marking and selecting python functions.""" - -from __future__ import annotations - -import collections -import dataclasses -from typing import AbstractSet -from typing import Collection -from typing import Iterable -from typing import Optional -from typing import TYPE_CHECKING - -from .expression import Expression -from .expression import ParseError -from .structures import EMPTY_PARAMETERSET_OPTION -from .structures import get_empty_parameterset_mark -from .structures import Mark -from .structures import MARK_GEN -from .structures import MarkDecorator -from .structures import MarkGenerator -from .structures import ParameterSet -from _pytest.config import Config -from _pytest.config import ExitCode -from _pytest.config import hookimpl -from _pytest.config import UsageError -from _pytest.config.argparsing import NOT_SET -from _pytest.config.argparsing import Parser -from _pytest.stash import StashKey - - -if TYPE_CHECKING: - from _pytest.nodes import Item - - -__all__ = [ - "MARK_GEN", - "Mark", - "MarkDecorator", - "MarkGenerator", - "ParameterSet", - "get_empty_parameterset_mark", -] - - -old_mark_config_key = StashKey[Optional[Config]]() - - -def param( - *values: object, - marks: MarkDecorator | Collection[MarkDecorator | Mark] = (), - id: str | None = None, -) -> ParameterSet: - """Specify a parameter in `pytest.mark.parametrize`_ calls or - :ref:`parametrized fixtures `. - - .. code-block:: python - - @pytest.mark.parametrize( - "test_input,expected", - [ - ("3+5", 8), - pytest.param("6*9", 42, marks=pytest.mark.xfail), - ], - ) - def test_eval(test_input, expected): - assert eval(test_input) == expected - - :param values: Variable args of the values of the parameter set, in order. - :param marks: A single mark or a list of marks to be applied to this parameter set. - :param id: The id to attribute to this parameter set. - """ - return ParameterSet.param(*values, marks=marks, id=id) - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") - group._addoption( - "-k", - action="store", - dest="keyword", - default="", - metavar="EXPRESSION", - help="Only run tests which match the given substring expression. " - "An expression is a Python evaluable expression " - "where all names are substring-matched against test names " - "and their parent classes. Example: -k 'test_method or test_" - "other' matches all test functions and classes whose name " - "contains 'test_method' or 'test_other', while -k 'not test_method' " - "matches those that don't contain 'test_method' in their names. " - "-k 'not test_method and not test_other' will eliminate the matches. " - "Additionally keywords are matched to classes and functions " - "containing extra names in their 'extra_keyword_matches' set, " - "as well as functions which have names assigned directly to them. " - "The matching is case-insensitive.", - ) - - group._addoption( - "-m", - action="store", - dest="markexpr", - default="", - metavar="MARKEXPR", - help="Only run tests matching given mark expression. " - "For example: -m 'mark1 and not mark2'.", - ) - - group.addoption( - "--markers", - action="store_true", - help="show markers (builtin, plugin and per-project ones).", - ) - - parser.addini("markers", "Register new markers for test functions", "linelist") - parser.addini(EMPTY_PARAMETERSET_OPTION, "Default marker for empty parametersets") - - -@hookimpl(tryfirst=True) -def pytest_cmdline_main(config: Config) -> int | ExitCode | None: - import _pytest.config - - if config.option.markers: - config._do_configure() - tw = _pytest.config.create_terminal_writer(config) - for line in config.getini("markers"): - parts = line.split(":", 1) - name = parts[0] - rest = parts[1] if len(parts) == 2 else "" - tw.write(f"@pytest.mark.{name}:", bold=True) - tw.line(rest) - tw.line() - config._ensure_unconfigure() - return 0 - - return None - - -@dataclasses.dataclass -class KeywordMatcher: - """A matcher for keywords. - - Given a list of names, matches any substring of one of these names. The - string inclusion check is case-insensitive. - - Will match on the name of colitem, including the names of its parents. - Only matches names of items which are either a :class:`Class` or a - :class:`Function`. - - Additionally, matches on names in the 'extra_keyword_matches' set of - any item, as well as names directly assigned to test functions. - """ - - __slots__ = ("_names",) - - _names: AbstractSet[str] - - @classmethod - def from_item(cls, item: Item) -> KeywordMatcher: - mapped_names = set() - - # Add the names of the current item and any parent items, - # except the Session and root Directory's which are not - # interesting for matching. - import pytest - - for node in item.listchain(): - if isinstance(node, pytest.Session): - continue - if isinstance(node, pytest.Directory) and isinstance( - node.parent, pytest.Session - ): - continue - mapped_names.add(node.name) - - # Add the names added as extra keywords to current or parent items. - mapped_names.update(item.listextrakeywords()) - - # Add the names attached to the current function through direct assignment. - function_obj = getattr(item, "function", None) - if function_obj: - mapped_names.update(function_obj.__dict__) - - # Add the markers to the keywords as we no longer handle them correctly. - mapped_names.update(mark.name for mark in item.iter_markers()) - - return cls(mapped_names) - - def __call__(self, subname: str, /, **kwargs: str | int | bool | None) -> bool: - if kwargs: - raise UsageError("Keyword expressions do not support call parameters.") - subname = subname.lower() - names = (name.lower() for name in self._names) - - for name in names: - if subname in name: - return True - return False - - -def deselect_by_keyword(items: list[Item], config: Config) -> None: - keywordexpr = config.option.keyword.lstrip() - if not keywordexpr: - return - - expr = _parse_expression(keywordexpr, "Wrong expression passed to '-k'") - - remaining = [] - deselected = [] - for colitem in items: - if not expr.evaluate(KeywordMatcher.from_item(colitem)): - deselected.append(colitem) - else: - remaining.append(colitem) - - if deselected: - config.hook.pytest_deselected(items=deselected) - items[:] = remaining - - -@dataclasses.dataclass -class MarkMatcher: - """A matcher for markers which are present. - - Tries to match on any marker names, attached to the given colitem. - """ - - __slots__ = ("own_mark_name_mapping",) - - own_mark_name_mapping: dict[str, list[Mark]] - - @classmethod - def from_markers(cls, markers: Iterable[Mark]) -> MarkMatcher: - mark_name_mapping = collections.defaultdict(list) - for mark in markers: - mark_name_mapping[mark.name].append(mark) - return cls(mark_name_mapping) - - def __call__(self, name: str, /, **kwargs: str | int | bool | None) -> bool: - if not (matches := self.own_mark_name_mapping.get(name, [])): - return False - - for mark in matches: - if all(mark.kwargs.get(k, NOT_SET) == v for k, v in kwargs.items()): - return True - - return False - - -def deselect_by_mark(items: list[Item], config: Config) -> None: - matchexpr = config.option.markexpr - if not matchexpr: - return - - expr = _parse_expression(matchexpr, "Wrong expression passed to '-m'") - remaining: list[Item] = [] - deselected: list[Item] = [] - for item in items: - if expr.evaluate(MarkMatcher.from_markers(item.iter_markers())): - remaining.append(item) - else: - deselected.append(item) - if deselected: - config.hook.pytest_deselected(items=deselected) - items[:] = remaining - - -def _parse_expression(expr: str, exc_message: str) -> Expression: - try: - return Expression.compile(expr) - except ParseError as e: - raise UsageError(f"{exc_message}: {expr}: {e}") from None - - -def pytest_collection_modifyitems(items: list[Item], config: Config) -> None: - deselect_by_keyword(items, config) - deselect_by_mark(items, config) - - -def pytest_configure(config: Config) -> None: - config.stash[old_mark_config_key] = MARK_GEN._config - MARK_GEN._config = config - - empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION) - - if empty_parameterset not in ("skip", "xfail", "fail_at_collect", None, ""): - raise UsageError( - f"{EMPTY_PARAMETERSET_OPTION!s} must be one of skip, xfail or fail_at_collect" - f" but it is {empty_parameterset!r}" - ) - - -def pytest_unconfigure(config: Config) -> None: - MARK_GEN._config = config.stash.get(old_mark_config_key, None) diff --git a/.venv/lib/python3.12/site-packages/_pytest/mark/expression.py b/.venv/lib/python3.12/site-packages/_pytest/mark/expression.py deleted file mode 100644 index 89cc0e94..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/mark/expression.py +++ /dev/null @@ -1,333 +0,0 @@ -r"""Evaluate match expressions, as used by `-k` and `-m`. - -The grammar is: - -expression: expr? EOF -expr: and_expr ('or' and_expr)* -and_expr: not_expr ('and' not_expr)* -not_expr: 'not' not_expr | '(' expr ')' | ident kwargs? - -ident: (\w|:|\+|-|\.|\[|\]|\\|/)+ -kwargs: ('(' name '=' value ( ', ' name '=' value )* ')') -name: a valid ident, but not a reserved keyword -value: (unescaped) string literal | (-)?[0-9]+ | 'False' | 'True' | 'None' - -The semantics are: - -- Empty expression evaluates to False. -- ident evaluates to True or False according to a provided matcher function. -- or/and/not evaluate according to the usual boolean semantics. -- ident with parentheses and keyword arguments evaluates to True or False according to a provided matcher function. -""" - -from __future__ import annotations - -import ast -import dataclasses -import enum -import keyword -import re -import types -from typing import Iterator -from typing import Literal -from typing import Mapping -from typing import NoReturn -from typing import overload -from typing import Protocol -from typing import Sequence - - -__all__ = [ - "Expression", - "ParseError", -] - - -class TokenType(enum.Enum): - LPAREN = "left parenthesis" - RPAREN = "right parenthesis" - OR = "or" - AND = "and" - NOT = "not" - IDENT = "identifier" - EOF = "end of input" - EQUAL = "=" - STRING = "string literal" - COMMA = "," - - -@dataclasses.dataclass(frozen=True) -class Token: - __slots__ = ("type", "value", "pos") - type: TokenType - value: str - pos: int - - -class ParseError(Exception): - """The expression contains invalid syntax. - - :param column: The column in the line where the error occurred (1-based). - :param message: A description of the error. - """ - - def __init__(self, column: int, message: str) -> None: - self.column = column - self.message = message - - def __str__(self) -> str: - return f"at column {self.column}: {self.message}" - - -class Scanner: - __slots__ = ("tokens", "current") - - def __init__(self, input: str) -> None: - self.tokens = self.lex(input) - self.current = next(self.tokens) - - def lex(self, input: str) -> Iterator[Token]: - pos = 0 - while pos < len(input): - if input[pos] in (" ", "\t"): - pos += 1 - elif input[pos] == "(": - yield Token(TokenType.LPAREN, "(", pos) - pos += 1 - elif input[pos] == ")": - yield Token(TokenType.RPAREN, ")", pos) - pos += 1 - elif input[pos] == "=": - yield Token(TokenType.EQUAL, "=", pos) - pos += 1 - elif input[pos] == ",": - yield Token(TokenType.COMMA, ",", pos) - pos += 1 - elif (quote_char := input[pos]) in ("'", '"'): - end_quote_pos = input.find(quote_char, pos + 1) - if end_quote_pos == -1: - raise ParseError( - pos + 1, - f'closing quote "{quote_char}" is missing', - ) - value = input[pos : end_quote_pos + 1] - if (backslash_pos := input.find("\\")) != -1: - raise ParseError( - backslash_pos + 1, - r'escaping with "\" not supported in marker expression', - ) - yield Token(TokenType.STRING, value, pos) - pos += len(value) - else: - match = re.match(r"(:?\w|:|\+|-|\.|\[|\]|\\|/)+", input[pos:]) - if match: - value = match.group(0) - if value == "or": - yield Token(TokenType.OR, value, pos) - elif value == "and": - yield Token(TokenType.AND, value, pos) - elif value == "not": - yield Token(TokenType.NOT, value, pos) - else: - yield Token(TokenType.IDENT, value, pos) - pos += len(value) - else: - raise ParseError( - pos + 1, - f'unexpected character "{input[pos]}"', - ) - yield Token(TokenType.EOF, "", pos) - - @overload - def accept(self, type: TokenType, *, reject: Literal[True]) -> Token: ... - - @overload - def accept( - self, type: TokenType, *, reject: Literal[False] = False - ) -> Token | None: ... - - def accept(self, type: TokenType, *, reject: bool = False) -> Token | None: - if self.current.type is type: - token = self.current - if token.type is not TokenType.EOF: - self.current = next(self.tokens) - return token - if reject: - self.reject((type,)) - return None - - def reject(self, expected: Sequence[TokenType]) -> NoReturn: - raise ParseError( - self.current.pos + 1, - "expected {}; got {}".format( - " OR ".join(type.value for type in expected), - self.current.type.value, - ), - ) - - -# True, False and None are legal match expression identifiers, -# but illegal as Python identifiers. To fix this, this prefix -# is added to identifiers in the conversion to Python AST. -IDENT_PREFIX = "$" - - -def expression(s: Scanner) -> ast.Expression: - if s.accept(TokenType.EOF): - ret: ast.expr = ast.Constant(False) - else: - ret = expr(s) - s.accept(TokenType.EOF, reject=True) - return ast.fix_missing_locations(ast.Expression(ret)) - - -def expr(s: Scanner) -> ast.expr: - ret = and_expr(s) - while s.accept(TokenType.OR): - rhs = and_expr(s) - ret = ast.BoolOp(ast.Or(), [ret, rhs]) - return ret - - -def and_expr(s: Scanner) -> ast.expr: - ret = not_expr(s) - while s.accept(TokenType.AND): - rhs = not_expr(s) - ret = ast.BoolOp(ast.And(), [ret, rhs]) - return ret - - -def not_expr(s: Scanner) -> ast.expr: - if s.accept(TokenType.NOT): - return ast.UnaryOp(ast.Not(), not_expr(s)) - if s.accept(TokenType.LPAREN): - ret = expr(s) - s.accept(TokenType.RPAREN, reject=True) - return ret - ident = s.accept(TokenType.IDENT) - if ident: - name = ast.Name(IDENT_PREFIX + ident.value, ast.Load()) - if s.accept(TokenType.LPAREN): - ret = ast.Call(func=name, args=[], keywords=all_kwargs(s)) - s.accept(TokenType.RPAREN, reject=True) - else: - ret = name - return ret - - s.reject((TokenType.NOT, TokenType.LPAREN, TokenType.IDENT)) - - -BUILTIN_MATCHERS = {"True": True, "False": False, "None": None} - - -def single_kwarg(s: Scanner) -> ast.keyword: - keyword_name = s.accept(TokenType.IDENT, reject=True) - if not keyword_name.value.isidentifier(): - raise ParseError( - keyword_name.pos + 1, - f"not a valid python identifier {keyword_name.value}", - ) - if keyword.iskeyword(keyword_name.value): - raise ParseError( - keyword_name.pos + 1, - f"unexpected reserved python keyword `{keyword_name.value}`", - ) - s.accept(TokenType.EQUAL, reject=True) - - if value_token := s.accept(TokenType.STRING): - value: str | int | bool | None = value_token.value[1:-1] # strip quotes - else: - value_token = s.accept(TokenType.IDENT, reject=True) - if ( - (number := value_token.value).isdigit() - or number.startswith("-") - and number[1:].isdigit() - ): - value = int(number) - elif value_token.value in BUILTIN_MATCHERS: - value = BUILTIN_MATCHERS[value_token.value] - else: - raise ParseError( - value_token.pos + 1, - f'unexpected character/s "{value_token.value}"', - ) - - ret = ast.keyword(keyword_name.value, ast.Constant(value)) - return ret - - -def all_kwargs(s: Scanner) -> list[ast.keyword]: - ret = [single_kwarg(s)] - while s.accept(TokenType.COMMA): - ret.append(single_kwarg(s)) - return ret - - -class MatcherCall(Protocol): - def __call__(self, name: str, /, **kwargs: str | int | bool | None) -> bool: ... - - -@dataclasses.dataclass -class MatcherNameAdapter: - matcher: MatcherCall - name: str - - def __bool__(self) -> bool: - return self.matcher(self.name) - - def __call__(self, **kwargs: str | int | bool | None) -> bool: - return self.matcher(self.name, **kwargs) - - -class MatcherAdapter(Mapping[str, MatcherNameAdapter]): - """Adapts a matcher function to a locals mapping as required by eval().""" - - def __init__(self, matcher: MatcherCall) -> None: - self.matcher = matcher - - def __getitem__(self, key: str) -> MatcherNameAdapter: - return MatcherNameAdapter(matcher=self.matcher, name=key[len(IDENT_PREFIX) :]) - - def __iter__(self) -> Iterator[str]: - raise NotImplementedError() - - def __len__(self) -> int: - raise NotImplementedError() - - -class Expression: - """A compiled match expression as used by -k and -m. - - The expression can be evaluated against different matchers. - """ - - __slots__ = ("code",) - - def __init__(self, code: types.CodeType) -> None: - self.code = code - - @classmethod - def compile(self, input: str) -> Expression: - """Compile a match expression. - - :param input: The input expression - one line. - """ - astexpr = expression(Scanner(input)) - code: types.CodeType = compile( - astexpr, - filename="", - mode="eval", - ) - return Expression(code) - - def evaluate(self, matcher: MatcherCall) -> bool: - """Evaluate the match expression. - - :param matcher: - Given an identifier, should return whether it matches or not. - Should be prepared to handle arbitrary strings as input. - - :returns: Whether the expression matches or not. - """ - ret: bool = bool(eval(self.code, {"__builtins__": {}}, MatcherAdapter(matcher))) - return ret diff --git a/.venv/lib/python3.12/site-packages/_pytest/mark/structures.py b/.venv/lib/python3.12/site-packages/_pytest/mark/structures.py deleted file mode 100644 index 92ade55f..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/mark/structures.py +++ /dev/null @@ -1,615 +0,0 @@ -# mypy: allow-untyped-defs -from __future__ import annotations - -import collections.abc -import dataclasses -import inspect -from typing import Any -from typing import Callable -from typing import Collection -from typing import final -from typing import Iterable -from typing import Iterator -from typing import Mapping -from typing import MutableMapping -from typing import NamedTuple -from typing import overload -from typing import Sequence -from typing import TYPE_CHECKING -from typing import TypeVar -from typing import Union -import warnings - -from .._code import getfslineno -from ..compat import ascii_escaped -from ..compat import NOTSET -from ..compat import NotSetType -from _pytest.config import Config -from _pytest.deprecated import check_ispytest -from _pytest.deprecated import MARKED_FIXTURE -from _pytest.outcomes import fail -from _pytest.scope import _ScopeName -from _pytest.warning_types import PytestUnknownMarkWarning - - -if TYPE_CHECKING: - from ..nodes import Node - - -EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" - - -def istestfunc(func) -> bool: - return callable(func) and getattr(func, "__name__", "") != "" - - -def get_empty_parameterset_mark( - config: Config, argnames: Sequence[str], func -) -> MarkDecorator: - from ..nodes import Collector - - fs, lineno = getfslineno(func) - reason = "got empty parameter set %r, function %s at %s:%d" % ( - argnames, - func.__name__, - fs, - lineno, - ) - - requested_mark = config.getini(EMPTY_PARAMETERSET_OPTION) - if requested_mark in ("", None, "skip"): - mark = MARK_GEN.skip(reason=reason) - elif requested_mark == "xfail": - mark = MARK_GEN.xfail(reason=reason, run=False) - elif requested_mark == "fail_at_collect": - f_name = func.__name__ - _, lineno = getfslineno(func) - raise Collector.CollectError( - "Empty parameter set in '%s' at line %d" % (f_name, lineno + 1) - ) - else: - raise LookupError(requested_mark) - return mark - - -class ParameterSet(NamedTuple): - values: Sequence[object | NotSetType] - marks: Collection[MarkDecorator | Mark] - id: str | None - - @classmethod - def param( - cls, - *values: object, - marks: MarkDecorator | Collection[MarkDecorator | Mark] = (), - id: str | None = None, - ) -> ParameterSet: - if isinstance(marks, MarkDecorator): - marks = (marks,) - else: - assert isinstance(marks, collections.abc.Collection) - - if id is not None: - if not isinstance(id, str): - raise TypeError(f"Expected id to be a string, got {type(id)}: {id!r}") - id = ascii_escaped(id) - return cls(values, marks, id) - - @classmethod - def extract_from( - cls, - parameterset: ParameterSet | Sequence[object] | object, - force_tuple: bool = False, - ) -> ParameterSet: - """Extract from an object or objects. - - :param parameterset: - A legacy style parameterset that may or may not be a tuple, - and may or may not be wrapped into a mess of mark objects. - - :param force_tuple: - Enforce tuple wrapping so single argument tuple values - don't get decomposed and break tests. - """ - if isinstance(parameterset, cls): - return parameterset - if force_tuple: - return cls.param(parameterset) - else: - # TODO: Refactor to fix this type-ignore. Currently the following - # passes type-checking but crashes: - # - # @pytest.mark.parametrize(('x', 'y'), [1, 2]) - # def test_foo(x, y): pass - return cls(parameterset, marks=[], id=None) # type: ignore[arg-type] - - @staticmethod - def _parse_parametrize_args( - argnames: str | Sequence[str], - argvalues: Iterable[ParameterSet | Sequence[object] | object], - *args, - **kwargs, - ) -> tuple[Sequence[str], bool]: - if isinstance(argnames, str): - argnames = [x.strip() for x in argnames.split(",") if x.strip()] - force_tuple = len(argnames) == 1 - else: - force_tuple = False - return argnames, force_tuple - - @staticmethod - def _parse_parametrize_parameters( - argvalues: Iterable[ParameterSet | Sequence[object] | object], - force_tuple: bool, - ) -> list[ParameterSet]: - return [ - ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues - ] - - @classmethod - def _for_parametrize( - cls, - argnames: str | Sequence[str], - argvalues: Iterable[ParameterSet | Sequence[object] | object], - func, - config: Config, - nodeid: str, - ) -> tuple[Sequence[str], list[ParameterSet]]: - argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues) - parameters = cls._parse_parametrize_parameters(argvalues, force_tuple) - del argvalues - - if parameters: - # Check all parameter sets have the correct number of values. - for param in parameters: - if len(param.values) != len(argnames): - msg = ( - '{nodeid}: in "parametrize" the number of names ({names_len}):\n' - " {names}\n" - "must be equal to the number of values ({values_len}):\n" - " {values}" - ) - fail( - msg.format( - nodeid=nodeid, - values=param.values, - names=argnames, - names_len=len(argnames), - values_len=len(param.values), - ), - pytrace=False, - ) - else: - # Empty parameter set (likely computed at runtime): create a single - # parameter set with NOTSET values, with the "empty parameter set" mark applied to it. - mark = get_empty_parameterset_mark(config, argnames, func) - parameters.append( - ParameterSet(values=(NOTSET,) * len(argnames), marks=[mark], id=None) - ) - return argnames, parameters - - -@final -@dataclasses.dataclass(frozen=True) -class Mark: - """A pytest mark.""" - - #: Name of the mark. - name: str - #: Positional arguments of the mark decorator. - args: tuple[Any, ...] - #: Keyword arguments of the mark decorator. - kwargs: Mapping[str, Any] - - #: Source Mark for ids with parametrize Marks. - _param_ids_from: Mark | None = dataclasses.field(default=None, repr=False) - #: Resolved/generated ids with parametrize Marks. - _param_ids_generated: Sequence[str] | None = dataclasses.field( - default=None, repr=False - ) - - def __init__( - self, - name: str, - args: tuple[Any, ...], - kwargs: Mapping[str, Any], - param_ids_from: Mark | None = None, - param_ids_generated: Sequence[str] | None = None, - *, - _ispytest: bool = False, - ) -> None: - """:meta private:""" - check_ispytest(_ispytest) - # Weirdness to bypass frozen=True. - object.__setattr__(self, "name", name) - object.__setattr__(self, "args", args) - object.__setattr__(self, "kwargs", kwargs) - object.__setattr__(self, "_param_ids_from", param_ids_from) - object.__setattr__(self, "_param_ids_generated", param_ids_generated) - - def _has_param_ids(self) -> bool: - return "ids" in self.kwargs or len(self.args) >= 4 - - def combined_with(self, other: Mark) -> Mark: - """Return a new Mark which is a combination of this - Mark and another Mark. - - Combines by appending args and merging kwargs. - - :param Mark other: The mark to combine with. - :rtype: Mark - """ - assert self.name == other.name - - # Remember source of ids with parametrize Marks. - param_ids_from: Mark | None = None - if self.name == "parametrize": - if other._has_param_ids(): - param_ids_from = other - elif self._has_param_ids(): - param_ids_from = self - - return Mark( - self.name, - self.args + other.args, - dict(self.kwargs, **other.kwargs), - param_ids_from=param_ids_from, - _ispytest=True, - ) - - -# A generic parameter designating an object to which a Mark may -# be applied -- a test function (callable) or class. -# Note: a lambda is not allowed, but this can't be represented. -Markable = TypeVar("Markable", bound=Union[Callable[..., object], type]) - - -@dataclasses.dataclass -class MarkDecorator: - """A decorator for applying a mark on test functions and classes. - - ``MarkDecorators`` are created with ``pytest.mark``:: - - mark1 = pytest.mark.NAME # Simple MarkDecorator - mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator - - and can then be applied as decorators to test functions:: - - @mark2 - def test_function(): - pass - - When a ``MarkDecorator`` is called, it does the following: - - 1. If called with a single class as its only positional argument and no - additional keyword arguments, it attaches the mark to the class so it - gets applied automatically to all test cases found in that class. - - 2. If called with a single function as its only positional argument and - no additional keyword arguments, it attaches the mark to the function, - containing all the arguments already stored internally in the - ``MarkDecorator``. - - 3. When called in any other case, it returns a new ``MarkDecorator`` - instance with the original ``MarkDecorator``'s content updated with - the arguments passed to this call. - - Note: The rules above prevent a ``MarkDecorator`` from storing only a - single function or class reference as its positional argument with no - additional keyword or positional arguments. You can work around this by - using `with_args()`. - """ - - mark: Mark - - def __init__(self, mark: Mark, *, _ispytest: bool = False) -> None: - """:meta private:""" - check_ispytest(_ispytest) - self.mark = mark - - @property - def name(self) -> str: - """Alias for mark.name.""" - return self.mark.name - - @property - def args(self) -> tuple[Any, ...]: - """Alias for mark.args.""" - return self.mark.args - - @property - def kwargs(self) -> Mapping[str, Any]: - """Alias for mark.kwargs.""" - return self.mark.kwargs - - @property - def markname(self) -> str: - """:meta private:""" - return self.name # for backward-compat (2.4.1 had this attr) - - def with_args(self, *args: object, **kwargs: object) -> MarkDecorator: - """Return a MarkDecorator with extra arguments added. - - Unlike calling the MarkDecorator, with_args() can be used even - if the sole argument is a callable/class. - """ - mark = Mark(self.name, args, kwargs, _ispytest=True) - return MarkDecorator(self.mark.combined_with(mark), _ispytest=True) - - # Type ignored because the overloads overlap with an incompatible - # return type. Not much we can do about that. Thankfully mypy picks - # the first match so it works out even if we break the rules. - @overload - def __call__(self, arg: Markable) -> Markable: # type: ignore[overload-overlap] - pass - - @overload - def __call__(self, *args: object, **kwargs: object) -> MarkDecorator: - pass - - def __call__(self, *args: object, **kwargs: object): - """Call the MarkDecorator.""" - if args and not kwargs: - func = args[0] - is_class = inspect.isclass(func) - if len(args) == 1 and (istestfunc(func) or is_class): - store_mark(func, self.mark, stacklevel=3) - return func - return self.with_args(*args, **kwargs) - - -def get_unpacked_marks( - obj: object | type, - *, - consider_mro: bool = True, -) -> list[Mark]: - """Obtain the unpacked marks that are stored on an object. - - If obj is a class and consider_mro is true, return marks applied to - this class and all of its super-classes in MRO order. If consider_mro - is false, only return marks applied directly to this class. - """ - if isinstance(obj, type): - if not consider_mro: - mark_lists = [obj.__dict__.get("pytestmark", [])] - else: - mark_lists = [ - x.__dict__.get("pytestmark", []) for x in reversed(obj.__mro__) - ] - mark_list = [] - for item in mark_lists: - if isinstance(item, list): - mark_list.extend(item) - else: - mark_list.append(item) - else: - mark_attribute = getattr(obj, "pytestmark", []) - if isinstance(mark_attribute, list): - mark_list = mark_attribute - else: - mark_list = [mark_attribute] - return list(normalize_mark_list(mark_list)) - - -def normalize_mark_list( - mark_list: Iterable[Mark | MarkDecorator], -) -> Iterable[Mark]: - """ - Normalize an iterable of Mark or MarkDecorator objects into a list of marks - by retrieving the `mark` attribute on MarkDecorator instances. - - :param mark_list: marks to normalize - :returns: A new list of the extracted Mark objects - """ - for mark in mark_list: - mark_obj = getattr(mark, "mark", mark) - if not isinstance(mark_obj, Mark): - raise TypeError(f"got {mark_obj!r} instead of Mark") - yield mark_obj - - -def store_mark(obj, mark: Mark, *, stacklevel: int = 2) -> None: - """Store a Mark on an object. - - This is used to implement the Mark declarations/decorators correctly. - """ - assert isinstance(mark, Mark), mark - - from ..fixtures import getfixturemarker - - if getfixturemarker(obj) is not None: - warnings.warn(MARKED_FIXTURE, stacklevel=stacklevel) - - # Always reassign name to avoid updating pytestmark in a reference that - # was only borrowed. - obj.pytestmark = [*get_unpacked_marks(obj, consider_mro=False), mark] - - -# Typing for builtin pytest marks. This is cheating; it gives builtin marks -# special privilege, and breaks modularity. But practicality beats purity... -if TYPE_CHECKING: - - class _SkipMarkDecorator(MarkDecorator): - @overload # type: ignore[override,no-overload-impl] - def __call__(self, arg: Markable) -> Markable: ... - - @overload - def __call__(self, reason: str = ...) -> MarkDecorator: ... - - class _SkipifMarkDecorator(MarkDecorator): - def __call__( # type: ignore[override] - self, - condition: str | bool = ..., - *conditions: str | bool, - reason: str = ..., - ) -> MarkDecorator: ... - - class _XfailMarkDecorator(MarkDecorator): - @overload # type: ignore[override,no-overload-impl] - def __call__(self, arg: Markable) -> Markable: ... - - @overload - def __call__( - self, - condition: str | bool = False, - *conditions: str | bool, - reason: str = ..., - run: bool = ..., - raises: None | type[BaseException] | tuple[type[BaseException], ...] = ..., - strict: bool = ..., - ) -> MarkDecorator: ... - - class _ParametrizeMarkDecorator(MarkDecorator): - def __call__( # type: ignore[override] - self, - argnames: str | Sequence[str], - argvalues: Iterable[ParameterSet | Sequence[object] | object], - *, - indirect: bool | Sequence[str] = ..., - ids: Iterable[None | str | float | int | bool] - | Callable[[Any], object | None] - | None = ..., - scope: _ScopeName | None = ..., - ) -> MarkDecorator: ... - - class _UsefixturesMarkDecorator(MarkDecorator): - def __call__(self, *fixtures: str) -> MarkDecorator: # type: ignore[override] - ... - - class _FilterwarningsMarkDecorator(MarkDecorator): - def __call__(self, *filters: str) -> MarkDecorator: # type: ignore[override] - ... - - -@final -class MarkGenerator: - """Factory for :class:`MarkDecorator` objects - exposed as - a ``pytest.mark`` singleton instance. - - Example:: - - import pytest - - - @pytest.mark.slowtest - def test_function(): - pass - - applies a 'slowtest' :class:`Mark` on ``test_function``. - """ - - # See TYPE_CHECKING above. - if TYPE_CHECKING: - skip: _SkipMarkDecorator - skipif: _SkipifMarkDecorator - xfail: _XfailMarkDecorator - parametrize: _ParametrizeMarkDecorator - usefixtures: _UsefixturesMarkDecorator - filterwarnings: _FilterwarningsMarkDecorator - - def __init__(self, *, _ispytest: bool = False) -> None: - check_ispytest(_ispytest) - self._config: Config | None = None - self._markers: set[str] = set() - - def __getattr__(self, name: str) -> MarkDecorator: - """Generate a new :class:`MarkDecorator` with the given name.""" - if name[0] == "_": - raise AttributeError("Marker name must NOT start with underscore") - - if self._config is not None: - # We store a set of markers as a performance optimisation - if a mark - # name is in the set we definitely know it, but a mark may be known and - # not in the set. We therefore start by updating the set! - if name not in self._markers: - for line in self._config.getini("markers"): - # example lines: "skipif(condition): skip the given test if..." - # or "hypothesis: tests which use Hypothesis", so to get the - # marker name we split on both `:` and `(`. - marker = line.split(":")[0].split("(")[0].strip() - self._markers.add(marker) - - # If the name is not in the set of known marks after updating, - # then it really is time to issue a warning or an error. - if name not in self._markers: - if self._config.option.strict_markers or self._config.option.strict: - fail( - f"{name!r} not found in `markers` configuration option", - pytrace=False, - ) - - # Raise a specific error for common misspellings of "parametrize". - if name in ["parameterize", "parametrise", "parameterise"]: - __tracebackhide__ = True - fail(f"Unknown '{name}' mark, did you mean 'parametrize'?") - - warnings.warn( - f"Unknown pytest.mark.{name} - is this a typo? You can register " - "custom marks to avoid this warning - for details, see " - "https://docs.pytest.org/en/stable/how-to/mark.html", - PytestUnknownMarkWarning, - 2, - ) - - return MarkDecorator(Mark(name, (), {}, _ispytest=True), _ispytest=True) - - -MARK_GEN = MarkGenerator(_ispytest=True) - - -@final -class NodeKeywords(MutableMapping[str, Any]): - __slots__ = ("node", "parent", "_markers") - - def __init__(self, node: Node) -> None: - self.node = node - self.parent = node.parent - self._markers = {node.name: True} - - def __getitem__(self, key: str) -> Any: - try: - return self._markers[key] - except KeyError: - if self.parent is None: - raise - return self.parent.keywords[key] - - def __setitem__(self, key: str, value: Any) -> None: - self._markers[key] = value - - # Note: we could've avoided explicitly implementing some of the methods - # below and use the collections.abc fallback, but that would be slow. - - def __contains__(self, key: object) -> bool: - return ( - key in self._markers - or self.parent is not None - and key in self.parent.keywords - ) - - def update( # type: ignore[override] - self, - other: Mapping[str, Any] | Iterable[tuple[str, Any]] = (), - **kwds: Any, - ) -> None: - self._markers.update(other) - self._markers.update(kwds) - - def __delitem__(self, key: str) -> None: - raise ValueError("cannot delete key in keywords dict") - - def __iter__(self) -> Iterator[str]: - # Doesn't need to be fast. - yield from self._markers - if self.parent is not None: - for keyword in self.parent.keywords: - # self._marks and self.parent.keywords can have duplicates. - if keyword not in self._markers: - yield keyword - - def __len__(self) -> int: - # Doesn't need to be fast. - return sum(1 for keyword in self) - - def __repr__(self) -> str: - return f"" diff --git a/.venv/lib/python3.12/site-packages/_pytest/monkeypatch.py b/.venv/lib/python3.12/site-packages/_pytest/monkeypatch.py deleted file mode 100644 index 46eb1724..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/monkeypatch.py +++ /dev/null @@ -1,415 +0,0 @@ -# mypy: allow-untyped-defs -"""Monkeypatching and mocking functionality.""" - -from __future__ import annotations - -from contextlib import contextmanager -import os -import re -import sys -from typing import Any -from typing import final -from typing import Generator -from typing import Mapping -from typing import MutableMapping -from typing import overload -from typing import TypeVar -import warnings - -from _pytest.fixtures import fixture -from _pytest.warning_types import PytestWarning - - -RE_IMPORT_ERROR_NAME = re.compile(r"^No module named (.*)$") - - -K = TypeVar("K") -V = TypeVar("V") - - -@fixture -def monkeypatch() -> Generator[MonkeyPatch]: - """A convenient fixture for monkey-patching. - - The fixture provides these methods to modify objects, dictionaries, or - :data:`os.environ`: - - * :meth:`monkeypatch.setattr(obj, name, value, raising=True) ` - * :meth:`monkeypatch.delattr(obj, name, raising=True) ` - * :meth:`monkeypatch.setitem(mapping, name, value) ` - * :meth:`monkeypatch.delitem(obj, name, raising=True) ` - * :meth:`monkeypatch.setenv(name, value, prepend=None) ` - * :meth:`monkeypatch.delenv(name, raising=True) ` - * :meth:`monkeypatch.syspath_prepend(path) ` - * :meth:`monkeypatch.chdir(path) ` - * :meth:`monkeypatch.context() ` - - All modifications will be undone after the requesting test function or - fixture has finished. The ``raising`` parameter determines if a :class:`KeyError` - or :class:`AttributeError` will be raised if the set/deletion operation does not have the - specified target. - - To undo modifications done by the fixture in a contained scope, - use :meth:`context() `. - """ - mpatch = MonkeyPatch() - yield mpatch - mpatch.undo() - - -def resolve(name: str) -> object: - # Simplified from zope.dottedname. - parts = name.split(".") - - used = parts.pop(0) - found: object = __import__(used) - for part in parts: - used += "." + part - try: - found = getattr(found, part) - except AttributeError: - pass - else: - continue - # We use explicit un-nesting of the handling block in order - # to avoid nested exceptions. - try: - __import__(used) - except ImportError as ex: - expected = str(ex).split()[-1] - if expected == used: - raise - else: - raise ImportError(f"import error in {used}: {ex}") from ex - found = annotated_getattr(found, part, used) - return found - - -def annotated_getattr(obj: object, name: str, ann: str) -> object: - try: - obj = getattr(obj, name) - except AttributeError as e: - raise AttributeError( - f"{type(obj).__name__!r} object at {ann} has no attribute {name!r}" - ) from e - return obj - - -def derive_importpath(import_path: str, raising: bool) -> tuple[str, object]: - if not isinstance(import_path, str) or "." not in import_path: - raise TypeError(f"must be absolute import path string, not {import_path!r}") - module, attr = import_path.rsplit(".", 1) - target = resolve(module) - if raising: - annotated_getattr(target, attr, ann=module) - return attr, target - - -class Notset: - def __repr__(self) -> str: - return "" - - -notset = Notset() - - -@final -class MonkeyPatch: - """Helper to conveniently monkeypatch attributes/items/environment - variables/syspath. - - Returned by the :fixture:`monkeypatch` fixture. - - .. versionchanged:: 6.2 - Can now also be used directly as `pytest.MonkeyPatch()`, for when - the fixture is not available. In this case, use - :meth:`with MonkeyPatch.context() as mp: ` or remember to call - :meth:`undo` explicitly. - """ - - def __init__(self) -> None: - self._setattr: list[tuple[object, str, object]] = [] - self._setitem: list[tuple[Mapping[Any, Any], object, object]] = [] - self._cwd: str | None = None - self._savesyspath: list[str] | None = None - - @classmethod - @contextmanager - def context(cls) -> Generator[MonkeyPatch]: - """Context manager that returns a new :class:`MonkeyPatch` object - which undoes any patching done inside the ``with`` block upon exit. - - Example: - - .. code-block:: python - - import functools - - - def test_partial(monkeypatch): - with monkeypatch.context() as m: - m.setattr(functools, "partial", 3) - - Useful in situations where it is desired to undo some patches before the test ends, - such as mocking ``stdlib`` functions that might break pytest itself if mocked (for examples - of this see :issue:`3290`). - """ - m = cls() - try: - yield m - finally: - m.undo() - - @overload - def setattr( - self, - target: str, - name: object, - value: Notset = ..., - raising: bool = ..., - ) -> None: ... - - @overload - def setattr( - self, - target: object, - name: str, - value: object, - raising: bool = ..., - ) -> None: ... - - def setattr( - self, - target: str | object, - name: object | str, - value: object = notset, - raising: bool = True, - ) -> None: - """ - Set attribute value on target, memorizing the old value. - - For example: - - .. code-block:: python - - import os - - monkeypatch.setattr(os, "getcwd", lambda: "/") - - The code above replaces the :func:`os.getcwd` function by a ``lambda`` which - always returns ``"/"``. - - For convenience, you can specify a string as ``target`` which - will be interpreted as a dotted import path, with the last part - being the attribute name: - - .. code-block:: python - - monkeypatch.setattr("os.getcwd", lambda: "/") - - Raises :class:`AttributeError` if the attribute does not exist, unless - ``raising`` is set to False. - - **Where to patch** - - ``monkeypatch.setattr`` works by (temporarily) changing the object that a name points to with another one. - There can be many names pointing to any individual object, so for patching to work you must ensure - that you patch the name used by the system under test. - - See the section :ref:`Where to patch ` in the :mod:`unittest.mock` - docs for a complete explanation, which is meant for :func:`unittest.mock.patch` but - applies to ``monkeypatch.setattr`` as well. - """ - __tracebackhide__ = True - import inspect - - if isinstance(value, Notset): - if not isinstance(target, str): - raise TypeError( - "use setattr(target, name, value) or " - "setattr(target, value) with target being a dotted " - "import string" - ) - value = name - name, target = derive_importpath(target, raising) - else: - if not isinstance(name, str): - raise TypeError( - "use setattr(target, name, value) with name being a string or " - "setattr(target, value) with target being a dotted " - "import string" - ) - - oldval = getattr(target, name, notset) - if raising and oldval is notset: - raise AttributeError(f"{target!r} has no attribute {name!r}") - - # avoid class descriptors like staticmethod/classmethod - if inspect.isclass(target): - oldval = target.__dict__.get(name, notset) - self._setattr.append((target, name, oldval)) - setattr(target, name, value) - - def delattr( - self, - target: object | str, - name: str | Notset = notset, - raising: bool = True, - ) -> None: - """Delete attribute ``name`` from ``target``. - - If no ``name`` is specified and ``target`` is a string - it will be interpreted as a dotted import path with the - last part being the attribute name. - - Raises AttributeError it the attribute does not exist, unless - ``raising`` is set to False. - """ - __tracebackhide__ = True - import inspect - - if isinstance(name, Notset): - if not isinstance(target, str): - raise TypeError( - "use delattr(target, name) or " - "delattr(target) with target being a dotted " - "import string" - ) - name, target = derive_importpath(target, raising) - - if not hasattr(target, name): - if raising: - raise AttributeError(name) - else: - oldval = getattr(target, name, notset) - # Avoid class descriptors like staticmethod/classmethod. - if inspect.isclass(target): - oldval = target.__dict__.get(name, notset) - self._setattr.append((target, name, oldval)) - delattr(target, name) - - def setitem(self, dic: Mapping[K, V], name: K, value: V) -> None: - """Set dictionary entry ``name`` to value.""" - self._setitem.append((dic, name, dic.get(name, notset))) - # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict - dic[name] = value # type: ignore[index] - - def delitem(self, dic: Mapping[K, V], name: K, raising: bool = True) -> None: - """Delete ``name`` from dict. - - Raises ``KeyError`` if it doesn't exist, unless ``raising`` is set to - False. - """ - if name not in dic: - if raising: - raise KeyError(name) - else: - self._setitem.append((dic, name, dic.get(name, notset))) - # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict - del dic[name] # type: ignore[attr-defined] - - def setenv(self, name: str, value: str, prepend: str | None = None) -> None: - """Set environment variable ``name`` to ``value``. - - If ``prepend`` is a character, read the current environment variable - value and prepend the ``value`` adjoined with the ``prepend`` - character. - """ - if not isinstance(value, str): - warnings.warn( # type: ignore[unreachable] - PytestWarning( - f"Value of environment variable {name} type should be str, but got " - f"{value!r} (type: {type(value).__name__}); converted to str implicitly" - ), - stacklevel=2, - ) - value = str(value) - if prepend and name in os.environ: - value = value + prepend + os.environ[name] - self.setitem(os.environ, name, value) - - def delenv(self, name: str, raising: bool = True) -> None: - """Delete ``name`` from the environment. - - Raises ``KeyError`` if it does not exist, unless ``raising`` is set to - False. - """ - environ: MutableMapping[str, str] = os.environ - self.delitem(environ, name, raising=raising) - - def syspath_prepend(self, path) -> None: - """Prepend ``path`` to ``sys.path`` list of import locations.""" - if self._savesyspath is None: - self._savesyspath = sys.path[:] - sys.path.insert(0, str(path)) - - # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 - # this is only needed when pkg_resources was already loaded by the namespace package - if "pkg_resources" in sys.modules: - from pkg_resources import fixup_namespace_packages - - fixup_namespace_packages(str(path)) - - # A call to syspathinsert() usually means that the caller wants to - # import some dynamically created files, thus with python3 we - # invalidate its import caches. - # This is especially important when any namespace package is in use, - # since then the mtime based FileFinder cache (that gets created in - # this case already) gets not invalidated when writing the new files - # quickly afterwards. - from importlib import invalidate_caches - - invalidate_caches() - - def chdir(self, path: str | os.PathLike[str]) -> None: - """Change the current working directory to the specified path. - - :param path: - The path to change into. - """ - if self._cwd is None: - self._cwd = os.getcwd() - os.chdir(path) - - def undo(self) -> None: - """Undo previous changes. - - This call consumes the undo stack. Calling it a second time has no - effect unless you do more monkeypatching after the undo call. - - There is generally no need to call `undo()`, since it is - called automatically during tear-down. - - .. note:: - The same `monkeypatch` fixture is used across a - single test function invocation. If `monkeypatch` is used both by - the test function itself and one of the test fixtures, - calling `undo()` will undo all of the changes made in - both functions. - - Prefer to use :meth:`context() ` instead. - """ - for obj, name, value in reversed(self._setattr): - if value is not notset: - setattr(obj, name, value) - else: - delattr(obj, name) - self._setattr[:] = [] - for dictionary, key, value in reversed(self._setitem): - if value is notset: - try: - # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict - del dictionary[key] # type: ignore[attr-defined] - except KeyError: - pass # Was already deleted, so we have the desired state. - else: - # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict - dictionary[key] = value # type: ignore[index] - self._setitem[:] = [] - if self._savesyspath is not None: - sys.path[:] = self._savesyspath - self._savesyspath = None - - if self._cwd is not None: - os.chdir(self._cwd) - self._cwd = None diff --git a/.venv/lib/python3.12/site-packages/_pytest/nodes.py b/.venv/lib/python3.12/site-packages/_pytest/nodes.py deleted file mode 100644 index 51bc5174..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/nodes.py +++ /dev/null @@ -1,766 +0,0 @@ -# mypy: allow-untyped-defs -from __future__ import annotations - -import abc -from functools import cached_property -from inspect import signature -import os -import pathlib -from pathlib import Path -from typing import Any -from typing import Callable -from typing import cast -from typing import Iterable -from typing import Iterator -from typing import MutableMapping -from typing import NoReturn -from typing import overload -from typing import TYPE_CHECKING -from typing import TypeVar -import warnings - -import pluggy - -import _pytest._code -from _pytest._code import getfslineno -from _pytest._code.code import ExceptionInfo -from _pytest._code.code import TerminalRepr -from _pytest._code.code import Traceback -from _pytest._code.code import TracebackStyle -from _pytest.compat import LEGACY_PATH -from _pytest.config import Config -from _pytest.config import ConftestImportFailure -from _pytest.config.compat import _check_path -from _pytest.deprecated import NODE_CTOR_FSPATH_ARG -from _pytest.mark.structures import Mark -from _pytest.mark.structures import MarkDecorator -from _pytest.mark.structures import NodeKeywords -from _pytest.outcomes import fail -from _pytest.pathlib import absolutepath -from _pytest.pathlib import commonpath -from _pytest.stash import Stash -from _pytest.warning_types import PytestWarning - - -if TYPE_CHECKING: - from typing_extensions import Self - - # Imported here due to circular import. - from _pytest.main import Session - - -SEP = "/" - -tracebackcutdir = Path(_pytest.__file__).parent - - -_T = TypeVar("_T") - - -def _imply_path( - node_type: type[Node], - path: Path | None, - fspath: LEGACY_PATH | None, -) -> Path: - if fspath is not None: - warnings.warn( - NODE_CTOR_FSPATH_ARG.format( - node_type_name=node_type.__name__, - ), - stacklevel=6, - ) - if path is not None: - if fspath is not None: - _check_path(path, fspath) - return path - else: - assert fspath is not None - return Path(fspath) - - -_NodeType = TypeVar("_NodeType", bound="Node") - - -class NodeMeta(abc.ABCMeta): - """Metaclass used by :class:`Node` to enforce that direct construction raises - :class:`Failed`. - - This behaviour supports the indirection introduced with :meth:`Node.from_parent`, - the named constructor to be used instead of direct construction. The design - decision to enforce indirection with :class:`NodeMeta` was made as a - temporary aid for refactoring the collection tree, which was diagnosed to - have :class:`Node` objects whose creational patterns were overly entangled. - Once the refactoring is complete, this metaclass can be removed. - - See https://github.com/pytest-dev/pytest/projects/3 for an overview of the - progress on detangling the :class:`Node` classes. - """ - - def __call__(cls, *k, **kw) -> NoReturn: - msg = ( - "Direct construction of {name} has been deprecated, please use {name}.from_parent.\n" - "See " - "https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent" - " for more details." - ).format(name=f"{cls.__module__}.{cls.__name__}") - fail(msg, pytrace=False) - - def _create(cls: type[_T], *k, **kw) -> _T: - try: - return super().__call__(*k, **kw) # type: ignore[no-any-return,misc] - except TypeError: - sig = signature(getattr(cls, "__init__")) - known_kw = {k: v for k, v in kw.items() if k in sig.parameters} - from .warning_types import PytestDeprecationWarning - - warnings.warn( - PytestDeprecationWarning( - f"{cls} is not using a cooperative constructor and only takes {set(known_kw)}.\n" - "See https://docs.pytest.org/en/stable/deprecations.html" - "#constructors-of-custom-pytest-node-subclasses-should-take-kwargs " - "for more details." - ) - ) - - return super().__call__(*k, **known_kw) # type: ignore[no-any-return,misc] - - -class Node(abc.ABC, metaclass=NodeMeta): - r"""Base class of :class:`Collector` and :class:`Item`, the components of - the test collection tree. - - ``Collector``\'s are the internal nodes of the tree, and ``Item``\'s are the - leaf nodes. - """ - - # Implemented in the legacypath plugin. - #: A ``LEGACY_PATH`` copy of the :attr:`path` attribute. Intended for usage - #: for methods not migrated to ``pathlib.Path`` yet, such as - #: :meth:`Item.reportinfo `. Will be deprecated in - #: a future release, prefer using :attr:`path` instead. - fspath: LEGACY_PATH - - # Use __slots__ to make attribute access faster. - # Note that __dict__ is still available. - __slots__ = ( - "name", - "parent", - "config", - "session", - "path", - "_nodeid", - "_store", - "__dict__", - ) - - def __init__( - self, - name: str, - parent: Node | None = None, - config: Config | None = None, - session: Session | None = None, - fspath: LEGACY_PATH | None = None, - path: Path | None = None, - nodeid: str | None = None, - ) -> None: - #: A unique name within the scope of the parent node. - self.name: str = name - - #: The parent collector node. - self.parent = parent - - if config: - #: The pytest config object. - self.config: Config = config - else: - if not parent: - raise TypeError("config or parent must be provided") - self.config = parent.config - - if session: - #: The pytest session this node is part of. - self.session: Session = session - else: - if not parent: - raise TypeError("session or parent must be provided") - self.session = parent.session - - if path is None and fspath is None: - path = getattr(parent, "path", None) - #: Filesystem path where this node was collected from (can be None). - self.path: pathlib.Path = _imply_path(type(self), path, fspath=fspath) - - # The explicit annotation is to avoid publicly exposing NodeKeywords. - #: Keywords/markers collected from all scopes. - self.keywords: MutableMapping[str, Any] = NodeKeywords(self) - - #: The marker objects belonging to this node. - self.own_markers: list[Mark] = [] - - #: Allow adding of extra keywords to use for matching. - self.extra_keyword_matches: set[str] = set() - - if nodeid is not None: - assert "::()" not in nodeid - self._nodeid = nodeid - else: - if not self.parent: - raise TypeError("nodeid or parent must be provided") - self._nodeid = self.parent.nodeid + "::" + self.name - - #: A place where plugins can store information on the node for their - #: own use. - self.stash: Stash = Stash() - # Deprecated alias. Was never public. Can be removed in a few releases. - self._store = self.stash - - @classmethod - def from_parent(cls, parent: Node, **kw) -> Self: - """Public constructor for Nodes. - - This indirection got introduced in order to enable removing - the fragile logic from the node constructors. - - Subclasses can use ``super().from_parent(...)`` when overriding the - construction. - - :param parent: The parent node of this Node. - """ - if "config" in kw: - raise TypeError("config is not a valid argument for from_parent") - if "session" in kw: - raise TypeError("session is not a valid argument for from_parent") - return cls._create(parent=parent, **kw) - - @property - def ihook(self) -> pluggy.HookRelay: - """fspath-sensitive hook proxy used to call pytest hooks.""" - return self.session.gethookproxy(self.path) - - def __repr__(self) -> str: - return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None)) - - def warn(self, warning: Warning) -> None: - """Issue a warning for this Node. - - Warnings will be displayed after the test session, unless explicitly suppressed. - - :param Warning warning: - The warning instance to issue. - - :raises ValueError: If ``warning`` instance is not a subclass of Warning. - - Example usage: - - .. code-block:: python - - node.warn(PytestWarning("some message")) - node.warn(UserWarning("some message")) - - .. versionchanged:: 6.2 - Any subclass of :class:`Warning` is now accepted, rather than only - :class:`PytestWarning ` subclasses. - """ - # enforce type checks here to avoid getting a generic type error later otherwise. - if not isinstance(warning, Warning): - raise ValueError( - f"warning must be an instance of Warning or subclass, got {warning!r}" - ) - path, lineno = get_fslocation_from_item(self) - assert lineno is not None - warnings.warn_explicit( - warning, - category=None, - filename=str(path), - lineno=lineno + 1, - ) - - # Methods for ordering nodes. - - @property - def nodeid(self) -> str: - """A ::-separated string denoting its collection tree address.""" - return self._nodeid - - def __hash__(self) -> int: - return hash(self._nodeid) - - def setup(self) -> None: - pass - - def teardown(self) -> None: - pass - - def iter_parents(self) -> Iterator[Node]: - """Iterate over all parent collectors starting from and including self - up to the root of the collection tree. - - .. versionadded:: 8.1 - """ - parent: Node | None = self - while parent is not None: - yield parent - parent = parent.parent - - def listchain(self) -> list[Node]: - """Return a list of all parent collectors starting from the root of the - collection tree down to and including self.""" - chain = [] - item: Node | None = self - while item is not None: - chain.append(item) - item = item.parent - chain.reverse() - return chain - - def add_marker(self, marker: str | MarkDecorator, append: bool = True) -> None: - """Dynamically add a marker object to the node. - - :param marker: - The marker. - :param append: - Whether to append the marker, or prepend it. - """ - from _pytest.mark import MARK_GEN - - if isinstance(marker, MarkDecorator): - marker_ = marker - elif isinstance(marker, str): - marker_ = getattr(MARK_GEN, marker) - else: - raise ValueError("is not a string or pytest.mark.* Marker") - self.keywords[marker_.name] = marker_ - if append: - self.own_markers.append(marker_.mark) - else: - self.own_markers.insert(0, marker_.mark) - - def iter_markers(self, name: str | None = None) -> Iterator[Mark]: - """Iterate over all markers of the node. - - :param name: If given, filter the results by the name attribute. - :returns: An iterator of the markers of the node. - """ - return (x[1] for x in self.iter_markers_with_node(name=name)) - - def iter_markers_with_node( - self, name: str | None = None - ) -> Iterator[tuple[Node, Mark]]: - """Iterate over all markers of the node. - - :param name: If given, filter the results by the name attribute. - :returns: An iterator of (node, mark) tuples. - """ - for node in self.iter_parents(): - for mark in node.own_markers: - if name is None or getattr(mark, "name", None) == name: - yield node, mark - - @overload - def get_closest_marker(self, name: str) -> Mark | None: ... - - @overload - def get_closest_marker(self, name: str, default: Mark) -> Mark: ... - - def get_closest_marker(self, name: str, default: Mark | None = None) -> Mark | None: - """Return the first marker matching the name, from closest (for - example function) to farther level (for example module level). - - :param default: Fallback return value if no marker was found. - :param name: Name to filter by. - """ - return next(self.iter_markers(name=name), default) - - def listextrakeywords(self) -> set[str]: - """Return a set of all extra keywords in self and any parents.""" - extra_keywords: set[str] = set() - for item in self.listchain(): - extra_keywords.update(item.extra_keyword_matches) - return extra_keywords - - def listnames(self) -> list[str]: - return [x.name for x in self.listchain()] - - def addfinalizer(self, fin: Callable[[], object]) -> None: - """Register a function to be called without arguments when this node is - finalized. - - This method can only be called when this node is active - in a setup chain, for example during self.setup(). - """ - self.session._setupstate.addfinalizer(fin, self) - - def getparent(self, cls: type[_NodeType]) -> _NodeType | None: - """Get the closest parent node (including self) which is an instance of - the given class. - - :param cls: The node class to search for. - :returns: The node, if found. - """ - for node in self.iter_parents(): - if isinstance(node, cls): - return node - return None - - def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: - return excinfo.traceback - - def _repr_failure_py( - self, - excinfo: ExceptionInfo[BaseException], - style: TracebackStyle | None = None, - ) -> TerminalRepr: - from _pytest.fixtures import FixtureLookupError - - if isinstance(excinfo.value, ConftestImportFailure): - excinfo = ExceptionInfo.from_exception(excinfo.value.cause) - if isinstance(excinfo.value, fail.Exception): - if not excinfo.value.pytrace: - style = "value" - if isinstance(excinfo.value, FixtureLookupError): - return excinfo.value.formatrepr() - - tbfilter: bool | Callable[[ExceptionInfo[BaseException]], Traceback] - if self.config.getoption("fulltrace", False): - style = "long" - tbfilter = False - else: - tbfilter = self._traceback_filter - if style == "auto": - style = "long" - # XXX should excinfo.getrepr record all data and toterminal() process it? - if style is None: - if self.config.getoption("tbstyle", "auto") == "short": - style = "short" - else: - style = "long" - - if self.config.get_verbosity() > 1: - truncate_locals = False - else: - truncate_locals = True - - truncate_args = False if self.config.get_verbosity() > 2 else True - - # excinfo.getrepr() formats paths relative to the CWD if `abspath` is False. - # It is possible for a fixture/test to change the CWD while this code runs, which - # would then result in the user seeing confusing paths in the failure message. - # To fix this, if the CWD changed, always display the full absolute path. - # It will be better to just always display paths relative to invocation_dir, but - # this requires a lot of plumbing (#6428). - try: - abspath = Path(os.getcwd()) != self.config.invocation_params.dir - except OSError: - abspath = True - - return excinfo.getrepr( - funcargs=True, - abspath=abspath, - showlocals=self.config.getoption("showlocals", False), - style=style, - tbfilter=tbfilter, - truncate_locals=truncate_locals, - truncate_args=truncate_args, - ) - - def repr_failure( - self, - excinfo: ExceptionInfo[BaseException], - style: TracebackStyle | None = None, - ) -> str | TerminalRepr: - """Return a representation of a collection or test failure. - - .. seealso:: :ref:`non-python tests` - - :param excinfo: Exception information for the failure. - """ - return self._repr_failure_py(excinfo, style) - - -def get_fslocation_from_item(node: Node) -> tuple[str | Path, int | None]: - """Try to extract the actual location from a node, depending on available attributes: - - * "location": a pair (path, lineno) - * "obj": a Python object that the node wraps. - * "path": just a path - - :rtype: A tuple of (str|Path, int) with filename and 0-based line number. - """ - # See Item.location. - location: tuple[str, int | None, str] | None = getattr(node, "location", None) - if location is not None: - return location[:2] - obj = getattr(node, "obj", None) - if obj is not None: - return getfslineno(obj) - return getattr(node, "path", "unknown location"), -1 - - -class Collector(Node, abc.ABC): - """Base class of all collectors. - - Collector create children through `collect()` and thus iteratively build - the collection tree. - """ - - class CollectError(Exception): - """An error during collection, contains a custom message.""" - - @abc.abstractmethod - def collect(self) -> Iterable[Item | Collector]: - """Collect children (items and collectors) for this collector.""" - raise NotImplementedError("abstract") - - # TODO: This omits the style= parameter which breaks Liskov Substitution. - def repr_failure( # type: ignore[override] - self, excinfo: ExceptionInfo[BaseException] - ) -> str | TerminalRepr: - """Return a representation of a collection failure. - - :param excinfo: Exception information for the failure. - """ - if isinstance(excinfo.value, self.CollectError) and not self.config.getoption( - "fulltrace", False - ): - exc = excinfo.value - return str(exc.args[0]) - - # Respect explicit tbstyle option, but default to "short" - # (_repr_failure_py uses "long" with "fulltrace" option always). - tbstyle = self.config.getoption("tbstyle", "auto") - if tbstyle == "auto": - tbstyle = "short" - - return self._repr_failure_py(excinfo, style=tbstyle) - - def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: - if hasattr(self, "path"): - traceback = excinfo.traceback - ntraceback = traceback.cut(path=self.path) - if ntraceback == traceback: - ntraceback = ntraceback.cut(excludepath=tracebackcutdir) - return ntraceback.filter(excinfo) - return excinfo.traceback - - -def _check_initialpaths_for_relpath(session: Session, path: Path) -> str | None: - for initial_path in session._initialpaths: - if commonpath(path, initial_path) == initial_path: - rel = str(path.relative_to(initial_path)) - return "" if rel == "." else rel - return None - - -class FSCollector(Collector, abc.ABC): - """Base class for filesystem collectors.""" - - def __init__( - self, - fspath: LEGACY_PATH | None = None, - path_or_parent: Path | Node | None = None, - path: Path | None = None, - name: str | None = None, - parent: Node | None = None, - config: Config | None = None, - session: Session | None = None, - nodeid: str | None = None, - ) -> None: - if path_or_parent: - if isinstance(path_or_parent, Node): - assert parent is None - parent = cast(FSCollector, path_or_parent) - elif isinstance(path_or_parent, Path): - assert path is None - path = path_or_parent - - path = _imply_path(type(self), path, fspath=fspath) - if name is None: - name = path.name - if parent is not None and parent.path != path: - try: - rel = path.relative_to(parent.path) - except ValueError: - pass - else: - name = str(rel) - name = name.replace(os.sep, SEP) - self.path = path - - if session is None: - assert parent is not None - session = parent.session - - if nodeid is None: - try: - nodeid = str(self.path.relative_to(session.config.rootpath)) - except ValueError: - nodeid = _check_initialpaths_for_relpath(session, path) - - if nodeid and os.sep != SEP: - nodeid = nodeid.replace(os.sep, SEP) - - super().__init__( - name=name, - parent=parent, - config=config, - session=session, - nodeid=nodeid, - path=path, - ) - - @classmethod - def from_parent( - cls, - parent, - *, - fspath: LEGACY_PATH | None = None, - path: Path | None = None, - **kw, - ) -> Self: - """The public constructor.""" - return super().from_parent(parent=parent, fspath=fspath, path=path, **kw) - - -class File(FSCollector, abc.ABC): - """Base class for collecting tests from a file. - - :ref:`non-python tests`. - """ - - -class Directory(FSCollector, abc.ABC): - """Base class for collecting files from a directory. - - A basic directory collector does the following: goes over the files and - sub-directories in the directory and creates collectors for them by calling - the hooks :hook:`pytest_collect_directory` and :hook:`pytest_collect_file`, - after checking that they are not ignored using - :hook:`pytest_ignore_collect`. - - The default directory collectors are :class:`~pytest.Dir` and - :class:`~pytest.Package`. - - .. versionadded:: 8.0 - - :ref:`custom directory collectors`. - """ - - -class Item(Node, abc.ABC): - """Base class of all test invocation items. - - Note that for a single function there might be multiple test invocation items. - """ - - nextitem = None - - def __init__( - self, - name, - parent=None, - config: Config | None = None, - session: Session | None = None, - nodeid: str | None = None, - **kw, - ) -> None: - # The first two arguments are intentionally passed positionally, - # to keep plugins who define a node type which inherits from - # (pytest.Item, pytest.File) working (see issue #8435). - # They can be made kwargs when the deprecation above is done. - super().__init__( - name, - parent, - config=config, - session=session, - nodeid=nodeid, - **kw, - ) - self._report_sections: list[tuple[str, str, str]] = [] - - #: A list of tuples (name, value) that holds user defined properties - #: for this test. - self.user_properties: list[tuple[str, object]] = [] - - self._check_item_and_collector_diamond_inheritance() - - def _check_item_and_collector_diamond_inheritance(self) -> None: - """ - Check if the current type inherits from both File and Collector - at the same time, emitting a warning accordingly (#8447). - """ - cls = type(self) - - # We inject an attribute in the type to avoid issuing this warning - # for the same class more than once, which is not helpful. - # It is a hack, but was deemed acceptable in order to avoid - # flooding the user in the common case. - attr_name = "_pytest_diamond_inheritance_warning_shown" - if getattr(cls, attr_name, False): - return - setattr(cls, attr_name, True) - - problems = ", ".join( - base.__name__ for base in cls.__bases__ if issubclass(base, Collector) - ) - if problems: - warnings.warn( - f"{cls.__name__} is an Item subclass and should not be a collector, " - f"however its bases {problems} are collectors.\n" - "Please split the Collectors and the Item into separate node types.\n" - "Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n" - "example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/", - PytestWarning, - ) - - @abc.abstractmethod - def runtest(self) -> None: - """Run the test case for this item. - - Must be implemented by subclasses. - - .. seealso:: :ref:`non-python tests` - """ - raise NotImplementedError("runtest must be implemented by Item subclass") - - def add_report_section(self, when: str, key: str, content: str) -> None: - """Add a new report section, similar to what's done internally to add - stdout and stderr captured output:: - - item.add_report_section("call", "stdout", "report section contents") - - :param str when: - One of the possible capture states, ``"setup"``, ``"call"``, ``"teardown"``. - :param str key: - Name of the section, can be customized at will. Pytest uses ``"stdout"`` and - ``"stderr"`` internally. - :param str content: - The full contents as a string. - """ - if content: - self._report_sections.append((when, key, content)) - - def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: - """Get location information for this item for test reports. - - Returns a tuple with three elements: - - - The path of the test (default ``self.path``) - - The 0-based line number of the test (default ``None``) - - A name of the test to be shown (default ``""``) - - .. seealso:: :ref:`non-python tests` - """ - return self.path, None, "" - - @cached_property - def location(self) -> tuple[str, int | None, str]: - """ - Returns a tuple of ``(relfspath, lineno, testname)`` for this item - where ``relfspath`` is file path relative to ``config.rootpath`` - and lineno is a 0-based line number. - """ - location = self.reportinfo() - path = absolutepath(location[0]) - relfspath = self.session._node_location_to_relpath(path) - assert type(location[2]) is str - return (relfspath, location[1], location[2]) diff --git a/.venv/lib/python3.12/site-packages/_pytest/outcomes.py b/.venv/lib/python3.12/site-packages/_pytest/outcomes.py deleted file mode 100644 index 5b20803e..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/outcomes.py +++ /dev/null @@ -1,318 +0,0 @@ -"""Exception classes and constants handling test outcomes as well as -functions creating them.""" - -from __future__ import annotations - -import sys -from typing import Any -from typing import Callable -from typing import cast -from typing import NoReturn -from typing import Protocol -from typing import Type -from typing import TypeVar - -from .warning_types import PytestDeprecationWarning - - -class OutcomeException(BaseException): - """OutcomeException and its subclass instances indicate and contain info - about test and collection outcomes.""" - - def __init__(self, msg: str | None = None, pytrace: bool = True) -> None: - if msg is not None and not isinstance(msg, str): - error_msg = ( # type: ignore[unreachable] - "{} expected string as 'msg' parameter, got '{}' instead.\n" - "Perhaps you meant to use a mark?" - ) - raise TypeError(error_msg.format(type(self).__name__, type(msg).__name__)) - super().__init__(msg) - self.msg = msg - self.pytrace = pytrace - - def __repr__(self) -> str: - if self.msg is not None: - return self.msg - return f"<{self.__class__.__name__} instance>" - - __str__ = __repr__ - - -TEST_OUTCOME = (OutcomeException, Exception) - - -class Skipped(OutcomeException): - # XXX hackish: on 3k we fake to live in the builtins - # in order to have Skipped exception printing shorter/nicer - __module__ = "builtins" - - def __init__( - self, - msg: str | None = None, - pytrace: bool = True, - allow_module_level: bool = False, - *, - _use_item_location: bool = False, - ) -> None: - super().__init__(msg=msg, pytrace=pytrace) - self.allow_module_level = allow_module_level - # If true, the skip location is reported as the item's location, - # instead of the place that raises the exception/calls skip(). - self._use_item_location = _use_item_location - - -class Failed(OutcomeException): - """Raised from an explicit call to pytest.fail().""" - - __module__ = "builtins" - - -class Exit(Exception): - """Raised for immediate program exits (no tracebacks/summaries).""" - - def __init__( - self, msg: str = "unknown reason", returncode: int | None = None - ) -> None: - self.msg = msg - self.returncode = returncode - super().__init__(msg) - - -# Elaborate hack to work around https://github.com/python/mypy/issues/2087. -# Ideally would just be `exit.Exception = Exit` etc. - -_F = TypeVar("_F", bound=Callable[..., object]) -_ET = TypeVar("_ET", bound=Type[BaseException]) - - -class _WithException(Protocol[_F, _ET]): - Exception: _ET - __call__: _F - - -def _with_exception(exception_type: _ET) -> Callable[[_F], _WithException[_F, _ET]]: - def decorate(func: _F) -> _WithException[_F, _ET]: - func_with_exception = cast(_WithException[_F, _ET], func) - func_with_exception.Exception = exception_type - return func_with_exception - - return decorate - - -# Exposed helper methods. - - -@_with_exception(Exit) -def exit( - reason: str = "", - returncode: int | None = None, -) -> NoReturn: - """Exit testing process. - - :param reason: - The message to show as the reason for exiting pytest. reason has a default value - only because `msg` is deprecated. - - :param returncode: - Return code to be used when exiting pytest. None means the same as ``0`` (no error), same as :func:`sys.exit`. - - :raises pytest.exit.Exception: - The exception that is raised. - """ - __tracebackhide__ = True - raise Exit(reason, returncode) - - -@_with_exception(Skipped) -def skip( - reason: str = "", - *, - allow_module_level: bool = False, -) -> NoReturn: - """Skip an executing test with the given message. - - This function should be called only during testing (setup, call or teardown) or - during collection by using the ``allow_module_level`` flag. This function can - be called in doctests as well. - - :param reason: - The message to show the user as reason for the skip. - - :param allow_module_level: - Allows this function to be called at module level. - Raising the skip exception at module level will stop - the execution of the module and prevent the collection of all tests in the module, - even those defined before the `skip` call. - - Defaults to False. - - :raises pytest.skip.Exception: - The exception that is raised. - - .. note:: - It is better to use the :ref:`pytest.mark.skipif ref` marker when - possible to declare a test to be skipped under certain conditions - like mismatching platforms or dependencies. - Similarly, use the ``# doctest: +SKIP`` directive (see :py:data:`doctest.SKIP`) - to skip a doctest statically. - """ - __tracebackhide__ = True - raise Skipped(msg=reason, allow_module_level=allow_module_level) - - -@_with_exception(Failed) -def fail(reason: str = "", pytrace: bool = True) -> NoReturn: - """Explicitly fail an executing test with the given message. - - :param reason: - The message to show the user as reason for the failure. - - :param pytrace: - If False, msg represents the full failure information and no - python traceback will be reported. - - :raises pytest.fail.Exception: - The exception that is raised. - """ - __tracebackhide__ = True - raise Failed(msg=reason, pytrace=pytrace) - - -class XFailed(Failed): - """Raised from an explicit call to pytest.xfail().""" - - -@_with_exception(XFailed) -def xfail(reason: str = "") -> NoReturn: - """Imperatively xfail an executing test or setup function with the given reason. - - This function should be called only during testing (setup, call or teardown). - - No other code is executed after using ``xfail()`` (it is implemented - internally by raising an exception). - - :param reason: - The message to show the user as reason for the xfail. - - .. note:: - It is better to use the :ref:`pytest.mark.xfail ref` marker when - possible to declare a test to be xfailed under certain conditions - like known bugs or missing features. - - :raises pytest.xfail.Exception: - The exception that is raised. - """ - __tracebackhide__ = True - raise XFailed(reason) - - -def importorskip( - modname: str, - minversion: str | None = None, - reason: str | None = None, - *, - exc_type: type[ImportError] | None = None, -) -> Any: - """Import and return the requested module ``modname``, or skip the - current test if the module cannot be imported. - - :param modname: - The name of the module to import. - :param minversion: - If given, the imported module's ``__version__`` attribute must be at - least this minimal version, otherwise the test is still skipped. - :param reason: - If given, this reason is shown as the message when the module cannot - be imported. - :param exc_type: - The exception that should be captured in order to skip modules. - Must be :py:class:`ImportError` or a subclass. - - If the module can be imported but raises :class:`ImportError`, pytest will - issue a warning to the user, as often users expect the module not to be - found (which would raise :class:`ModuleNotFoundError` instead). - - This warning can be suppressed by passing ``exc_type=ImportError`` explicitly. - - See :ref:`import-or-skip-import-error` for details. - - - :returns: - The imported module. This should be assigned to its canonical name. - - :raises pytest.skip.Exception: - If the module cannot be imported. - - Example:: - - docutils = pytest.importorskip("docutils") - - .. versionadded:: 8.2 - - The ``exc_type`` parameter. - """ - import warnings - - __tracebackhide__ = True - compile(modname, "", "eval") # to catch syntaxerrors - - # Until pytest 9.1, we will warn the user if we catch ImportError (instead of ModuleNotFoundError), - # as this might be hiding an installation/environment problem, which is not usually what is intended - # when using importorskip() (#11523). - # In 9.1, to keep the function signature compatible, we just change the code below to: - # 1. Use `exc_type = ModuleNotFoundError` if `exc_type` is not given. - # 2. Remove `warn_on_import` and the warning handling. - if exc_type is None: - exc_type = ImportError - warn_on_import_error = True - else: - warn_on_import_error = False - - skipped: Skipped | None = None - warning: Warning | None = None - - with warnings.catch_warnings(): - # Make sure to ignore ImportWarnings that might happen because - # of existing directories with the same name we're trying to - # import but without a __init__.py file. - warnings.simplefilter("ignore") - - try: - __import__(modname) - except exc_type as exc: - # Do not raise or issue warnings inside the catch_warnings() block. - if reason is None: - reason = f"could not import {modname!r}: {exc}" - skipped = Skipped(reason, allow_module_level=True) - - if warn_on_import_error and not isinstance(exc, ModuleNotFoundError): - lines = [ - "", - f"Module '{modname}' was found, but when imported by pytest it raised:", - f" {exc!r}", - "In pytest 9.1 this warning will become an error by default.", - "You can fix the underlying problem, or alternatively overwrite this behavior and silence this " - "warning by passing exc_type=ImportError explicitly.", - "See https://docs.pytest.org/en/stable/deprecations.html#pytest-importorskip-default-behavior-regarding-importerror", - ] - warning = PytestDeprecationWarning("\n".join(lines)) - - if warning: - warnings.warn(warning, stacklevel=2) - if skipped: - raise skipped - - mod = sys.modules[modname] - if minversion is None: - return mod - verattr = getattr(mod, "__version__", None) - if minversion is not None: - # Imported lazily to improve start-up time. - from packaging.version import Version - - if verattr is None or Version(verattr) < Version(minversion): - raise Skipped( - f"module {modname!r} has __version__ {verattr!r}, required is: {minversion!r}", - allow_module_level=True, - ) - return mod diff --git a/.venv/lib/python3.12/site-packages/_pytest/pastebin.py b/.venv/lib/python3.12/site-packages/_pytest/pastebin.py deleted file mode 100644 index 69c011ed..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/pastebin.py +++ /dev/null @@ -1,113 +0,0 @@ -# mypy: allow-untyped-defs -"""Submit failure or test session information to a pastebin service.""" - -from __future__ import annotations - -from io import StringIO -import tempfile -from typing import IO - -from _pytest.config import Config -from _pytest.config import create_terminal_writer -from _pytest.config.argparsing import Parser -from _pytest.stash import StashKey -from _pytest.terminal import TerminalReporter -import pytest - - -pastebinfile_key = StashKey[IO[bytes]]() - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("terminal reporting") - group._addoption( - "--pastebin", - metavar="mode", - action="store", - dest="pastebin", - default=None, - choices=["failed", "all"], - help="Send failed|all info to bpaste.net pastebin service", - ) - - -@pytest.hookimpl(trylast=True) -def pytest_configure(config: Config) -> None: - if config.option.pastebin == "all": - tr = config.pluginmanager.getplugin("terminalreporter") - # If no terminal reporter plugin is present, nothing we can do here; - # this can happen when this function executes in a worker node - # when using pytest-xdist, for example. - if tr is not None: - # pastebin file will be UTF-8 encoded binary file. - config.stash[pastebinfile_key] = tempfile.TemporaryFile("w+b") - oldwrite = tr._tw.write - - def tee_write(s, **kwargs): - oldwrite(s, **kwargs) - if isinstance(s, str): - s = s.encode("utf-8") - config.stash[pastebinfile_key].write(s) - - tr._tw.write = tee_write - - -def pytest_unconfigure(config: Config) -> None: - if pastebinfile_key in config.stash: - pastebinfile = config.stash[pastebinfile_key] - # Get terminal contents and delete file. - pastebinfile.seek(0) - sessionlog = pastebinfile.read() - pastebinfile.close() - del config.stash[pastebinfile_key] - # Undo our patching in the terminal reporter. - tr = config.pluginmanager.getplugin("terminalreporter") - del tr._tw.__dict__["write"] - # Write summary. - tr.write_sep("=", "Sending information to Paste Service") - pastebinurl = create_new_paste(sessionlog) - tr.write_line(f"pastebin session-log: {pastebinurl}\n") - - -def create_new_paste(contents: str | bytes) -> str: - """Create a new paste using the bpaste.net service. - - :contents: Paste contents string. - :returns: URL to the pasted contents, or an error message. - """ - import re - from urllib.parse import urlencode - from urllib.request import urlopen - - params = {"code": contents, "lexer": "text", "expiry": "1week"} - url = "https://bpa.st" - try: - response: str = ( - urlopen(url, data=urlencode(params).encode("ascii")).read().decode("utf-8") - ) - except OSError as exc_info: # urllib errors - return f"bad response: {exc_info}" - m = re.search(r'href="/raw/(\w+)"', response) - if m: - return f"{url}/show/{m.group(1)}" - else: - return "bad response: invalid format ('" + response + "')" - - -def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: - if terminalreporter.config.option.pastebin != "failed": - return - if "failed" in terminalreporter.stats: - terminalreporter.write_sep("=", "Sending information to Paste Service") - for rep in terminalreporter.stats["failed"]: - try: - msg = rep.longrepr.reprtraceback.reprentries[-1].reprfileloc - except AttributeError: - msg = terminalreporter._getfailureheadline(rep) - file = StringIO() - tw = create_terminal_writer(terminalreporter.config, file) - rep.toterminal(tw) - s = file.getvalue() - assert len(s) - pastebinurl = create_new_paste(s) - terminalreporter.write_line(f"{msg} --> {pastebinurl}") diff --git a/.venv/lib/python3.12/site-packages/_pytest/pathlib.py b/.venv/lib/python3.12/site-packages/_pytest/pathlib.py deleted file mode 100644 index dd36559c..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/pathlib.py +++ /dev/null @@ -1,1038 +0,0 @@ -from __future__ import annotations - -import atexit -import contextlib -from enum import Enum -from errno import EBADF -from errno import ELOOP -from errno import ENOENT -from errno import ENOTDIR -import fnmatch -from functools import partial -from importlib.machinery import ModuleSpec -from importlib.machinery import PathFinder -import importlib.util -import itertools -import os -from os.path import expanduser -from os.path import expandvars -from os.path import isabs -from os.path import sep -from pathlib import Path -from pathlib import PurePath -from posixpath import sep as posix_sep -import shutil -import sys -import types -from types import ModuleType -from typing import Any -from typing import Callable -from typing import Iterable -from typing import Iterator -from typing import TypeVar -import uuid -import warnings - -from _pytest.compat import assert_never -from _pytest.outcomes import skip -from _pytest.warning_types import PytestWarning - - -if sys.version_info < (3, 11): - from importlib._bootstrap_external import _NamespaceLoader as NamespaceLoader -else: - from importlib.machinery import NamespaceLoader - -LOCK_TIMEOUT = 60 * 60 * 24 * 3 - -_AnyPurePath = TypeVar("_AnyPurePath", bound=PurePath) - -# The following function, variables and comments were -# copied from cpython 3.9 Lib/pathlib.py file. - -# EBADF - guard against macOS `stat` throwing EBADF -_IGNORED_ERRORS = (ENOENT, ENOTDIR, EBADF, ELOOP) - -_IGNORED_WINERRORS = ( - 21, # ERROR_NOT_READY - drive exists but is not accessible - 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself -) - - -def _ignore_error(exception: Exception) -> bool: - return ( - getattr(exception, "errno", None) in _IGNORED_ERRORS - or getattr(exception, "winerror", None) in _IGNORED_WINERRORS - ) - - -def get_lock_path(path: _AnyPurePath) -> _AnyPurePath: - return path.joinpath(".lock") - - -def on_rm_rf_error( - func: Callable[..., Any] | None, - path: str, - excinfo: BaseException - | tuple[type[BaseException], BaseException, types.TracebackType | None], - *, - start_path: Path, -) -> bool: - """Handle known read-only errors during rmtree. - - The returned value is used only by our own tests. - """ - if isinstance(excinfo, BaseException): - exc = excinfo - else: - exc = excinfo[1] - - # Another process removed the file in the middle of the "rm_rf" (xdist for example). - # More context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018 - if isinstance(exc, FileNotFoundError): - return False - - if not isinstance(exc, PermissionError): - warnings.warn( - PytestWarning(f"(rm_rf) error removing {path}\n{type(exc)}: {exc}") - ) - return False - - if func not in (os.rmdir, os.remove, os.unlink): - if func not in (os.open,): - warnings.warn( - PytestWarning( - f"(rm_rf) unknown function {func} when removing {path}:\n{type(exc)}: {exc}" - ) - ) - return False - - # Chmod + retry. - import stat - - def chmod_rw(p: str) -> None: - mode = os.stat(p).st_mode - os.chmod(p, mode | stat.S_IRUSR | stat.S_IWUSR) - - # For files, we need to recursively go upwards in the directories to - # ensure they all are also writable. - p = Path(path) - if p.is_file(): - for parent in p.parents: - chmod_rw(str(parent)) - # Stop when we reach the original path passed to rm_rf. - if parent == start_path: - break - chmod_rw(str(path)) - - func(path) - return True - - -def ensure_extended_length_path(path: Path) -> Path: - """Get the extended-length version of a path (Windows). - - On Windows, by default, the maximum length of a path (MAX_PATH) is 260 - characters, and operations on paths longer than that fail. But it is possible - to overcome this by converting the path to "extended-length" form before - performing the operation: - https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation - - On Windows, this function returns the extended-length absolute version of path. - On other platforms it returns path unchanged. - """ - if sys.platform.startswith("win32"): - path = path.resolve() - path = Path(get_extended_length_path_str(str(path))) - return path - - -def get_extended_length_path_str(path: str) -> str: - """Convert a path to a Windows extended length path.""" - long_path_prefix = "\\\\?\\" - unc_long_path_prefix = "\\\\?\\UNC\\" - if path.startswith((long_path_prefix, unc_long_path_prefix)): - return path - # UNC - if path.startswith("\\\\"): - return unc_long_path_prefix + path[2:] - return long_path_prefix + path - - -def rm_rf(path: Path) -> None: - """Remove the path contents recursively, even if some elements - are read-only.""" - path = ensure_extended_length_path(path) - onerror = partial(on_rm_rf_error, start_path=path) - if sys.version_info >= (3, 12): - shutil.rmtree(str(path), onexc=onerror) - else: - shutil.rmtree(str(path), onerror=onerror) - - -def find_prefixed(root: Path, prefix: str) -> Iterator[os.DirEntry[str]]: - """Find all elements in root that begin with the prefix, case-insensitive.""" - l_prefix = prefix.lower() - for x in os.scandir(root): - if x.name.lower().startswith(l_prefix): - yield x - - -def extract_suffixes(iter: Iterable[os.DirEntry[str]], prefix: str) -> Iterator[str]: - """Return the parts of the paths following the prefix. - - :param iter: Iterator over path names. - :param prefix: Expected prefix of the path names. - """ - p_len = len(prefix) - for entry in iter: - yield entry.name[p_len:] - - -def find_suffixes(root: Path, prefix: str) -> Iterator[str]: - """Combine find_prefixes and extract_suffixes.""" - return extract_suffixes(find_prefixed(root, prefix), prefix) - - -def parse_num(maybe_num: str) -> int: - """Parse number path suffixes, returns -1 on error.""" - try: - return int(maybe_num) - except ValueError: - return -1 - - -def _force_symlink(root: Path, target: str | PurePath, link_to: str | Path) -> None: - """Helper to create the current symlink. - - It's full of race conditions that are reasonably OK to ignore - for the context of best effort linking to the latest test run. - - The presumption being that in case of much parallelism - the inaccuracy is going to be acceptable. - """ - current_symlink = root.joinpath(target) - try: - current_symlink.unlink() - except OSError: - pass - try: - current_symlink.symlink_to(link_to) - except Exception: - pass - - -def make_numbered_dir(root: Path, prefix: str, mode: int = 0o700) -> Path: - """Create a directory with an increased number as suffix for the given prefix.""" - for i in range(10): - # try up to 10 times to create the folder - max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) - new_number = max_existing + 1 - new_path = root.joinpath(f"{prefix}{new_number}") - try: - new_path.mkdir(mode=mode) - except Exception: - pass - else: - _force_symlink(root, prefix + "current", new_path) - return new_path - else: - raise OSError( - "could not create numbered dir with prefix " - f"{prefix} in {root} after 10 tries" - ) - - -def create_cleanup_lock(p: Path) -> Path: - """Create a lock to prevent premature folder cleanup.""" - lock_path = get_lock_path(p) - try: - fd = os.open(str(lock_path), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644) - except FileExistsError as e: - raise OSError(f"cannot create lockfile in {p}") from e - else: - pid = os.getpid() - spid = str(pid).encode() - os.write(fd, spid) - os.close(fd) - if not lock_path.is_file(): - raise OSError("lock path got renamed after successful creation") - return lock_path - - -def register_cleanup_lock_removal( - lock_path: Path, register: Any = atexit.register -) -> Any: - """Register a cleanup function for removing a lock, by default on atexit.""" - pid = os.getpid() - - def cleanup_on_exit(lock_path: Path = lock_path, original_pid: int = pid) -> None: - current_pid = os.getpid() - if current_pid != original_pid: - # fork - return - try: - lock_path.unlink() - except OSError: - pass - - return register(cleanup_on_exit) - - -def maybe_delete_a_numbered_dir(path: Path) -> None: - """Remove a numbered directory if its lock can be obtained and it does - not seem to be in use.""" - path = ensure_extended_length_path(path) - lock_path = None - try: - lock_path = create_cleanup_lock(path) - parent = path.parent - - garbage = parent.joinpath(f"garbage-{uuid.uuid4()}") - path.rename(garbage) - rm_rf(garbage) - except OSError: - # known races: - # * other process did a cleanup at the same time - # * deletable folder was found - # * process cwd (Windows) - return - finally: - # If we created the lock, ensure we remove it even if we failed - # to properly remove the numbered dir. - if lock_path is not None: - try: - lock_path.unlink() - except OSError: - pass - - -def ensure_deletable(path: Path, consider_lock_dead_if_created_before: float) -> bool: - """Check if `path` is deletable based on whether the lock file is expired.""" - if path.is_symlink(): - return False - lock = get_lock_path(path) - try: - if not lock.is_file(): - return True - except OSError: - # we might not have access to the lock file at all, in this case assume - # we don't have access to the entire directory (#7491). - return False - try: - lock_time = lock.stat().st_mtime - except Exception: - return False - else: - if lock_time < consider_lock_dead_if_created_before: - # We want to ignore any errors while trying to remove the lock such as: - # - PermissionDenied, like the file permissions have changed since the lock creation; - # - FileNotFoundError, in case another pytest process got here first; - # and any other cause of failure. - with contextlib.suppress(OSError): - lock.unlink() - return True - return False - - -def try_cleanup(path: Path, consider_lock_dead_if_created_before: float) -> None: - """Try to cleanup a folder if we can ensure it's deletable.""" - if ensure_deletable(path, consider_lock_dead_if_created_before): - maybe_delete_a_numbered_dir(path) - - -def cleanup_candidates(root: Path, prefix: str, keep: int) -> Iterator[Path]: - """List candidates for numbered directories to be removed - follows py.path.""" - max_existing = max(map(parse_num, find_suffixes(root, prefix)), default=-1) - max_delete = max_existing - keep - entries = find_prefixed(root, prefix) - entries, entries2 = itertools.tee(entries) - numbers = map(parse_num, extract_suffixes(entries2, prefix)) - for entry, number in zip(entries, numbers): - if number <= max_delete: - yield Path(entry) - - -def cleanup_dead_symlinks(root: Path) -> None: - for left_dir in root.iterdir(): - if left_dir.is_symlink(): - if not left_dir.resolve().exists(): - left_dir.unlink() - - -def cleanup_numbered_dir( - root: Path, prefix: str, keep: int, consider_lock_dead_if_created_before: float -) -> None: - """Cleanup for lock driven numbered directories.""" - if not root.exists(): - return - for path in cleanup_candidates(root, prefix, keep): - try_cleanup(path, consider_lock_dead_if_created_before) - for path in root.glob("garbage-*"): - try_cleanup(path, consider_lock_dead_if_created_before) - - cleanup_dead_symlinks(root) - - -def make_numbered_dir_with_cleanup( - root: Path, - prefix: str, - keep: int, - lock_timeout: float, - mode: int, -) -> Path: - """Create a numbered dir with a cleanup lock and remove old ones.""" - e = None - for i in range(10): - try: - p = make_numbered_dir(root, prefix, mode) - # Only lock the current dir when keep is not 0 - if keep != 0: - lock_path = create_cleanup_lock(p) - register_cleanup_lock_removal(lock_path) - except Exception as exc: - e = exc - else: - consider_lock_dead_if_created_before = p.stat().st_mtime - lock_timeout - # Register a cleanup for program exit - atexit.register( - cleanup_numbered_dir, - root, - prefix, - keep, - consider_lock_dead_if_created_before, - ) - return p - assert e is not None - raise e - - -def resolve_from_str(input: str, rootpath: Path) -> Path: - input = expanduser(input) - input = expandvars(input) - if isabs(input): - return Path(input) - else: - return rootpath.joinpath(input) - - -def fnmatch_ex(pattern: str, path: str | os.PathLike[str]) -> bool: - """A port of FNMatcher from py.path.common which works with PurePath() instances. - - The difference between this algorithm and PurePath.match() is that the - latter matches "**" glob expressions for each part of the path, while - this algorithm uses the whole path instead. - - For example: - "tests/foo/bar/doc/test_foo.py" matches pattern "tests/**/doc/test*.py" - with this algorithm, but not with PurePath.match(). - - This algorithm was ported to keep backward-compatibility with existing - settings which assume paths match according this logic. - - References: - * https://bugs.python.org/issue29249 - * https://bugs.python.org/issue34731 - """ - path = PurePath(path) - iswin32 = sys.platform.startswith("win") - - if iswin32 and sep not in pattern and posix_sep in pattern: - # Running on Windows, the pattern has no Windows path separators, - # and the pattern has one or more Posix path separators. Replace - # the Posix path separators with the Windows path separator. - pattern = pattern.replace(posix_sep, sep) - - if sep not in pattern: - name = path.name - else: - name = str(path) - if path.is_absolute() and not os.path.isabs(pattern): - pattern = f"*{os.sep}{pattern}" - return fnmatch.fnmatch(name, pattern) - - -def parts(s: str) -> set[str]: - parts = s.split(sep) - return {sep.join(parts[: i + 1]) or sep for i in range(len(parts))} - - -def symlink_or_skip( - src: os.PathLike[str] | str, - dst: os.PathLike[str] | str, - **kwargs: Any, -) -> None: - """Make a symlink, or skip the test in case symlinks are not supported.""" - try: - os.symlink(src, dst, **kwargs) - except OSError as e: - skip(f"symlinks not supported: {e}") - - -class ImportMode(Enum): - """Possible values for `mode` parameter of `import_path`.""" - - prepend = "prepend" - append = "append" - importlib = "importlib" - - -class ImportPathMismatchError(ImportError): - """Raised on import_path() if there is a mismatch of __file__'s. - - This can happen when `import_path` is called multiple times with different filenames that has - the same basename but reside in packages - (for example "/tests1/test_foo.py" and "/tests2/test_foo.py"). - """ - - -def import_path( - path: str | os.PathLike[str], - *, - mode: str | ImportMode = ImportMode.prepend, - root: Path, - consider_namespace_packages: bool, -) -> ModuleType: - """ - Import and return a module from the given path, which can be a file (a module) or - a directory (a package). - - :param path: - Path to the file to import. - - :param mode: - Controls the underlying import mechanism that will be used: - - * ImportMode.prepend: the directory containing the module (or package, taking - `__init__.py` files into account) will be put at the *start* of `sys.path` before - being imported with `importlib.import_module`. - - * ImportMode.append: same as `prepend`, but the directory will be appended - to the end of `sys.path`, if not already in `sys.path`. - - * ImportMode.importlib: uses more fine control mechanisms provided by `importlib` - to import the module, which avoids having to muck with `sys.path` at all. It effectively - allows having same-named test modules in different places. - - :param root: - Used as an anchor when mode == ImportMode.importlib to obtain - a unique name for the module being imported so it can safely be stored - into ``sys.modules``. - - :param consider_namespace_packages: - If True, consider namespace packages when resolving module names. - - :raises ImportPathMismatchError: - If after importing the given `path` and the module `__file__` - are different. Only raised in `prepend` and `append` modes. - """ - path = Path(path) - mode = ImportMode(mode) - - if not path.exists(): - raise ImportError(path) - - if mode is ImportMode.importlib: - # Try to import this module using the standard import mechanisms, but - # without touching sys.path. - try: - pkg_root, module_name = resolve_pkg_root_and_module_name( - path, consider_namespace_packages=consider_namespace_packages - ) - except CouldNotResolvePathError: - pass - else: - # If the given module name is already in sys.modules, do not import it again. - with contextlib.suppress(KeyError): - return sys.modules[module_name] - - mod = _import_module_using_spec( - module_name, path, pkg_root, insert_modules=False - ) - if mod is not None: - return mod - - # Could not import the module with the current sys.path, so we fall back - # to importing the file as a single module, not being a part of a package. - module_name = module_name_from_path(path, root) - with contextlib.suppress(KeyError): - return sys.modules[module_name] - - mod = _import_module_using_spec( - module_name, path, path.parent, insert_modules=True - ) - if mod is None: - raise ImportError(f"Can't find module {module_name} at location {path}") - return mod - - try: - pkg_root, module_name = resolve_pkg_root_and_module_name( - path, consider_namespace_packages=consider_namespace_packages - ) - except CouldNotResolvePathError: - pkg_root, module_name = path.parent, path.stem - - # Change sys.path permanently: restoring it at the end of this function would cause surprising - # problems because of delayed imports: for example, a conftest.py file imported by this function - # might have local imports, which would fail at runtime if we restored sys.path. - if mode is ImportMode.append: - if str(pkg_root) not in sys.path: - sys.path.append(str(pkg_root)) - elif mode is ImportMode.prepend: - if str(pkg_root) != sys.path[0]: - sys.path.insert(0, str(pkg_root)) - else: - assert_never(mode) - - importlib.import_module(module_name) - - mod = sys.modules[module_name] - if path.name == "__init__.py": - return mod - - ignore = os.environ.get("PY_IGNORE_IMPORTMISMATCH", "") - if ignore != "1": - module_file = mod.__file__ - if module_file is None: - raise ImportPathMismatchError(module_name, module_file, path) - - if module_file.endswith((".pyc", ".pyo")): - module_file = module_file[:-1] - if module_file.endswith(os.sep + "__init__.py"): - module_file = module_file[: -(len(os.sep + "__init__.py"))] - - try: - is_same = _is_same(str(path), module_file) - except FileNotFoundError: - is_same = False - - if not is_same: - raise ImportPathMismatchError(module_name, module_file, path) - - return mod - - -def _import_module_using_spec( - module_name: str, module_path: Path, module_location: Path, *, insert_modules: bool -) -> ModuleType | None: - """ - Tries to import a module by its canonical name, path, and its parent location. - - :param module_name: - The expected module name, will become the key of `sys.modules`. - - :param module_path: - The file path of the module, for example `/foo/bar/test_demo.py`. - If module is a package, pass the path to the `__init__.py` of the package. - If module is a namespace package, pass directory path. - - :param module_location: - The parent location of the module. - If module is a package, pass the directory containing the `__init__.py` file. - - :param insert_modules: - If True, will call `insert_missing_modules` to create empty intermediate modules - with made-up module names (when importing test files not reachable from `sys.path`). - - Example 1 of parent_module_*: - - module_name: "a.b.c.demo" - module_path: Path("a/b/c/demo.py") - module_location: Path("a/b/c/") - if "a.b.c" is package ("a/b/c/__init__.py" exists), then - parent_module_name: "a.b.c" - parent_module_path: Path("a/b/c/__init__.py") - parent_module_location: Path("a/b/c/") - else: - parent_module_name: "a.b.c" - parent_module_path: Path("a/b/c") - parent_module_location: Path("a/b/") - - Example 2 of parent_module_*: - - module_name: "a.b.c" - module_path: Path("a/b/c/__init__.py") - module_location: Path("a/b/c/") - if "a.b" is package ("a/b/__init__.py" exists), then - parent_module_name: "a.b" - parent_module_path: Path("a/b/__init__.py") - parent_module_location: Path("a/b/") - else: - parent_module_name: "a.b" - parent_module_path: Path("a/b/") - parent_module_location: Path("a/") - """ - # Attempt to import the parent module, seems is our responsibility: - # https://github.com/python/cpython/blob/73906d5c908c1e0b73c5436faeff7d93698fc074/Lib/importlib/_bootstrap.py#L1308-L1311 - parent_module_name, _, name = module_name.rpartition(".") - parent_module: ModuleType | None = None - if parent_module_name: - parent_module = sys.modules.get(parent_module_name) - if parent_module is None: - # Get parent_location based on location, get parent_path based on path. - if module_path.name == "__init__.py": - # If the current module is in a package, - # need to leave the package first and then enter the parent module. - parent_module_path = module_path.parent.parent - else: - parent_module_path = module_path.parent - - if (parent_module_path / "__init__.py").is_file(): - # If the parent module is a package, loading by __init__.py file. - parent_module_path = parent_module_path / "__init__.py" - - parent_module = _import_module_using_spec( - parent_module_name, - parent_module_path, - parent_module_path.parent, - insert_modules=insert_modules, - ) - - # Checking with sys.meta_path first in case one of its hooks can import this module, - # such as our own assertion-rewrite hook. - for meta_importer in sys.meta_path: - spec = meta_importer.find_spec( - module_name, [str(module_location), str(module_path)] - ) - if spec_matches_module_path(spec, module_path): - break - else: - loader = None - if module_path.is_dir(): - # The `spec_from_file_location` matches a loader based on the file extension by default. - # For a namespace package, need to manually specify a loader. - loader = NamespaceLoader(name, module_path, PathFinder()) - - spec = importlib.util.spec_from_file_location( - module_name, str(module_path), loader=loader - ) - - if spec_matches_module_path(spec, module_path): - assert spec is not None - # Find spec and import this module. - mod = importlib.util.module_from_spec(spec) - sys.modules[module_name] = mod - spec.loader.exec_module(mod) # type: ignore[union-attr] - - # Set this module as an attribute of the parent module (#12194). - if parent_module is not None: - setattr(parent_module, name, mod) - - if insert_modules: - insert_missing_modules(sys.modules, module_name) - return mod - - return None - - -def spec_matches_module_path(module_spec: ModuleSpec | None, module_path: Path) -> bool: - """Return true if the given ModuleSpec can be used to import the given module path.""" - if module_spec is None: - return False - - if module_spec.origin: - return Path(module_spec.origin) == module_path - - # Compare the path with the `module_spec.submodule_Search_Locations` in case - # the module is part of a namespace package. - # https://docs.python.org/3/library/importlib.html#importlib.machinery.ModuleSpec.submodule_search_locations - if module_spec.submodule_search_locations: # can be None. - for path in module_spec.submodule_search_locations: - if Path(path) == module_path: - return True - - return False - - -# Implement a special _is_same function on Windows which returns True if the two filenames -# compare equal, to circumvent os.path.samefile returning False for mounts in UNC (#7678). -if sys.platform.startswith("win"): - - def _is_same(f1: str, f2: str) -> bool: - return Path(f1) == Path(f2) or os.path.samefile(f1, f2) - -else: - - def _is_same(f1: str, f2: str) -> bool: - return os.path.samefile(f1, f2) - - -def module_name_from_path(path: Path, root: Path) -> str: - """ - Return a dotted module name based on the given path, anchored on root. - - For example: path="projects/src/tests/test_foo.py" and root="/projects", the - resulting module name will be "src.tests.test_foo". - """ - path = path.with_suffix("") - try: - relative_path = path.relative_to(root) - except ValueError: - # If we can't get a relative path to root, use the full path, except - # for the first part ("d:\\" or "/" depending on the platform, for example). - path_parts = path.parts[1:] - else: - # Use the parts for the relative path to the root path. - path_parts = relative_path.parts - - # Module name for packages do not contain the __init__ file, unless - # the `__init__.py` file is at the root. - if len(path_parts) >= 2 and path_parts[-1] == "__init__": - path_parts = path_parts[:-1] - - # Module names cannot contain ".", normalize them to "_". This prevents - # a directory having a "." in the name (".env.310" for example) causing extra intermediate modules. - # Also, important to replace "." at the start of paths, as those are considered relative imports. - path_parts = tuple(x.replace(".", "_") for x in path_parts) - - return ".".join(path_parts) - - -def insert_missing_modules(modules: dict[str, ModuleType], module_name: str) -> None: - """ - Used by ``import_path`` to create intermediate modules when using mode=importlib. - - When we want to import a module as "src.tests.test_foo" for example, we need - to create empty modules "src" and "src.tests" after inserting "src.tests.test_foo", - otherwise "src.tests.test_foo" is not importable by ``__import__``. - """ - module_parts = module_name.split(".") - while module_name: - parent_module_name, _, child_name = module_name.rpartition(".") - if parent_module_name: - parent_module = modules.get(parent_module_name) - if parent_module is None: - try: - # If sys.meta_path is empty, calling import_module will issue - # a warning and raise ModuleNotFoundError. To avoid the - # warning, we check sys.meta_path explicitly and raise the error - # ourselves to fall back to creating a dummy module. - if not sys.meta_path: - raise ModuleNotFoundError - parent_module = importlib.import_module(parent_module_name) - except ModuleNotFoundError: - parent_module = ModuleType( - module_name, - doc="Empty module created by pytest's importmode=importlib.", - ) - modules[parent_module_name] = parent_module - - # Add child attribute to the parent that can reference the child - # modules. - if not hasattr(parent_module, child_name): - setattr(parent_module, child_name, modules[module_name]) - - module_parts.pop(-1) - module_name = ".".join(module_parts) - - -def resolve_package_path(path: Path) -> Path | None: - """Return the Python package path by looking for the last - directory upwards which still contains an __init__.py. - - Returns None if it cannot be determined. - """ - result = None - for parent in itertools.chain((path,), path.parents): - if parent.is_dir(): - if not (parent / "__init__.py").is_file(): - break - if not parent.name.isidentifier(): - break - result = parent - return result - - -def resolve_pkg_root_and_module_name( - path: Path, *, consider_namespace_packages: bool = False -) -> tuple[Path, str]: - """ - Return the path to the directory of the root package that contains the - given Python file, and its module name: - - src/ - app/ - __init__.py - core/ - __init__.py - models.py - - Passing the full path to `models.py` will yield Path("src") and "app.core.models". - - If consider_namespace_packages is True, then we additionally check upwards in the hierarchy - for namespace packages: - - https://packaging.python.org/en/latest/guides/packaging-namespace-packages - - Raises CouldNotResolvePathError if the given path does not belong to a package (missing any __init__.py files). - """ - pkg_root: Path | None = None - pkg_path = resolve_package_path(path) - if pkg_path is not None: - pkg_root = pkg_path.parent - if consider_namespace_packages: - start = pkg_root if pkg_root is not None else path.parent - for candidate in (start, *start.parents): - module_name = compute_module_name(candidate, path) - if module_name and is_importable(module_name, path): - # Point the pkg_root to the root of the namespace package. - pkg_root = candidate - break - - if pkg_root is not None: - module_name = compute_module_name(pkg_root, path) - if module_name: - return pkg_root, module_name - - raise CouldNotResolvePathError(f"Could not resolve for {path}") - - -def is_importable(module_name: str, module_path: Path) -> bool: - """ - Return if the given module path could be imported normally by Python, akin to the user - entering the REPL and importing the corresponding module name directly, and corresponds - to the module_path specified. - - :param module_name: - Full module name that we want to check if is importable. - For example, "app.models". - - :param module_path: - Full path to the python module/package we want to check if is importable. - For example, "/projects/src/app/models.py". - """ - try: - # Note this is different from what we do in ``_import_module_using_spec``, where we explicitly search through - # sys.meta_path to be able to pass the path of the module that we want to import (``meta_importer.find_spec``). - # Using importlib.util.find_spec() is different, it gives the same results as trying to import - # the module normally in the REPL. - spec = importlib.util.find_spec(module_name) - except (ImportError, ValueError, ImportWarning): - return False - else: - return spec_matches_module_path(spec, module_path) - - -def compute_module_name(root: Path, module_path: Path) -> str | None: - """Compute a module name based on a path and a root anchor.""" - try: - path_without_suffix = module_path.with_suffix("") - except ValueError: - # Empty paths (such as Path.cwd()) might break meta_path hooks (like our own assertion rewriter). - return None - - try: - relative = path_without_suffix.relative_to(root) - except ValueError: # pragma: no cover - return None - names = list(relative.parts) - if not names: - return None - if names[-1] == "__init__": - names.pop() - return ".".join(names) - - -class CouldNotResolvePathError(Exception): - """Custom exception raised by resolve_pkg_root_and_module_name.""" - - -def scandir( - path: str | os.PathLike[str], - sort_key: Callable[[os.DirEntry[str]], object] = lambda entry: entry.name, -) -> list[os.DirEntry[str]]: - """Scan a directory recursively, in breadth-first order. - - The returned entries are sorted according to the given key. - The default is to sort by name. - """ - entries = [] - with os.scandir(path) as s: - # Skip entries with symlink loops and other brokenness, so the caller - # doesn't have to deal with it. - for entry in s: - try: - entry.is_file() - except OSError as err: - if _ignore_error(err): - continue - raise - entries.append(entry) - entries.sort(key=sort_key) # type: ignore[arg-type] - return entries - - -def visit( - path: str | os.PathLike[str], recurse: Callable[[os.DirEntry[str]], bool] -) -> Iterator[os.DirEntry[str]]: - """Walk a directory recursively, in breadth-first order. - - The `recurse` predicate determines whether a directory is recursed. - - Entries at each directory level are sorted. - """ - entries = scandir(path) - yield from entries - for entry in entries: - if entry.is_dir() and recurse(entry): - yield from visit(entry.path, recurse) - - -def absolutepath(path: str | os.PathLike[str]) -> Path: - """Convert a path to an absolute path using os.path.abspath. - - Prefer this over Path.resolve() (see #6523). - Prefer this over Path.absolute() (not public, doesn't normalize). - """ - return Path(os.path.abspath(path)) - - -def commonpath(path1: Path, path2: Path) -> Path | None: - """Return the common part shared with the other path, or None if there is - no common part. - - If one path is relative and one is absolute, returns None. - """ - try: - return Path(os.path.commonpath((str(path1), str(path2)))) - except ValueError: - return None - - -def bestrelpath(directory: Path, dest: Path) -> str: - """Return a string which is a relative path from directory to dest such - that directory/bestrelpath == dest. - - The paths must be either both absolute or both relative. - - If no such path can be determined, returns dest. - """ - assert isinstance(directory, Path) - assert isinstance(dest, Path) - if dest == directory: - return os.curdir - # Find the longest common directory. - base = commonpath(directory, dest) - # Can be the case on Windows for two absolute paths on different drives. - # Can be the case for two relative paths without common prefix. - # Can be the case for a relative path and an absolute path. - if not base: - return str(dest) - reldirectory = directory.relative_to(base) - reldest = dest.relative_to(base) - return os.path.join( - # Back from directory to base. - *([os.pardir] * len(reldirectory.parts)), - # Forward from base to dest. - *reldest.parts, - ) - - -def safe_exists(p: Path) -> bool: - """Like Path.exists(), but account for input arguments that might be too long (#11394).""" - try: - return p.exists() - except (ValueError, OSError): - # ValueError: stat: path too long for Windows - # OSError: [WinError 123] The filename, directory name, or volume label syntax is incorrect - return False diff --git a/.venv/lib/python3.12/site-packages/_pytest/py.typed b/.venv/lib/python3.12/site-packages/_pytest/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/_pytest/pytester.py b/.venv/lib/python3.12/site-packages/_pytest/pytester.py deleted file mode 100644 index 3f7520ee..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/pytester.py +++ /dev/null @@ -1,1766 +0,0 @@ -# mypy: allow-untyped-defs -"""(Disabled by default) support for testing pytest and pytest plugins. - -PYTEST_DONT_REWRITE -""" - -from __future__ import annotations - -import collections.abc -import contextlib -from fnmatch import fnmatch -import gc -import importlib -from io import StringIO -import locale -import os -from pathlib import Path -import platform -import re -import shutil -import subprocess -import sys -import traceback -from typing import Any -from typing import Callable -from typing import Final -from typing import final -from typing import Generator -from typing import IO -from typing import Iterable -from typing import Literal -from typing import overload -from typing import Sequence -from typing import TextIO -from typing import TYPE_CHECKING -from weakref import WeakKeyDictionary - -from iniconfig import IniConfig -from iniconfig import SectionWrapper - -from _pytest import timing -from _pytest._code import Source -from _pytest.capture import _get_multicapture -from _pytest.compat import NOTSET -from _pytest.compat import NotSetType -from _pytest.config import _PluggyPlugin -from _pytest.config import Config -from _pytest.config import ExitCode -from _pytest.config import hookimpl -from _pytest.config import main -from _pytest.config import PytestPluginManager -from _pytest.config.argparsing import Parser -from _pytest.deprecated import check_ispytest -from _pytest.fixtures import fixture -from _pytest.fixtures import FixtureRequest -from _pytest.main import Session -from _pytest.monkeypatch import MonkeyPatch -from _pytest.nodes import Collector -from _pytest.nodes import Item -from _pytest.outcomes import fail -from _pytest.outcomes import importorskip -from _pytest.outcomes import skip -from _pytest.pathlib import bestrelpath -from _pytest.pathlib import make_numbered_dir -from _pytest.reports import CollectReport -from _pytest.reports import TestReport -from _pytest.tmpdir import TempPathFactory -from _pytest.warning_types import PytestWarning - - -if TYPE_CHECKING: - import pexpect - - -pytest_plugins = ["pytester_assertions"] - - -IGNORE_PAM = [ # filenames added when obtaining details about the current user - "/var/lib/sss/mc/passwd" -] - - -def pytest_addoption(parser: Parser) -> None: - parser.addoption( - "--lsof", - action="store_true", - dest="lsof", - default=False, - help="Run FD checks if lsof is available", - ) - - parser.addoption( - "--runpytest", - default="inprocess", - dest="runpytest", - choices=("inprocess", "subprocess"), - help=( - "Run pytest sub runs in tests using an 'inprocess' " - "or 'subprocess' (python -m main) method" - ), - ) - - parser.addini( - "pytester_example_dir", help="Directory to take the pytester example files from" - ) - - -def pytest_configure(config: Config) -> None: - if config.getvalue("lsof"): - checker = LsofFdLeakChecker() - if checker.matching_platform(): - config.pluginmanager.register(checker) - - config.addinivalue_line( - "markers", - "pytester_example_path(*path_segments): join the given path " - "segments to `pytester_example_dir` for this test.", - ) - - -class LsofFdLeakChecker: - def get_open_files(self) -> list[tuple[str, str]]: - if sys.version_info >= (3, 11): - # New in Python 3.11, ignores utf-8 mode - encoding = locale.getencoding() - else: - encoding = locale.getpreferredencoding(False) - out = subprocess.run( - ("lsof", "-Ffn0", "-p", str(os.getpid())), - stdout=subprocess.PIPE, - stderr=subprocess.DEVNULL, - check=True, - text=True, - encoding=encoding, - ).stdout - - def isopen(line: str) -> bool: - return line.startswith("f") and ( - "deleted" not in line - and "mem" not in line - and "txt" not in line - and "cwd" not in line - ) - - open_files = [] - - for line in out.split("\n"): - if isopen(line): - fields = line.split("\0") - fd = fields[0][1:] - filename = fields[1][1:] - if filename in IGNORE_PAM: - continue - if filename.startswith("/"): - open_files.append((fd, filename)) - - return open_files - - def matching_platform(self) -> bool: - try: - subprocess.run(("lsof", "-v"), check=True) - except (OSError, subprocess.CalledProcessError): - return False - else: - return True - - @hookimpl(wrapper=True, tryfirst=True) - def pytest_runtest_protocol(self, item: Item) -> Generator[None, object, object]: - lines1 = self.get_open_files() - try: - return (yield) - finally: - if hasattr(sys, "pypy_version_info"): - gc.collect() - lines2 = self.get_open_files() - - new_fds = {t[0] for t in lines2} - {t[0] for t in lines1} - leaked_files = [t for t in lines2 if t[0] in new_fds] - if leaked_files: - error = [ - f"***** {len(leaked_files)} FD leakage detected", - *(str(f) for f in leaked_files), - "*** Before:", - *(str(f) for f in lines1), - "*** After:", - *(str(f) for f in lines2), - f"***** {len(leaked_files)} FD leakage detected", - "*** function {}:{}: {} ".format(*item.location), - "See issue #2366", - ] - item.warn(PytestWarning("\n".join(error))) - - -# used at least by pytest-xdist plugin - - -@fixture -def _pytest(request: FixtureRequest) -> PytestArg: - """Return a helper which offers a gethookrecorder(hook) method which - returns a HookRecorder instance which helps to make assertions about called - hooks.""" - return PytestArg(request) - - -class PytestArg: - def __init__(self, request: FixtureRequest) -> None: - self._request = request - - def gethookrecorder(self, hook) -> HookRecorder: - hookrecorder = HookRecorder(hook._pm) - self._request.addfinalizer(hookrecorder.finish_recording) - return hookrecorder - - -def get_public_names(values: Iterable[str]) -> list[str]: - """Only return names from iterator values without a leading underscore.""" - return [x for x in values if x[0] != "_"] - - -@final -class RecordedHookCall: - """A recorded call to a hook. - - The arguments to the hook call are set as attributes. - For example: - - .. code-block:: python - - calls = hook_recorder.getcalls("pytest_runtest_setup") - # Suppose pytest_runtest_setup was called once with `item=an_item`. - assert calls[0].item is an_item - """ - - def __init__(self, name: str, kwargs) -> None: - self.__dict__.update(kwargs) - self._name = name - - def __repr__(self) -> str: - d = self.__dict__.copy() - del d["_name"] - return f"" - - if TYPE_CHECKING: - # The class has undetermined attributes, this tells mypy about it. - def __getattr__(self, key: str): ... - - -@final -class HookRecorder: - """Record all hooks called in a plugin manager. - - Hook recorders are created by :class:`Pytester`. - - This wraps all the hook calls in the plugin manager, recording each call - before propagating the normal calls. - """ - - def __init__( - self, pluginmanager: PytestPluginManager, *, _ispytest: bool = False - ) -> None: - check_ispytest(_ispytest) - - self._pluginmanager = pluginmanager - self.calls: list[RecordedHookCall] = [] - self.ret: int | ExitCode | None = None - - def before(hook_name: str, hook_impls, kwargs) -> None: - self.calls.append(RecordedHookCall(hook_name, kwargs)) - - def after(outcome, hook_name: str, hook_impls, kwargs) -> None: - pass - - self._undo_wrapping = pluginmanager.add_hookcall_monitoring(before, after) - - def finish_recording(self) -> None: - self._undo_wrapping() - - def getcalls(self, names: str | Iterable[str]) -> list[RecordedHookCall]: - """Get all recorded calls to hooks with the given names (or name).""" - if isinstance(names, str): - names = names.split() - return [call for call in self.calls if call._name in names] - - def assert_contains(self, entries: Sequence[tuple[str, str]]) -> None: - __tracebackhide__ = True - i = 0 - entries = list(entries) - # Since Python 3.13, f_locals is not a dict, but eval requires a dict. - backlocals = dict(sys._getframe(1).f_locals) - while entries: - name, check = entries.pop(0) - for ind, call in enumerate(self.calls[i:]): - if call._name == name: - print("NAMEMATCH", name, call) - if eval(check, backlocals, call.__dict__): - print("CHECKERMATCH", repr(check), "->", call) - else: - print("NOCHECKERMATCH", repr(check), "-", call) - continue - i += ind + 1 - break - print("NONAMEMATCH", name, "with", call) - else: - fail(f"could not find {name!r} check {check!r}") - - def popcall(self, name: str) -> RecordedHookCall: - __tracebackhide__ = True - for i, call in enumerate(self.calls): - if call._name == name: - del self.calls[i] - return call - lines = [f"could not find call {name!r}, in:"] - lines.extend([f" {x}" for x in self.calls]) - fail("\n".join(lines)) - - def getcall(self, name: str) -> RecordedHookCall: - values = self.getcalls(name) - assert len(values) == 1, (name, values) - return values[0] - - # functionality for test reports - - @overload - def getreports( - self, - names: Literal["pytest_collectreport"], - ) -> Sequence[CollectReport]: ... - - @overload - def getreports( - self, - names: Literal["pytest_runtest_logreport"], - ) -> Sequence[TestReport]: ... - - @overload - def getreports( - self, - names: str | Iterable[str] = ( - "pytest_collectreport", - "pytest_runtest_logreport", - ), - ) -> Sequence[CollectReport | TestReport]: ... - - def getreports( - self, - names: str | Iterable[str] = ( - "pytest_collectreport", - "pytest_runtest_logreport", - ), - ) -> Sequence[CollectReport | TestReport]: - return [x.report for x in self.getcalls(names)] - - def matchreport( - self, - inamepart: str = "", - names: str | Iterable[str] = ( - "pytest_runtest_logreport", - "pytest_collectreport", - ), - when: str | None = None, - ) -> CollectReport | TestReport: - """Return a testreport whose dotted import path matches.""" - values = [] - for rep in self.getreports(names=names): - if not when and rep.when != "call" and rep.passed: - # setup/teardown passing reports - let's ignore those - continue - if when and rep.when != when: - continue - if not inamepart or inamepart in rep.nodeid.split("::"): - values.append(rep) - if not values: - raise ValueError( - f"could not find test report matching {inamepart!r}: " - "no test reports at all!" - ) - if len(values) > 1: - raise ValueError( - f"found 2 or more testreports matching {inamepart!r}: {values}" - ) - return values[0] - - @overload - def getfailures( - self, - names: Literal["pytest_collectreport"], - ) -> Sequence[CollectReport]: ... - - @overload - def getfailures( - self, - names: Literal["pytest_runtest_logreport"], - ) -> Sequence[TestReport]: ... - - @overload - def getfailures( - self, - names: str | Iterable[str] = ( - "pytest_collectreport", - "pytest_runtest_logreport", - ), - ) -> Sequence[CollectReport | TestReport]: ... - - def getfailures( - self, - names: str | Iterable[str] = ( - "pytest_collectreport", - "pytest_runtest_logreport", - ), - ) -> Sequence[CollectReport | TestReport]: - return [rep for rep in self.getreports(names) if rep.failed] - - def getfailedcollections(self) -> Sequence[CollectReport]: - return self.getfailures("pytest_collectreport") - - def listoutcomes( - self, - ) -> tuple[ - Sequence[TestReport], - Sequence[CollectReport | TestReport], - Sequence[CollectReport | TestReport], - ]: - passed = [] - skipped = [] - failed = [] - for rep in self.getreports( - ("pytest_collectreport", "pytest_runtest_logreport") - ): - if rep.passed: - if rep.when == "call": - assert isinstance(rep, TestReport) - passed.append(rep) - elif rep.skipped: - skipped.append(rep) - else: - assert rep.failed, f"Unexpected outcome: {rep!r}" - failed.append(rep) - return passed, skipped, failed - - def countoutcomes(self) -> list[int]: - return [len(x) for x in self.listoutcomes()] - - def assertoutcome(self, passed: int = 0, skipped: int = 0, failed: int = 0) -> None: - __tracebackhide__ = True - from _pytest.pytester_assertions import assertoutcome - - outcomes = self.listoutcomes() - assertoutcome( - outcomes, - passed=passed, - skipped=skipped, - failed=failed, - ) - - def clear(self) -> None: - self.calls[:] = [] - - -@fixture -def linecomp() -> LineComp: - """A :class: `LineComp` instance for checking that an input linearly - contains a sequence of strings.""" - return LineComp() - - -@fixture(name="LineMatcher") -def LineMatcher_fixture(request: FixtureRequest) -> type[LineMatcher]: - """A reference to the :class: `LineMatcher`. - - This is instantiable with a list of lines (without their trailing newlines). - This is useful for testing large texts, such as the output of commands. - """ - return LineMatcher - - -@fixture -def pytester( - request: FixtureRequest, tmp_path_factory: TempPathFactory, monkeypatch: MonkeyPatch -) -> Pytester: - """ - Facilities to write tests/configuration files, execute pytest in isolation, and match - against expected output, perfect for black-box testing of pytest plugins. - - It attempts to isolate the test run from external factors as much as possible, modifying - the current working directory to ``path`` and environment variables during initialization. - - It is particularly useful for testing plugins. It is similar to the :fixture:`tmp_path` - fixture but provides methods which aid in testing pytest itself. - """ - return Pytester(request, tmp_path_factory, monkeypatch, _ispytest=True) - - -@fixture -def _sys_snapshot() -> Generator[None]: - snappaths = SysPathsSnapshot() - snapmods = SysModulesSnapshot() - yield - snapmods.restore() - snappaths.restore() - - -@fixture -def _config_for_test() -> Generator[Config]: - from _pytest.config import get_config - - config = get_config() - yield config - config._ensure_unconfigure() # cleanup, e.g. capman closing tmpfiles. - - -# Regex to match the session duration string in the summary: "74.34s". -rex_session_duration = re.compile(r"\d+\.\d\ds") -# Regex to match all the counts and phrases in the summary line: "34 passed, 111 skipped". -rex_outcome = re.compile(r"(\d+) (\w+)") - - -@final -class RunResult: - """The result of running a command from :class:`~pytest.Pytester`.""" - - def __init__( - self, - ret: int | ExitCode, - outlines: list[str], - errlines: list[str], - duration: float, - ) -> None: - try: - self.ret: int | ExitCode = ExitCode(ret) - """The return value.""" - except ValueError: - self.ret = ret - self.outlines = outlines - """List of lines captured from stdout.""" - self.errlines = errlines - """List of lines captured from stderr.""" - self.stdout = LineMatcher(outlines) - """:class:`~pytest.LineMatcher` of stdout. - - Use e.g. :func:`str(stdout) ` to reconstruct stdout, or the commonly used - :func:`stdout.fnmatch_lines() ` method. - """ - self.stderr = LineMatcher(errlines) - """:class:`~pytest.LineMatcher` of stderr.""" - self.duration = duration - """Duration in seconds.""" - - def __repr__(self) -> str: - return ( - "" - % (self.ret, len(self.stdout.lines), len(self.stderr.lines), self.duration) - ) - - def parseoutcomes(self) -> dict[str, int]: - """Return a dictionary of outcome noun -> count from parsing the terminal - output that the test process produced. - - The returned nouns will always be in plural form:: - - ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== - - Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. - """ - return self.parse_summary_nouns(self.outlines) - - @classmethod - def parse_summary_nouns(cls, lines) -> dict[str, int]: - """Extract the nouns from a pytest terminal summary line. - - It always returns the plural noun for consistency:: - - ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== - - Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. - """ - for line in reversed(lines): - if rex_session_duration.search(line): - outcomes = rex_outcome.findall(line) - ret = {noun: int(count) for (count, noun) in outcomes} - break - else: - raise ValueError("Pytest terminal summary report not found") - - to_plural = { - "warning": "warnings", - "error": "errors", - } - return {to_plural.get(k, k): v for k, v in ret.items()} - - def assert_outcomes( - self, - passed: int = 0, - skipped: int = 0, - failed: int = 0, - errors: int = 0, - xpassed: int = 0, - xfailed: int = 0, - warnings: int | None = None, - deselected: int | None = None, - ) -> None: - """ - Assert that the specified outcomes appear with the respective - numbers (0 means it didn't occur) in the text output from a test run. - - ``warnings`` and ``deselected`` are only checked if not None. - """ - __tracebackhide__ = True - from _pytest.pytester_assertions import assert_outcomes - - outcomes = self.parseoutcomes() - assert_outcomes( - outcomes, - passed=passed, - skipped=skipped, - failed=failed, - errors=errors, - xpassed=xpassed, - xfailed=xfailed, - warnings=warnings, - deselected=deselected, - ) - - -class SysModulesSnapshot: - def __init__(self, preserve: Callable[[str], bool] | None = None) -> None: - self.__preserve = preserve - self.__saved = dict(sys.modules) - - def restore(self) -> None: - if self.__preserve: - self.__saved.update( - (k, m) for k, m in sys.modules.items() if self.__preserve(k) - ) - sys.modules.clear() - sys.modules.update(self.__saved) - - -class SysPathsSnapshot: - def __init__(self) -> None: - self.__saved = list(sys.path), list(sys.meta_path) - - def restore(self) -> None: - sys.path[:], sys.meta_path[:] = self.__saved - - -@final -class Pytester: - """ - Facilities to write tests/configuration files, execute pytest in isolation, and match - against expected output, perfect for black-box testing of pytest plugins. - - It attempts to isolate the test run from external factors as much as possible, modifying - the current working directory to :attr:`path` and environment variables during initialization. - """ - - __test__ = False - - CLOSE_STDIN: Final = NOTSET - - class TimeoutExpired(Exception): - pass - - def __init__( - self, - request: FixtureRequest, - tmp_path_factory: TempPathFactory, - monkeypatch: MonkeyPatch, - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - self._request = request - self._mod_collections: WeakKeyDictionary[Collector, list[Item | Collector]] = ( - WeakKeyDictionary() - ) - if request.function: - name: str = request.function.__name__ - else: - name = request.node.name - self._name = name - self._path: Path = tmp_path_factory.mktemp(name, numbered=True) - #: A list of plugins to use with :py:meth:`parseconfig` and - #: :py:meth:`runpytest`. Initially this is an empty list but plugins can - #: be added to the list. The type of items to add to the list depends on - #: the method using them so refer to them for details. - self.plugins: list[str | _PluggyPlugin] = [] - self._sys_path_snapshot = SysPathsSnapshot() - self._sys_modules_snapshot = self.__take_sys_modules_snapshot() - self._request.addfinalizer(self._finalize) - self._method = self._request.config.getoption("--runpytest") - self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True) - - self._monkeypatch = mp = monkeypatch - self.chdir() - mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self._test_tmproot)) - # Ensure no unexpected caching via tox. - mp.delenv("TOX_ENV_DIR", raising=False) - # Discard outer pytest options. - mp.delenv("PYTEST_ADDOPTS", raising=False) - # Ensure no user config is used. - tmphome = str(self.path) - mp.setenv("HOME", tmphome) - mp.setenv("USERPROFILE", tmphome) - # Do not use colors for inner runs by default. - mp.setenv("PY_COLORS", "0") - - @property - def path(self) -> Path: - """Temporary directory path used to create files/run tests from, etc.""" - return self._path - - def __repr__(self) -> str: - return f"" - - def _finalize(self) -> None: - """ - Clean up global state artifacts. - - Some methods modify the global interpreter state and this tries to - clean this up. It does not remove the temporary directory however so - it can be looked at after the test run has finished. - """ - self._sys_modules_snapshot.restore() - self._sys_path_snapshot.restore() - - def __take_sys_modules_snapshot(self) -> SysModulesSnapshot: - # Some zope modules used by twisted-related tests keep internal state - # and can't be deleted; we had some trouble in the past with - # `zope.interface` for example. - # - # Preserve readline due to https://bugs.python.org/issue41033. - # pexpect issues a SIGWINCH. - def preserve_module(name): - return name.startswith(("zope", "readline")) - - return SysModulesSnapshot(preserve=preserve_module) - - def make_hook_recorder(self, pluginmanager: PytestPluginManager) -> HookRecorder: - """Create a new :class:`HookRecorder` for a :class:`PytestPluginManager`.""" - pluginmanager.reprec = reprec = HookRecorder(pluginmanager, _ispytest=True) # type: ignore[attr-defined] - self._request.addfinalizer(reprec.finish_recording) - return reprec - - def chdir(self) -> None: - """Cd into the temporary directory. - - This is done automatically upon instantiation. - """ - self._monkeypatch.chdir(self.path) - - def _makefile( - self, - ext: str, - lines: Sequence[Any | bytes], - files: dict[str, str], - encoding: str = "utf-8", - ) -> Path: - items = list(files.items()) - - if ext is None: - raise TypeError("ext must not be None") - - if ext and not ext.startswith("."): - raise ValueError( - f"pytester.makefile expects a file extension, try .{ext} instead of {ext}" - ) - - def to_text(s: Any | bytes) -> str: - return s.decode(encoding) if isinstance(s, bytes) else str(s) - - if lines: - source = "\n".join(to_text(x) for x in lines) - basename = self._name - items.insert(0, (basename, source)) - - ret = None - for basename, value in items: - p = self.path.joinpath(basename).with_suffix(ext) - p.parent.mkdir(parents=True, exist_ok=True) - source_ = Source(value) - source = "\n".join(to_text(line) for line in source_.lines) - p.write_text(source.strip(), encoding=encoding) - if ret is None: - ret = p - assert ret is not None - return ret - - def makefile(self, ext: str, *args: str, **kwargs: str) -> Path: - r"""Create new text file(s) in the test directory. - - :param ext: - The extension the file(s) should use, including the dot, e.g. `.py`. - :param args: - All args are treated as strings and joined using newlines. - The result is written as contents to the file. The name of the - file is based on the test function requesting this fixture. - :param kwargs: - Each keyword is the name of a file, while the value of it will - be written as contents of the file. - :returns: - The first created file. - - Examples: - - .. code-block:: python - - pytester.makefile(".txt", "line1", "line2") - - pytester.makefile(".ini", pytest="[pytest]\naddopts=-rs\n") - - To create binary files, use :meth:`pathlib.Path.write_bytes` directly: - - .. code-block:: python - - filename = pytester.path.joinpath("foo.bin") - filename.write_bytes(b"...") - """ - return self._makefile(ext, args, kwargs) - - def makeconftest(self, source: str) -> Path: - """Write a conftest.py file. - - :param source: The contents. - :returns: The conftest.py file. - """ - return self.makepyfile(conftest=source) - - def makeini(self, source: str) -> Path: - """Write a tox.ini file. - - :param source: The contents. - :returns: The tox.ini file. - """ - return self.makefile(".ini", tox=source) - - def getinicfg(self, source: str) -> SectionWrapper: - """Return the pytest section from the tox.ini config file.""" - p = self.makeini(source) - return IniConfig(str(p))["pytest"] - - def makepyprojecttoml(self, source: str) -> Path: - """Write a pyproject.toml file. - - :param source: The contents. - :returns: The pyproject.ini file. - - .. versionadded:: 6.0 - """ - return self.makefile(".toml", pyproject=source) - - def makepyfile(self, *args, **kwargs) -> Path: - r"""Shortcut for .makefile() with a .py extension. - - Defaults to the test name with a '.py' extension, e.g test_foobar.py, overwriting - existing files. - - Examples: - - .. code-block:: python - - def test_something(pytester): - # Initial file is created test_something.py. - pytester.makepyfile("foobar") - # To create multiple files, pass kwargs accordingly. - pytester.makepyfile(custom="foobar") - # At this point, both 'test_something.py' & 'custom.py' exist in the test directory. - - """ - return self._makefile(".py", args, kwargs) - - def maketxtfile(self, *args, **kwargs) -> Path: - r"""Shortcut for .makefile() with a .txt extension. - - Defaults to the test name with a '.txt' extension, e.g test_foobar.txt, overwriting - existing files. - - Examples: - - .. code-block:: python - - def test_something(pytester): - # Initial file is created test_something.txt. - pytester.maketxtfile("foobar") - # To create multiple files, pass kwargs accordingly. - pytester.maketxtfile(custom="foobar") - # At this point, both 'test_something.txt' & 'custom.txt' exist in the test directory. - - """ - return self._makefile(".txt", args, kwargs) - - def syspathinsert(self, path: str | os.PathLike[str] | None = None) -> None: - """Prepend a directory to sys.path, defaults to :attr:`path`. - - This is undone automatically when this object dies at the end of each - test. - - :param path: - The path. - """ - if path is None: - path = self.path - - self._monkeypatch.syspath_prepend(str(path)) - - def mkdir(self, name: str | os.PathLike[str]) -> Path: - """Create a new (sub)directory. - - :param name: - The name of the directory, relative to the pytester path. - :returns: - The created directory. - :rtype: pathlib.Path - """ - p = self.path / name - p.mkdir() - return p - - def mkpydir(self, name: str | os.PathLike[str]) -> Path: - """Create a new python package. - - This creates a (sub)directory with an empty ``__init__.py`` file so it - gets recognised as a Python package. - """ - p = self.path / name - p.mkdir() - p.joinpath("__init__.py").touch() - return p - - def copy_example(self, name: str | None = None) -> Path: - """Copy file from project's directory into the testdir. - - :param name: - The name of the file to copy. - :return: - Path to the copied directory (inside ``self.path``). - :rtype: pathlib.Path - """ - example_dir_ = self._request.config.getini("pytester_example_dir") - if example_dir_ is None: - raise ValueError("pytester_example_dir is unset, can't copy examples") - example_dir: Path = self._request.config.rootpath / example_dir_ - - for extra_element in self._request.node.iter_markers("pytester_example_path"): - assert extra_element.args - example_dir = example_dir.joinpath(*extra_element.args) - - if name is None: - func_name = self._name - maybe_dir = example_dir / func_name - maybe_file = example_dir / (func_name + ".py") - - if maybe_dir.is_dir(): - example_path = maybe_dir - elif maybe_file.is_file(): - example_path = maybe_file - else: - raise LookupError( - f"{func_name} can't be found as module or package in {example_dir}" - ) - else: - example_path = example_dir.joinpath(name) - - if example_path.is_dir() and not example_path.joinpath("__init__.py").is_file(): - shutil.copytree(example_path, self.path, symlinks=True, dirs_exist_ok=True) - return self.path - elif example_path.is_file(): - result = self.path.joinpath(example_path.name) - shutil.copy(example_path, result) - return result - else: - raise LookupError( - f'example "{example_path}" is not found as a file or directory' - ) - - def getnode(self, config: Config, arg: str | os.PathLike[str]) -> Collector | Item: - """Get the collection node of a file. - - :param config: - A pytest config. - See :py:meth:`parseconfig` and :py:meth:`parseconfigure` for creating it. - :param arg: - Path to the file. - :returns: - The node. - """ - session = Session.from_config(config) - assert "::" not in str(arg) - p = Path(os.path.abspath(arg)) - config.hook.pytest_sessionstart(session=session) - res = session.perform_collect([str(p)], genitems=False)[0] - config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) - return res - - def getpathnode(self, path: str | os.PathLike[str]) -> Collector | Item: - """Return the collection node of a file. - - This is like :py:meth:`getnode` but uses :py:meth:`parseconfigure` to - create the (configured) pytest Config instance. - - :param path: - Path to the file. - :returns: - The node. - """ - path = Path(path) - config = self.parseconfigure(path) - session = Session.from_config(config) - x = bestrelpath(session.path, path) - config.hook.pytest_sessionstart(session=session) - res = session.perform_collect([x], genitems=False)[0] - config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) - return res - - def genitems(self, colitems: Sequence[Item | Collector]) -> list[Item]: - """Generate all test items from a collection node. - - This recurses into the collection node and returns a list of all the - test items contained within. - - :param colitems: - The collection nodes. - :returns: - The collected items. - """ - session = colitems[0].session - result: list[Item] = [] - for colitem in colitems: - result.extend(session.genitems(colitem)) - return result - - def runitem(self, source: str) -> Any: - """Run the "test_func" Item. - - The calling test instance (class containing the test method) must - provide a ``.getrunner()`` method which should return a runner which - can run the test protocol for a single item, e.g. - ``_pytest.runner.runtestprotocol``. - """ - # used from runner functional tests - item = self.getitem(source) - # the test class where we are called from wants to provide the runner - testclassinstance = self._request.instance - runner = testclassinstance.getrunner() - return runner(item) - - def inline_runsource(self, source: str, *cmdlineargs) -> HookRecorder: - """Run a test module in process using ``pytest.main()``. - - This run writes "source" into a temporary file and runs - ``pytest.main()`` on it, returning a :py:class:`HookRecorder` instance - for the result. - - :param source: The source code of the test module. - :param cmdlineargs: Any extra command line arguments to use. - """ - p = self.makepyfile(source) - values = [*list(cmdlineargs), p] - return self.inline_run(*values) - - def inline_genitems(self, *args) -> tuple[list[Item], HookRecorder]: - """Run ``pytest.main(['--collect-only'])`` in-process. - - Runs the :py:func:`pytest.main` function to run all of pytest inside - the test process itself like :py:meth:`inline_run`, but returns a - tuple of the collected items and a :py:class:`HookRecorder` instance. - """ - rec = self.inline_run("--collect-only", *args) - items = [x.item for x in rec.getcalls("pytest_itemcollected")] - return items, rec - - def inline_run( - self, - *args: str | os.PathLike[str], - plugins=(), - no_reraise_ctrlc: bool = False, - ) -> HookRecorder: - """Run ``pytest.main()`` in-process, returning a HookRecorder. - - Runs the :py:func:`pytest.main` function to run all of pytest inside - the test process itself. This means it can return a - :py:class:`HookRecorder` instance which gives more detailed results - from that run than can be done by matching stdout/stderr from - :py:meth:`runpytest`. - - :param args: - Command line arguments to pass to :py:func:`pytest.main`. - :param plugins: - Extra plugin instances the ``pytest.main()`` instance should use. - :param no_reraise_ctrlc: - Typically we reraise keyboard interrupts from the child run. If - True, the KeyboardInterrupt exception is captured. - """ - # (maybe a cpython bug?) the importlib cache sometimes isn't updated - # properly between file creation and inline_run (especially if imports - # are interspersed with file creation) - importlib.invalidate_caches() - - plugins = list(plugins) - finalizers = [] - try: - # Any sys.module or sys.path changes done while running pytest - # inline should be reverted after the test run completes to avoid - # clashing with later inline tests run within the same pytest test, - # e.g. just because they use matching test module names. - finalizers.append(self.__take_sys_modules_snapshot().restore) - finalizers.append(SysPathsSnapshot().restore) - - # Important note: - # - our tests should not leave any other references/registrations - # laying around other than possibly loaded test modules - # referenced from sys.modules, as nothing will clean those up - # automatically - - rec = [] - - class Collect: - def pytest_configure(x, config: Config) -> None: - rec.append(self.make_hook_recorder(config.pluginmanager)) - - plugins.append(Collect()) - ret = main([str(x) for x in args], plugins=plugins) - if len(rec) == 1: - reprec = rec.pop() - else: - - class reprec: # type: ignore - pass - - reprec.ret = ret - - # Typically we reraise keyboard interrupts from the child run - # because it's our user requesting interruption of the testing. - if ret == ExitCode.INTERRUPTED and not no_reraise_ctrlc: - calls = reprec.getcalls("pytest_keyboard_interrupt") - if calls and calls[-1].excinfo.type == KeyboardInterrupt: - raise KeyboardInterrupt() - return reprec - finally: - for finalizer in finalizers: - finalizer() - - def runpytest_inprocess( - self, *args: str | os.PathLike[str], **kwargs: Any - ) -> RunResult: - """Return result of running pytest in-process, providing a similar - interface to what self.runpytest() provides.""" - syspathinsert = kwargs.pop("syspathinsert", False) - - if syspathinsert: - self.syspathinsert() - now = timing.time() - capture = _get_multicapture("sys") - capture.start_capturing() - try: - try: - reprec = self.inline_run(*args, **kwargs) - except SystemExit as e: - ret = e.args[0] - try: - ret = ExitCode(e.args[0]) - except ValueError: - pass - - class reprec: # type: ignore - ret = ret - - except Exception: - traceback.print_exc() - - class reprec: # type: ignore - ret = ExitCode(3) - - finally: - out, err = capture.readouterr() - capture.stop_capturing() - sys.stdout.write(out) - sys.stderr.write(err) - - assert reprec.ret is not None - res = RunResult( - reprec.ret, out.splitlines(), err.splitlines(), timing.time() - now - ) - res.reprec = reprec # type: ignore - return res - - def runpytest(self, *args: str | os.PathLike[str], **kwargs: Any) -> RunResult: - """Run pytest inline or in a subprocess, depending on the command line - option "--runpytest" and return a :py:class:`~pytest.RunResult`.""" - new_args = self._ensure_basetemp(args) - if self._method == "inprocess": - return self.runpytest_inprocess(*new_args, **kwargs) - elif self._method == "subprocess": - return self.runpytest_subprocess(*new_args, **kwargs) - raise RuntimeError(f"Unrecognized runpytest option: {self._method}") - - def _ensure_basetemp( - self, args: Sequence[str | os.PathLike[str]] - ) -> list[str | os.PathLike[str]]: - new_args = list(args) - for x in new_args: - if str(x).startswith("--basetemp"): - break - else: - new_args.append( - "--basetemp={}".format(self.path.parent.joinpath("basetemp")) - ) - return new_args - - def parseconfig(self, *args: str | os.PathLike[str]) -> Config: - """Return a new pytest :class:`pytest.Config` instance from given - commandline args. - - This invokes the pytest bootstrapping code in _pytest.config to create a - new :py:class:`pytest.PytestPluginManager` and call the - :hook:`pytest_cmdline_parse` hook to create a new :class:`pytest.Config` - instance. - - If :attr:`plugins` has been populated they should be plugin modules - to be registered with the plugin manager. - """ - import _pytest.config - - new_args = self._ensure_basetemp(args) - new_args = [str(x) for x in new_args] - - config = _pytest.config._prepareconfig(new_args, self.plugins) # type: ignore[arg-type] - # we don't know what the test will do with this half-setup config - # object and thus we make sure it gets unconfigured properly in any - # case (otherwise capturing could still be active, for example) - self._request.addfinalizer(config._ensure_unconfigure) - return config - - def parseconfigure(self, *args: str | os.PathLike[str]) -> Config: - """Return a new pytest configured Config instance. - - Returns a new :py:class:`pytest.Config` instance like - :py:meth:`parseconfig`, but also calls the :hook:`pytest_configure` - hook. - """ - config = self.parseconfig(*args) - config._do_configure() - return config - - def getitem( - self, source: str | os.PathLike[str], funcname: str = "test_func" - ) -> Item: - """Return the test item for a test function. - - Writes the source to a python file and runs pytest's collection on - the resulting module, returning the test item for the requested - function name. - - :param source: - The module source. - :param funcname: - The name of the test function for which to return a test item. - :returns: - The test item. - """ - items = self.getitems(source) - for item in items: - if item.name == funcname: - return item - assert 0, f"{funcname!r} item not found in module:\n{source}\nitems: {items}" - - def getitems(self, source: str | os.PathLike[str]) -> list[Item]: - """Return all test items collected from the module. - - Writes the source to a Python file and runs pytest's collection on - the resulting module, returning all test items contained within. - """ - modcol = self.getmodulecol(source) - return self.genitems([modcol]) - - def getmodulecol( - self, - source: str | os.PathLike[str], - configargs=(), - *, - withinit: bool = False, - ): - """Return the module collection node for ``source``. - - Writes ``source`` to a file using :py:meth:`makepyfile` and then - runs the pytest collection on it, returning the collection node for the - test module. - - :param source: - The source code of the module to collect. - - :param configargs: - Any extra arguments to pass to :py:meth:`parseconfigure`. - - :param withinit: - Whether to also write an ``__init__.py`` file to the same - directory to ensure it is a package. - """ - if isinstance(source, os.PathLike): - path = self.path.joinpath(source) - assert not withinit, "not supported for paths" - else: - kw = {self._name: str(source)} - path = self.makepyfile(**kw) - if withinit: - self.makepyfile(__init__="#") - self.config = config = self.parseconfigure(path, *configargs) - return self.getnode(config, path) - - def collect_by_name(self, modcol: Collector, name: str) -> Item | Collector | None: - """Return the collection node for name from the module collection. - - Searches a module collection node for a collection node matching the - given name. - - :param modcol: A module collection node; see :py:meth:`getmodulecol`. - :param name: The name of the node to return. - """ - if modcol not in self._mod_collections: - self._mod_collections[modcol] = list(modcol.collect()) - for colitem in self._mod_collections[modcol]: - if colitem.name == name: - return colitem - return None - - def popen( - self, - cmdargs: Sequence[str | os.PathLike[str]], - stdout: int | TextIO = subprocess.PIPE, - stderr: int | TextIO = subprocess.PIPE, - stdin: NotSetType | bytes | IO[Any] | int = CLOSE_STDIN, - **kw, - ): - """Invoke :py:class:`subprocess.Popen`. - - Calls :py:class:`subprocess.Popen` making sure the current working - directory is in ``PYTHONPATH``. - - You probably want to use :py:meth:`run` instead. - """ - env = os.environ.copy() - env["PYTHONPATH"] = os.pathsep.join( - filter(None, [os.getcwd(), env.get("PYTHONPATH", "")]) - ) - kw["env"] = env - - if stdin is self.CLOSE_STDIN: - kw["stdin"] = subprocess.PIPE - elif isinstance(stdin, bytes): - kw["stdin"] = subprocess.PIPE - else: - kw["stdin"] = stdin - - popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) - if stdin is self.CLOSE_STDIN: - assert popen.stdin is not None - popen.stdin.close() - elif isinstance(stdin, bytes): - assert popen.stdin is not None - popen.stdin.write(stdin) - - return popen - - def run( - self, - *cmdargs: str | os.PathLike[str], - timeout: float | None = None, - stdin: NotSetType | bytes | IO[Any] | int = CLOSE_STDIN, - ) -> RunResult: - """Run a command with arguments. - - Run a process using :py:class:`subprocess.Popen` saving the stdout and - stderr. - - :param cmdargs: - The sequence of arguments to pass to :py:class:`subprocess.Popen`, - with path-like objects being converted to :py:class:`str` - automatically. - :param timeout: - The period in seconds after which to timeout and raise - :py:class:`Pytester.TimeoutExpired`. - :param stdin: - Optional standard input. - - - If it is ``CLOSE_STDIN`` (Default), then this method calls - :py:class:`subprocess.Popen` with ``stdin=subprocess.PIPE``, and - the standard input is closed immediately after the new command is - started. - - - If it is of type :py:class:`bytes`, these bytes are sent to the - standard input of the command. - - - Otherwise, it is passed through to :py:class:`subprocess.Popen`. - For further information in this case, consult the document of the - ``stdin`` parameter in :py:class:`subprocess.Popen`. - :type stdin: _pytest.compat.NotSetType | bytes | IO[Any] | int - :returns: - The result. - - """ - __tracebackhide__ = True - - cmdargs = tuple(os.fspath(arg) for arg in cmdargs) - p1 = self.path.joinpath("stdout") - p2 = self.path.joinpath("stderr") - print("running:", *cmdargs) - print(" in:", Path.cwd()) - - with p1.open("w", encoding="utf8") as f1, p2.open("w", encoding="utf8") as f2: - now = timing.time() - popen = self.popen( - cmdargs, - stdin=stdin, - stdout=f1, - stderr=f2, - close_fds=(sys.platform != "win32"), - ) - if popen.stdin is not None: - popen.stdin.close() - - def handle_timeout() -> None: - __tracebackhide__ = True - - timeout_message = f"{timeout} second timeout expired running: {cmdargs}" - - popen.kill() - popen.wait() - raise self.TimeoutExpired(timeout_message) - - if timeout is None: - ret = popen.wait() - else: - try: - ret = popen.wait(timeout) - except subprocess.TimeoutExpired: - handle_timeout() - - with p1.open(encoding="utf8") as f1, p2.open(encoding="utf8") as f2: - out = f1.read().splitlines() - err = f2.read().splitlines() - - self._dump_lines(out, sys.stdout) - self._dump_lines(err, sys.stderr) - - with contextlib.suppress(ValueError): - ret = ExitCode(ret) - return RunResult(ret, out, err, timing.time() - now) - - def _dump_lines(self, lines, fp): - try: - for line in lines: - print(line, file=fp) - except UnicodeEncodeError: - print(f"couldn't print to {fp} because of encoding") - - def _getpytestargs(self) -> tuple[str, ...]: - return sys.executable, "-mpytest" - - def runpython(self, script: os.PathLike[str]) -> RunResult: - """Run a python script using sys.executable as interpreter.""" - return self.run(sys.executable, script) - - def runpython_c(self, command: str) -> RunResult: - """Run ``python -c "command"``.""" - return self.run(sys.executable, "-c", command) - - def runpytest_subprocess( - self, *args: str | os.PathLike[str], timeout: float | None = None - ) -> RunResult: - """Run pytest as a subprocess with given arguments. - - Any plugins added to the :py:attr:`plugins` list will be added using the - ``-p`` command line option. Additionally ``--basetemp`` is used to put - any temporary files and directories in a numbered directory prefixed - with "runpytest-" to not conflict with the normal numbered pytest - location for temporary files and directories. - - :param args: - The sequence of arguments to pass to the pytest subprocess. - :param timeout: - The period in seconds after which to timeout and raise - :py:class:`Pytester.TimeoutExpired`. - :returns: - The result. - """ - __tracebackhide__ = True - p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700) - args = (f"--basetemp={p}", *args) - plugins = [x for x in self.plugins if isinstance(x, str)] - if plugins: - args = ("-p", plugins[0], *args) - args = self._getpytestargs() + args - return self.run(*args, timeout=timeout) - - def spawn_pytest(self, string: str, expect_timeout: float = 10.0) -> pexpect.spawn: - """Run pytest using pexpect. - - This makes sure to use the right pytest and sets up the temporary - directory locations. - - The pexpect child is returned. - """ - basetemp = self.path / "temp-pexpect" - basetemp.mkdir(mode=0o700) - invoke = " ".join(map(str, self._getpytestargs())) - cmd = f"{invoke} --basetemp={basetemp} {string}" - return self.spawn(cmd, expect_timeout=expect_timeout) - - def spawn(self, cmd: str, expect_timeout: float = 10.0) -> pexpect.spawn: - """Run a command using pexpect. - - The pexpect child is returned. - """ - pexpect = importorskip("pexpect", "3.0") - if hasattr(sys, "pypy_version_info") and "64" in platform.machine(): - skip("pypy-64 bit not supported") - if not hasattr(pexpect, "spawn"): - skip("pexpect.spawn not available") - logfile = self.path.joinpath("spawn.out").open("wb") - - child = pexpect.spawn(cmd, logfile=logfile, timeout=expect_timeout) - self._request.addfinalizer(logfile.close) - return child - - -class LineComp: - def __init__(self) -> None: - self.stringio = StringIO() - """:class:`python:io.StringIO()` instance used for input.""" - - def assert_contains_lines(self, lines2: Sequence[str]) -> None: - """Assert that ``lines2`` are contained (linearly) in :attr:`stringio`'s value. - - Lines are matched using :func:`LineMatcher.fnmatch_lines `. - """ - __tracebackhide__ = True - val = self.stringio.getvalue() - self.stringio.truncate(0) - self.stringio.seek(0) - lines1 = val.split("\n") - LineMatcher(lines1).fnmatch_lines(lines2) - - -class LineMatcher: - """Flexible matching of text. - - This is a convenience class to test large texts like the output of - commands. - - The constructor takes a list of lines without their trailing newlines, i.e. - ``text.splitlines()``. - """ - - def __init__(self, lines: list[str]) -> None: - self.lines = lines - self._log_output: list[str] = [] - - def __str__(self) -> str: - """Return the entire original text. - - .. versionadded:: 6.2 - You can use :meth:`str` in older versions. - """ - return "\n".join(self.lines) - - def _getlines(self, lines2: str | Sequence[str] | Source) -> Sequence[str]: - if isinstance(lines2, str): - lines2 = Source(lines2) - if isinstance(lines2, Source): - lines2 = lines2.strip().lines - return lines2 - - def fnmatch_lines_random(self, lines2: Sequence[str]) -> None: - """Check lines exist in the output in any order (using :func:`python:fnmatch.fnmatch`).""" - __tracebackhide__ = True - self._match_lines_random(lines2, fnmatch) - - def re_match_lines_random(self, lines2: Sequence[str]) -> None: - """Check lines exist in the output in any order (using :func:`python:re.match`).""" - __tracebackhide__ = True - self._match_lines_random(lines2, lambda name, pat: bool(re.match(pat, name))) - - def _match_lines_random( - self, lines2: Sequence[str], match_func: Callable[[str, str], bool] - ) -> None: - __tracebackhide__ = True - lines2 = self._getlines(lines2) - for line in lines2: - for x in self.lines: - if line == x or match_func(x, line): - self._log("matched: ", repr(line)) - break - else: - msg = f"line {line!r} not found in output" - self._log(msg) - self._fail(msg) - - def get_lines_after(self, fnline: str) -> Sequence[str]: - """Return all lines following the given line in the text. - - The given line can contain glob wildcards. - """ - for i, line in enumerate(self.lines): - if fnline == line or fnmatch(line, fnline): - return self.lines[i + 1 :] - raise ValueError(f"line {fnline!r} not found in output") - - def _log(self, *args) -> None: - self._log_output.append(" ".join(str(x) for x in args)) - - @property - def _log_text(self) -> str: - return "\n".join(self._log_output) - - def fnmatch_lines( - self, lines2: Sequence[str], *, consecutive: bool = False - ) -> None: - """Check lines exist in the output (using :func:`python:fnmatch.fnmatch`). - - The argument is a list of lines which have to match and can use glob - wildcards. If they do not match a pytest.fail() is called. The - matches and non-matches are also shown as part of the error message. - - :param lines2: String patterns to match. - :param consecutive: Match lines consecutively? - """ - __tracebackhide__ = True - self._match_lines(lines2, fnmatch, "fnmatch", consecutive=consecutive) - - def re_match_lines( - self, lines2: Sequence[str], *, consecutive: bool = False - ) -> None: - """Check lines exist in the output (using :func:`python:re.match`). - - The argument is a list of lines which have to match using ``re.match``. - If they do not match a pytest.fail() is called. - - The matches and non-matches are also shown as part of the error message. - - :param lines2: string patterns to match. - :param consecutive: match lines consecutively? - """ - __tracebackhide__ = True - self._match_lines( - lines2, - lambda name, pat: bool(re.match(pat, name)), - "re.match", - consecutive=consecutive, - ) - - def _match_lines( - self, - lines2: Sequence[str], - match_func: Callable[[str, str], bool], - match_nickname: str, - *, - consecutive: bool = False, - ) -> None: - """Underlying implementation of ``fnmatch_lines`` and ``re_match_lines``. - - :param Sequence[str] lines2: - List of string patterns to match. The actual format depends on - ``match_func``. - :param match_func: - A callable ``match_func(line, pattern)`` where line is the - captured line from stdout/stderr and pattern is the matching - pattern. - :param str match_nickname: - The nickname for the match function that will be logged to stdout - when a match occurs. - :param consecutive: - Match lines consecutively? - """ - if not isinstance(lines2, collections.abc.Sequence): - raise TypeError(f"invalid type for lines2: {type(lines2).__name__}") - lines2 = self._getlines(lines2) - lines1 = self.lines[:] - extralines = [] - __tracebackhide__ = True - wnick = len(match_nickname) + 1 - started = False - for line in lines2: - nomatchprinted = False - while lines1: - nextline = lines1.pop(0) - if line == nextline: - self._log("exact match:", repr(line)) - started = True - break - elif match_func(nextline, line): - self._log(f"{match_nickname}:", repr(line)) - self._log( - "{:>{width}}".format("with:", width=wnick), repr(nextline) - ) - started = True - break - else: - if consecutive and started: - msg = f"no consecutive match: {line!r}" - self._log(msg) - self._log( - "{:>{width}}".format("with:", width=wnick), repr(nextline) - ) - self._fail(msg) - if not nomatchprinted: - self._log( - "{:>{width}}".format("nomatch:", width=wnick), repr(line) - ) - nomatchprinted = True - self._log("{:>{width}}".format("and:", width=wnick), repr(nextline)) - extralines.append(nextline) - else: - msg = f"remains unmatched: {line!r}" - self._log(msg) - self._fail(msg) - self._log_output = [] - - def no_fnmatch_line(self, pat: str) -> None: - """Ensure captured lines do not match the given pattern, using ``fnmatch.fnmatch``. - - :param str pat: The pattern to match lines. - """ - __tracebackhide__ = True - self._no_match_line(pat, fnmatch, "fnmatch") - - def no_re_match_line(self, pat: str) -> None: - """Ensure captured lines do not match the given pattern, using ``re.match``. - - :param str pat: The regular expression to match lines. - """ - __tracebackhide__ = True - self._no_match_line( - pat, lambda name, pat: bool(re.match(pat, name)), "re.match" - ) - - def _no_match_line( - self, pat: str, match_func: Callable[[str, str], bool], match_nickname: str - ) -> None: - """Ensure captured lines does not have a the given pattern, using ``fnmatch.fnmatch``. - - :param str pat: The pattern to match lines. - """ - __tracebackhide__ = True - nomatch_printed = False - wnick = len(match_nickname) + 1 - for line in self.lines: - if match_func(line, pat): - msg = f"{match_nickname}: {pat!r}" - self._log(msg) - self._log("{:>{width}}".format("with:", width=wnick), repr(line)) - self._fail(msg) - else: - if not nomatch_printed: - self._log("{:>{width}}".format("nomatch:", width=wnick), repr(pat)) - nomatch_printed = True - self._log("{:>{width}}".format("and:", width=wnick), repr(line)) - self._log_output = [] - - def _fail(self, msg: str) -> None: - __tracebackhide__ = True - log_text = self._log_text - self._log_output = [] - fail(log_text) - - def str(self) -> str: - """Return the entire original text.""" - return str(self) diff --git a/.venv/lib/python3.12/site-packages/_pytest/pytester_assertions.py b/.venv/lib/python3.12/site-packages/_pytest/pytester_assertions.py deleted file mode 100644 index d543798f..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/pytester_assertions.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Helper plugin for pytester; should not be loaded on its own.""" - -# This plugin contains assertions used by pytester. pytester cannot -# contain them itself, since it is imported by the `pytest` module, -# hence cannot be subject to assertion rewriting, which requires a -# module to not be already imported. -from __future__ import annotations - -from typing import Sequence - -from _pytest.reports import CollectReport -from _pytest.reports import TestReport - - -def assertoutcome( - outcomes: tuple[ - Sequence[TestReport], - Sequence[CollectReport | TestReport], - Sequence[CollectReport | TestReport], - ], - passed: int = 0, - skipped: int = 0, - failed: int = 0, -) -> None: - __tracebackhide__ = True - - realpassed, realskipped, realfailed = outcomes - obtained = { - "passed": len(realpassed), - "skipped": len(realskipped), - "failed": len(realfailed), - } - expected = {"passed": passed, "skipped": skipped, "failed": failed} - assert obtained == expected, outcomes - - -def assert_outcomes( - outcomes: dict[str, int], - passed: int = 0, - skipped: int = 0, - failed: int = 0, - errors: int = 0, - xpassed: int = 0, - xfailed: int = 0, - warnings: int | None = None, - deselected: int | None = None, -) -> None: - """Assert that the specified outcomes appear with the respective - numbers (0 means it didn't occur) in the text output from a test run.""" - __tracebackhide__ = True - - obtained = { - "passed": outcomes.get("passed", 0), - "skipped": outcomes.get("skipped", 0), - "failed": outcomes.get("failed", 0), - "errors": outcomes.get("errors", 0), - "xpassed": outcomes.get("xpassed", 0), - "xfailed": outcomes.get("xfailed", 0), - } - expected = { - "passed": passed, - "skipped": skipped, - "failed": failed, - "errors": errors, - "xpassed": xpassed, - "xfailed": xfailed, - } - if warnings is not None: - obtained["warnings"] = outcomes.get("warnings", 0) - expected["warnings"] = warnings - if deselected is not None: - obtained["deselected"] = outcomes.get("deselected", 0) - expected["deselected"] = deselected - assert obtained == expected diff --git a/.venv/lib/python3.12/site-packages/_pytest/python.py b/.venv/lib/python3.12/site-packages/_pytest/python.py deleted file mode 100644 index 3478c34c..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/python.py +++ /dev/null @@ -1,1679 +0,0 @@ -# mypy: allow-untyped-defs -"""Python test discovery, setup and run of test functions.""" - -from __future__ import annotations - -import abc -from collections import Counter -from collections import defaultdict -import dataclasses -import enum -import fnmatch -from functools import partial -import inspect -import itertools -import os -from pathlib import Path -import types -from typing import Any -from typing import Callable -from typing import Dict -from typing import final -from typing import Generator -from typing import Iterable -from typing import Iterator -from typing import Literal -from typing import Mapping -from typing import Pattern -from typing import Sequence -from typing import TYPE_CHECKING -import warnings - -import _pytest -from _pytest import fixtures -from _pytest import nodes -from _pytest._code import filter_traceback -from _pytest._code import getfslineno -from _pytest._code.code import ExceptionInfo -from _pytest._code.code import TerminalRepr -from _pytest._code.code import Traceback -from _pytest._io.saferepr import saferepr -from _pytest.compat import ascii_escaped -from _pytest.compat import get_default_arg_names -from _pytest.compat import get_real_func -from _pytest.compat import getimfunc -from _pytest.compat import is_async_function -from _pytest.compat import is_generator -from _pytest.compat import LEGACY_PATH -from _pytest.compat import NOTSET -from _pytest.compat import safe_getattr -from _pytest.compat import safe_isclass -from _pytest.config import Config -from _pytest.config import hookimpl -from _pytest.config.argparsing import Parser -from _pytest.deprecated import check_ispytest -from _pytest.fixtures import FixtureDef -from _pytest.fixtures import FixtureRequest -from _pytest.fixtures import FuncFixtureInfo -from _pytest.fixtures import get_scope_node -from _pytest.main import Session -from _pytest.mark import MARK_GEN -from _pytest.mark import ParameterSet -from _pytest.mark.structures import get_unpacked_marks -from _pytest.mark.structures import Mark -from _pytest.mark.structures import MarkDecorator -from _pytest.mark.structures import normalize_mark_list -from _pytest.outcomes import fail -from _pytest.outcomes import skip -from _pytest.pathlib import fnmatch_ex -from _pytest.pathlib import import_path -from _pytest.pathlib import ImportPathMismatchError -from _pytest.pathlib import scandir -from _pytest.scope import _ScopeName -from _pytest.scope import Scope -from _pytest.stash import StashKey -from _pytest.warning_types import PytestCollectionWarning -from _pytest.warning_types import PytestReturnNotNoneWarning -from _pytest.warning_types import PytestUnhandledCoroutineWarning - - -if TYPE_CHECKING: - from typing_extensions import Self - - -def pytest_addoption(parser: Parser) -> None: - parser.addini( - "python_files", - type="args", - # NOTE: default is also used in AssertionRewritingHook. - default=["test_*.py", "*_test.py"], - help="Glob-style file patterns for Python test module discovery", - ) - parser.addini( - "python_classes", - type="args", - default=["Test"], - help="Prefixes or glob names for Python test class discovery", - ) - parser.addini( - "python_functions", - type="args", - default=["test"], - help="Prefixes or glob names for Python test function and method discovery", - ) - parser.addini( - "disable_test_id_escaping_and_forfeit_all_rights_to_community_support", - type="bool", - default=False, - help="Disable string escape non-ASCII characters, might cause unwanted " - "side effects(use at your own risk)", - ) - - -def pytest_generate_tests(metafunc: Metafunc) -> None: - for marker in metafunc.definition.iter_markers(name="parametrize"): - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) - - -def pytest_configure(config: Config) -> None: - config.addinivalue_line( - "markers", - "parametrize(argnames, argvalues): call a test function multiple " - "times passing in different arguments in turn. argvalues generally " - "needs to be a list of values if argnames specifies only one name " - "or a list of tuples of values if argnames specifies multiple names. " - "Example: @parametrize('arg1', [1,2]) would lead to two calls of the " - "decorated test function, one with arg1=1 and another with arg1=2." - "see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info " - "and examples.", - ) - config.addinivalue_line( - "markers", - "usefixtures(fixturename1, fixturename2, ...): mark tests as needing " - "all of the specified fixtures. see " - "https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures ", - ) - - -def async_warn_and_skip(nodeid: str) -> None: - msg = "async def functions are not natively supported and have been skipped.\n" - msg += ( - "You need to install a suitable plugin for your async framework, for example:\n" - ) - msg += " - anyio\n" - msg += " - pytest-asyncio\n" - msg += " - pytest-tornasync\n" - msg += " - pytest-trio\n" - msg += " - pytest-twisted" - warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid))) - skip(reason="async def function and no async plugin installed (see warnings)") - - -@hookimpl(trylast=True) -def pytest_pyfunc_call(pyfuncitem: Function) -> object | None: - testfunction = pyfuncitem.obj - if is_async_function(testfunction): - async_warn_and_skip(pyfuncitem.nodeid) - funcargs = pyfuncitem.funcargs - testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} - result = testfunction(**testargs) - if hasattr(result, "__await__") or hasattr(result, "__aiter__"): - async_warn_and_skip(pyfuncitem.nodeid) - elif result is not None: - warnings.warn( - PytestReturnNotNoneWarning( - f"Expected None, but {pyfuncitem.nodeid} returned {result!r}, which will be an error in a " - "future version of pytest. Did you mean to use `assert` instead of `return`?" - ) - ) - return True - - -def pytest_collect_directory( - path: Path, parent: nodes.Collector -) -> nodes.Collector | None: - pkginit = path / "__init__.py" - try: - has_pkginit = pkginit.is_file() - except PermissionError: - # See https://github.com/pytest-dev/pytest/issues/12120#issuecomment-2106349096. - return None - if has_pkginit: - return Package.from_parent(parent, path=path) - return None - - -def pytest_collect_file(file_path: Path, parent: nodes.Collector) -> Module | None: - if file_path.suffix == ".py": - if not parent.session.isinitpath(file_path): - if not path_matches_patterns( - file_path, parent.config.getini("python_files") - ): - return None - ihook = parent.session.gethookproxy(file_path) - module: Module = ihook.pytest_pycollect_makemodule( - module_path=file_path, parent=parent - ) - return module - return None - - -def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool: - """Return whether path matches any of the patterns in the list of globs given.""" - return any(fnmatch_ex(pattern, path) for pattern in patterns) - - -def pytest_pycollect_makemodule(module_path: Path, parent) -> Module: - return Module.from_parent(parent, path=module_path) - - -@hookimpl(trylast=True) -def pytest_pycollect_makeitem( - collector: Module | Class, name: str, obj: object -) -> None | nodes.Item | nodes.Collector | list[nodes.Item | nodes.Collector]: - assert isinstance(collector, (Class, Module)), type(collector) - # Nothing was collected elsewhere, let's do it here. - if safe_isclass(obj): - if collector.istestclass(obj, name): - return Class.from_parent(collector, name=name, obj=obj) - elif collector.istestfunction(obj, name): - # mock seems to store unbound methods (issue473), normalize it. - obj = getattr(obj, "__func__", obj) - # We need to try and unwrap the function if it's a functools.partial - # or a functools.wrapped. - # We mustn't if it's been wrapped with mock.patch (python 2 only). - if not (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))): - filename, lineno = getfslineno(obj) - warnings.warn_explicit( - message=PytestCollectionWarning( - f"cannot collect {name!r} because it is not a function." - ), - category=None, - filename=str(filename), - lineno=lineno + 1, - ) - elif getattr(obj, "__test__", True): - if is_generator(obj): - res = Function.from_parent(collector, name=name) - reason = ( - f"yield tests were removed in pytest 4.0 - {name} will be ignored" - ) - res.add_marker(MARK_GEN.xfail(run=False, reason=reason)) - res.warn(PytestCollectionWarning(reason)) - return res - else: - return list(collector._genfunctions(name, obj)) - return None - - -class PyobjMixin(nodes.Node): - """this mix-in inherits from Node to carry over the typing information - - as its intended to always mix in before a node - its position in the mro is unaffected""" - - _ALLOW_MARKERS = True - - @property - def module(self): - """Python module object this node was collected from (can be None).""" - node = self.getparent(Module) - return node.obj if node is not None else None - - @property - def cls(self): - """Python class object this node was collected from (can be None).""" - node = self.getparent(Class) - return node.obj if node is not None else None - - @property - def instance(self): - """Python instance object the function is bound to. - - Returns None if not a test method, e.g. for a standalone test function, - a class or a module. - """ - # Overridden by Function. - return None - - @property - def obj(self): - """Underlying Python object.""" - obj = getattr(self, "_obj", None) - if obj is None: - self._obj = obj = self._getobj() - # XXX evil hack - # used to avoid Function marker duplication - if self._ALLOW_MARKERS: - self.own_markers.extend(get_unpacked_marks(self.obj)) - # This assumes that `obj` is called before there is a chance - # to add custom keys to `self.keywords`, so no fear of overriding. - self.keywords.update((mark.name, mark) for mark in self.own_markers) - return obj - - @obj.setter - def obj(self, value): - self._obj = value - - def _getobj(self): - """Get the underlying Python object. May be overwritten by subclasses.""" - # TODO: Improve the type of `parent` such that assert/ignore aren't needed. - assert self.parent is not None - obj = self.parent.obj # type: ignore[attr-defined] - return getattr(obj, self.name) - - def getmodpath(self, stopatmodule: bool = True, includemodule: bool = False) -> str: - """Return Python path relative to the containing module.""" - parts = [] - for node in self.iter_parents(): - name = node.name - if isinstance(node, Module): - name = os.path.splitext(name)[0] - if stopatmodule: - if includemodule: - parts.append(name) - break - parts.append(name) - parts.reverse() - return ".".join(parts) - - def reportinfo(self) -> tuple[os.PathLike[str] | str, int | None, str]: - # XXX caching? - path, lineno = getfslineno(self.obj) - modpath = self.getmodpath() - return path, lineno, modpath - - -# As an optimization, these builtin attribute names are pre-ignored when -# iterating over an object during collection -- the pytest_pycollect_makeitem -# hook is not called for them. -# fmt: off -class _EmptyClass: pass # noqa: E701 -IGNORED_ATTRIBUTES = frozenset.union( - frozenset(), - # Module. - dir(types.ModuleType("empty_module")), - # Some extra module attributes the above doesn't catch. - {"__builtins__", "__file__", "__cached__"}, - # Class. - dir(_EmptyClass), - # Instance. - dir(_EmptyClass()), -) -del _EmptyClass -# fmt: on - - -class PyCollector(PyobjMixin, nodes.Collector, abc.ABC): - def funcnamefilter(self, name: str) -> bool: - return self._matches_prefix_or_glob_option("python_functions", name) - - def isnosetest(self, obj: object) -> bool: - """Look for the __test__ attribute, which is applied by the - @nose.tools.istest decorator. - """ - # We explicitly check for "is True" here to not mistakenly treat - # classes with a custom __getattr__ returning something truthy (like a - # function) as test classes. - return safe_getattr(obj, "__test__", False) is True - - def classnamefilter(self, name: str) -> bool: - return self._matches_prefix_or_glob_option("python_classes", name) - - def istestfunction(self, obj: object, name: str) -> bool: - if self.funcnamefilter(name) or self.isnosetest(obj): - if isinstance(obj, (staticmethod, classmethod)): - # staticmethods and classmethods need to be unwrapped. - obj = safe_getattr(obj, "__func__", False) - return callable(obj) and fixtures.getfixturemarker(obj) is None - else: - return False - - def istestclass(self, obj: object, name: str) -> bool: - if not (self.classnamefilter(name) or self.isnosetest(obj)): - return False - if inspect.isabstract(obj): - return False - return True - - def _matches_prefix_or_glob_option(self, option_name: str, name: str) -> bool: - """Check if the given name matches the prefix or glob-pattern defined - in ini configuration.""" - for option in self.config.getini(option_name): - if name.startswith(option): - return True - # Check that name looks like a glob-string before calling fnmatch - # because this is called for every name in each collected module, - # and fnmatch is somewhat expensive to call. - elif ("*" in option or "?" in option or "[" in option) and fnmatch.fnmatch( - name, option - ): - return True - return False - - def collect(self) -> Iterable[nodes.Item | nodes.Collector]: - if not getattr(self.obj, "__test__", True): - return [] - - # Avoid random getattrs and peek in the __dict__ instead. - dicts = [getattr(self.obj, "__dict__", {})] - if isinstance(self.obj, type): - for basecls in self.obj.__mro__: - dicts.append(basecls.__dict__) - - # In each class, nodes should be definition ordered. - # __dict__ is definition ordered. - seen: set[str] = set() - dict_values: list[list[nodes.Item | nodes.Collector]] = [] - ihook = self.ihook - for dic in dicts: - values: list[nodes.Item | nodes.Collector] = [] - # Note: seems like the dict can change during iteration - - # be careful not to remove the list() without consideration. - for name, obj in list(dic.items()): - if name in IGNORED_ATTRIBUTES: - continue - if name in seen: - continue - seen.add(name) - res = ihook.pytest_pycollect_makeitem( - collector=self, name=name, obj=obj - ) - if res is None: - continue - elif isinstance(res, list): - values.extend(res) - else: - values.append(res) - dict_values.append(values) - - # Between classes in the class hierarchy, reverse-MRO order -- nodes - # inherited from base classes should come before subclasses. - result = [] - for values in reversed(dict_values): - result.extend(values) - return result - - def _genfunctions(self, name: str, funcobj) -> Iterator[Function]: - modulecol = self.getparent(Module) - assert modulecol is not None - module = modulecol.obj - clscol = self.getparent(Class) - cls = clscol and clscol.obj or None - - definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj) - fixtureinfo = definition._fixtureinfo - - # pytest_generate_tests impls call metafunc.parametrize() which fills - # metafunc._calls, the outcome of the hook. - metafunc = Metafunc( - definition=definition, - fixtureinfo=fixtureinfo, - config=self.config, - cls=cls, - module=module, - _ispytest=True, - ) - methods = [] - if hasattr(module, "pytest_generate_tests"): - methods.append(module.pytest_generate_tests) - if cls is not None and hasattr(cls, "pytest_generate_tests"): - methods.append(cls().pytest_generate_tests) - self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc)) - - if not metafunc._calls: - yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo) - else: - # Direct parametrizations taking place in module/class-specific - # `metafunc.parametrize` calls may have shadowed some fixtures, so make sure - # we update what the function really needs a.k.a its fixture closure. Note that - # direct parametrizations using `@pytest.mark.parametrize` have already been considered - # into making the closure using `ignore_args` arg to `getfixtureclosure`. - fixtureinfo.prune_dependency_tree() - - for callspec in metafunc._calls: - subname = f"{name}[{callspec.id}]" - yield Function.from_parent( - self, - name=subname, - callspec=callspec, - fixtureinfo=fixtureinfo, - keywords={callspec.id: True}, - originalname=name, - ) - - -def importtestmodule( - path: Path, - config: Config, -): - # We assume we are only called once per module. - importmode = config.getoption("--import-mode") - try: - mod = import_path( - path, - mode=importmode, - root=config.rootpath, - consider_namespace_packages=config.getini("consider_namespace_packages"), - ) - except SyntaxError as e: - raise nodes.Collector.CollectError( - ExceptionInfo.from_current().getrepr(style="short") - ) from e - except ImportPathMismatchError as e: - raise nodes.Collector.CollectError( - "import file mismatch:\n" - "imported module {!r} has this __file__ attribute:\n" - " {}\n" - "which is not the same as the test file we want to collect:\n" - " {}\n" - "HINT: remove __pycache__ / .pyc files and/or use a " - "unique basename for your test file modules".format(*e.args) - ) from e - except ImportError as e: - exc_info = ExceptionInfo.from_current() - if config.get_verbosity() < 2: - exc_info.traceback = exc_info.traceback.filter(filter_traceback) - exc_repr = ( - exc_info.getrepr(style="short") - if exc_info.traceback - else exc_info.exconly() - ) - formatted_tb = str(exc_repr) - raise nodes.Collector.CollectError( - f"ImportError while importing test module '{path}'.\n" - "Hint: make sure your test modules/packages have valid Python names.\n" - "Traceback:\n" - f"{formatted_tb}" - ) from e - except skip.Exception as e: - if e.allow_module_level: - raise - raise nodes.Collector.CollectError( - "Using pytest.skip outside of a test will skip the entire module. " - "If that's your intention, pass `allow_module_level=True`. " - "If you want to skip a specific test or an entire class, " - "use the @pytest.mark.skip or @pytest.mark.skipif decorators." - ) from e - config.pluginmanager.consider_module(mod) - return mod - - -class Module(nodes.File, PyCollector): - """Collector for test classes and functions in a Python module.""" - - def _getobj(self): - return importtestmodule(self.path, self.config) - - def collect(self) -> Iterable[nodes.Item | nodes.Collector]: - self._register_setup_module_fixture() - self._register_setup_function_fixture() - self.session._fixturemanager.parsefactories(self) - return super().collect() - - def _register_setup_module_fixture(self) -> None: - """Register an autouse, module-scoped fixture for the collected module object - that invokes setUpModule/tearDownModule if either or both are available. - - Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with - other fixtures (#517). - """ - setup_module = _get_first_non_fixture_func( - self.obj, ("setUpModule", "setup_module") - ) - teardown_module = _get_first_non_fixture_func( - self.obj, ("tearDownModule", "teardown_module") - ) - - if setup_module is None and teardown_module is None: - return - - def xunit_setup_module_fixture(request) -> Generator[None]: - module = request.module - if setup_module is not None: - _call_with_optional_argument(setup_module, module) - yield - if teardown_module is not None: - _call_with_optional_argument(teardown_module, module) - - self.session._fixturemanager._register_fixture( - # Use a unique name to speed up lookup. - name=f"_xunit_setup_module_fixture_{self.obj.__name__}", - func=xunit_setup_module_fixture, - nodeid=self.nodeid, - scope="module", - autouse=True, - ) - - def _register_setup_function_fixture(self) -> None: - """Register an autouse, function-scoped fixture for the collected module object - that invokes setup_function/teardown_function if either or both are available. - - Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with - other fixtures (#517). - """ - setup_function = _get_first_non_fixture_func(self.obj, ("setup_function",)) - teardown_function = _get_first_non_fixture_func( - self.obj, ("teardown_function",) - ) - if setup_function is None and teardown_function is None: - return - - def xunit_setup_function_fixture(request) -> Generator[None]: - if request.instance is not None: - # in this case we are bound to an instance, so we need to let - # setup_method handle this - yield - return - function = request.function - if setup_function is not None: - _call_with_optional_argument(setup_function, function) - yield - if teardown_function is not None: - _call_with_optional_argument(teardown_function, function) - - self.session._fixturemanager._register_fixture( - # Use a unique name to speed up lookup. - name=f"_xunit_setup_function_fixture_{self.obj.__name__}", - func=xunit_setup_function_fixture, - nodeid=self.nodeid, - scope="function", - autouse=True, - ) - - -class Package(nodes.Directory): - """Collector for files and directories in a Python packages -- directories - with an `__init__.py` file. - - .. note:: - - Directories without an `__init__.py` file are instead collected by - :class:`~pytest.Dir` by default. Both are :class:`~pytest.Directory` - collectors. - - .. versionchanged:: 8.0 - - Now inherits from :class:`~pytest.Directory`. - """ - - def __init__( - self, - fspath: LEGACY_PATH | None, - parent: nodes.Collector, - # NOTE: following args are unused: - config=None, - session=None, - nodeid=None, - path: Path | None = None, - ) -> None: - # NOTE: Could be just the following, but kept as-is for compat. - # super().__init__(self, fspath, parent=parent) - session = parent.session - super().__init__( - fspath=fspath, - path=path, - parent=parent, - config=config, - session=session, - nodeid=nodeid, - ) - - def setup(self) -> None: - init_mod = importtestmodule(self.path / "__init__.py", self.config) - - # Not using fixtures to call setup_module here because autouse fixtures - # from packages are not called automatically (#4085). - setup_module = _get_first_non_fixture_func( - init_mod, ("setUpModule", "setup_module") - ) - if setup_module is not None: - _call_with_optional_argument(setup_module, init_mod) - - teardown_module = _get_first_non_fixture_func( - init_mod, ("tearDownModule", "teardown_module") - ) - if teardown_module is not None: - func = partial(_call_with_optional_argument, teardown_module, init_mod) - self.addfinalizer(func) - - def collect(self) -> Iterable[nodes.Item | nodes.Collector]: - # Always collect __init__.py first. - def sort_key(entry: os.DirEntry[str]) -> object: - return (entry.name != "__init__.py", entry.name) - - config = self.config - col: nodes.Collector | None - cols: Sequence[nodes.Collector] - ihook = self.ihook - for direntry in scandir(self.path, sort_key): - if direntry.is_dir(): - path = Path(direntry.path) - if not self.session.isinitpath(path, with_parents=True): - if ihook.pytest_ignore_collect(collection_path=path, config=config): - continue - col = ihook.pytest_collect_directory(path=path, parent=self) - if col is not None: - yield col - - elif direntry.is_file(): - path = Path(direntry.path) - if not self.session.isinitpath(path): - if ihook.pytest_ignore_collect(collection_path=path, config=config): - continue - cols = ihook.pytest_collect_file(file_path=path, parent=self) - yield from cols - - -def _call_with_optional_argument(func, arg) -> None: - """Call the given function with the given argument if func accepts one argument, otherwise - calls func without arguments.""" - arg_count = func.__code__.co_argcount - if inspect.ismethod(func): - arg_count -= 1 - if arg_count: - func(arg) - else: - func() - - -def _get_first_non_fixture_func(obj: object, names: Iterable[str]) -> object | None: - """Return the attribute from the given object to be used as a setup/teardown - xunit-style function, but only if not marked as a fixture to avoid calling it twice. - """ - for name in names: - meth: object | None = getattr(obj, name, None) - if meth is not None and fixtures.getfixturemarker(meth) is None: - return meth - return None - - -class Class(PyCollector): - """Collector for test methods (and nested classes) in a Python class.""" - - @classmethod - def from_parent(cls, parent, *, name, obj=None, **kw) -> Self: # type: ignore[override] - """The public constructor.""" - return super().from_parent(name=name, parent=parent, **kw) - - def newinstance(self): - return self.obj() - - def collect(self) -> Iterable[nodes.Item | nodes.Collector]: - if not safe_getattr(self.obj, "__test__", True): - return [] - if hasinit(self.obj): - assert self.parent is not None - self.warn( - PytestCollectionWarning( - f"cannot collect test class {self.obj.__name__!r} because it has a " - f"__init__ constructor (from: {self.parent.nodeid})" - ) - ) - return [] - elif hasnew(self.obj): - assert self.parent is not None - self.warn( - PytestCollectionWarning( - f"cannot collect test class {self.obj.__name__!r} because it has a " - f"__new__ constructor (from: {self.parent.nodeid})" - ) - ) - return [] - - self._register_setup_class_fixture() - self._register_setup_method_fixture() - - self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid) - - return super().collect() - - def _register_setup_class_fixture(self) -> None: - """Register an autouse, class scoped fixture into the collected class object - that invokes setup_class/teardown_class if either or both are available. - - Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with - other fixtures (#517). - """ - setup_class = _get_first_non_fixture_func(self.obj, ("setup_class",)) - teardown_class = _get_first_non_fixture_func(self.obj, ("teardown_class",)) - if setup_class is None and teardown_class is None: - return - - def xunit_setup_class_fixture(request) -> Generator[None]: - cls = request.cls - if setup_class is not None: - func = getimfunc(setup_class) - _call_with_optional_argument(func, cls) - yield - if teardown_class is not None: - func = getimfunc(teardown_class) - _call_with_optional_argument(func, cls) - - self.session._fixturemanager._register_fixture( - # Use a unique name to speed up lookup. - name=f"_xunit_setup_class_fixture_{self.obj.__qualname__}", - func=xunit_setup_class_fixture, - nodeid=self.nodeid, - scope="class", - autouse=True, - ) - - def _register_setup_method_fixture(self) -> None: - """Register an autouse, function scoped fixture into the collected class object - that invokes setup_method/teardown_method if either or both are available. - - Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with - other fixtures (#517). - """ - setup_name = "setup_method" - setup_method = _get_first_non_fixture_func(self.obj, (setup_name,)) - teardown_name = "teardown_method" - teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,)) - if setup_method is None and teardown_method is None: - return - - def xunit_setup_method_fixture(request) -> Generator[None]: - instance = request.instance - method = request.function - if setup_method is not None: - func = getattr(instance, setup_name) - _call_with_optional_argument(func, method) - yield - if teardown_method is not None: - func = getattr(instance, teardown_name) - _call_with_optional_argument(func, method) - - self.session._fixturemanager._register_fixture( - # Use a unique name to speed up lookup. - name=f"_xunit_setup_method_fixture_{self.obj.__qualname__}", - func=xunit_setup_method_fixture, - nodeid=self.nodeid, - scope="function", - autouse=True, - ) - - -def hasinit(obj: object) -> bool: - init: object = getattr(obj, "__init__", None) - if init: - return init != object.__init__ - return False - - -def hasnew(obj: object) -> bool: - new: object = getattr(obj, "__new__", None) - if new: - return new != object.__new__ - return False - - -@final -@dataclasses.dataclass(frozen=True) -class IdMaker: - """Make IDs for a parametrization.""" - - __slots__ = ( - "argnames", - "parametersets", - "idfn", - "ids", - "config", - "nodeid", - "func_name", - ) - - # The argnames of the parametrization. - argnames: Sequence[str] - # The ParameterSets of the parametrization. - parametersets: Sequence[ParameterSet] - # Optionally, a user-provided callable to make IDs for parameters in a - # ParameterSet. - idfn: Callable[[Any], object | None] | None - # Optionally, explicit IDs for ParameterSets by index. - ids: Sequence[object | None] | None - # Optionally, the pytest config. - # Used for controlling ASCII escaping, and for calling the - # :hook:`pytest_make_parametrize_id` hook. - config: Config | None - # Optionally, the ID of the node being parametrized. - # Used only for clearer error messages. - nodeid: str | None - # Optionally, the ID of the function being parametrized. - # Used only for clearer error messages. - func_name: str | None - - def make_unique_parameterset_ids(self) -> list[str]: - """Make a unique identifier for each ParameterSet, that may be used to - identify the parametrization in a node ID. - - Format is -...-[counter], where prm_x_token is - - user-provided id, if given - - else an id derived from the value, applicable for certain types - - else - The counter suffix is appended only in case a string wouldn't be unique - otherwise. - """ - resolved_ids = list(self._resolve_ids()) - # All IDs must be unique! - if len(resolved_ids) != len(set(resolved_ids)): - # Record the number of occurrences of each ID. - id_counts = Counter(resolved_ids) - # Map the ID to its next suffix. - id_suffixes: dict[str, int] = defaultdict(int) - # Suffix non-unique IDs to make them unique. - for index, id in enumerate(resolved_ids): - if id_counts[id] > 1: - suffix = "" - if id and id[-1].isdigit(): - suffix = "_" - new_id = f"{id}{suffix}{id_suffixes[id]}" - while new_id in set(resolved_ids): - id_suffixes[id] += 1 - new_id = f"{id}{suffix}{id_suffixes[id]}" - resolved_ids[index] = new_id - id_suffixes[id] += 1 - assert len(resolved_ids) == len( - set(resolved_ids) - ), f"Internal error: {resolved_ids=}" - return resolved_ids - - def _resolve_ids(self) -> Iterable[str]: - """Resolve IDs for all ParameterSets (may contain duplicates).""" - for idx, parameterset in enumerate(self.parametersets): - if parameterset.id is not None: - # ID provided directly - pytest.param(..., id="...") - yield parameterset.id - elif self.ids and idx < len(self.ids) and self.ids[idx] is not None: - # ID provided in the IDs list - parametrize(..., ids=[...]). - yield self._idval_from_value_required(self.ids[idx], idx) - else: - # ID not provided - generate it. - yield "-".join( - self._idval(val, argname, idx) - for val, argname in zip(parameterset.values, self.argnames) - ) - - def _idval(self, val: object, argname: str, idx: int) -> str: - """Make an ID for a parameter in a ParameterSet.""" - idval = self._idval_from_function(val, argname, idx) - if idval is not None: - return idval - idval = self._idval_from_hook(val, argname) - if idval is not None: - return idval - idval = self._idval_from_value(val) - if idval is not None: - return idval - return self._idval_from_argname(argname, idx) - - def _idval_from_function(self, val: object, argname: str, idx: int) -> str | None: - """Try to make an ID for a parameter in a ParameterSet using the - user-provided id callable, if given.""" - if self.idfn is None: - return None - try: - id = self.idfn(val) - except Exception as e: - prefix = f"{self.nodeid}: " if self.nodeid is not None else "" - msg = "error raised while trying to determine id of parameter '{}' at position {}" - msg = prefix + msg.format(argname, idx) - raise ValueError(msg) from e - if id is None: - return None - return self._idval_from_value(id) - - def _idval_from_hook(self, val: object, argname: str) -> str | None: - """Try to make an ID for a parameter in a ParameterSet by calling the - :hook:`pytest_make_parametrize_id` hook.""" - if self.config: - id: str | None = self.config.hook.pytest_make_parametrize_id( - config=self.config, val=val, argname=argname - ) - return id - return None - - def _idval_from_value(self, val: object) -> str | None: - """Try to make an ID for a parameter in a ParameterSet from its value, - if the value type is supported.""" - if isinstance(val, (str, bytes)): - return _ascii_escaped_by_config(val, self.config) - elif val is None or isinstance(val, (float, int, bool, complex)): - return str(val) - elif isinstance(val, Pattern): - return ascii_escaped(val.pattern) - elif val is NOTSET: - # Fallback to default. Note that NOTSET is an enum.Enum. - pass - elif isinstance(val, enum.Enum): - return str(val) - elif isinstance(getattr(val, "__name__", None), str): - # Name of a class, function, module, etc. - name: str = getattr(val, "__name__") - return name - return None - - def _idval_from_value_required(self, val: object, idx: int) -> str: - """Like _idval_from_value(), but fails if the type is not supported.""" - id = self._idval_from_value(val) - if id is not None: - return id - - # Fail. - if self.func_name is not None: - prefix = f"In {self.func_name}: " - elif self.nodeid is not None: - prefix = f"In {self.nodeid}: " - else: - prefix = "" - msg = ( - f"{prefix}ids contains unsupported value {saferepr(val)} (type: {type(val)!r}) at index {idx}. " - "Supported types are: str, bytes, int, float, complex, bool, enum, regex or anything with a __name__." - ) - fail(msg, pytrace=False) - - @staticmethod - def _idval_from_argname(argname: str, idx: int) -> str: - """Make an ID for a parameter in a ParameterSet from the argument name - and the index of the ParameterSet.""" - return str(argname) + str(idx) - - -@final -@dataclasses.dataclass(frozen=True) -class CallSpec2: - """A planned parameterized invocation of a test function. - - Calculated during collection for a given test function's Metafunc. - Once collection is over, each callspec is turned into a single Item - and stored in item.callspec. - """ - - # arg name -> arg value which will be passed to a fixture or pseudo-fixture - # of the same name. (indirect or direct parametrization respectively) - params: dict[str, object] = dataclasses.field(default_factory=dict) - # arg name -> arg index. - indices: dict[str, int] = dataclasses.field(default_factory=dict) - # Used for sorting parametrized resources. - _arg2scope: Mapping[str, Scope] = dataclasses.field(default_factory=dict) - # Parts which will be added to the item's name in `[..]` separated by "-". - _idlist: Sequence[str] = dataclasses.field(default_factory=tuple) - # Marks which will be applied to the item. - marks: list[Mark] = dataclasses.field(default_factory=list) - - def setmulti( - self, - *, - argnames: Iterable[str], - valset: Iterable[object], - id: str, - marks: Iterable[Mark | MarkDecorator], - scope: Scope, - param_index: int, - ) -> CallSpec2: - params = self.params.copy() - indices = self.indices.copy() - arg2scope = dict(self._arg2scope) - for arg, val in zip(argnames, valset): - if arg in params: - raise ValueError(f"duplicate parametrization of {arg!r}") - params[arg] = val - indices[arg] = param_index - arg2scope[arg] = scope - return CallSpec2( - params=params, - indices=indices, - _arg2scope=arg2scope, - _idlist=[*self._idlist, id], - marks=[*self.marks, *normalize_mark_list(marks)], - ) - - def getparam(self, name: str) -> object: - try: - return self.params[name] - except KeyError as e: - raise ValueError(name) from e - - @property - def id(self) -> str: - return "-".join(self._idlist) - - -def get_direct_param_fixture_func(request: FixtureRequest) -> Any: - return request.param - - -# Used for storing pseudo fixturedefs for direct parametrization. -name2pseudofixturedef_key = StashKey[Dict[str, FixtureDef[Any]]]() - - -@final -class Metafunc: - """Objects passed to the :hook:`pytest_generate_tests` hook. - - They help to inspect a test function and to generate tests according to - test configuration or values specified in the class or module where a - test function is defined. - """ - - def __init__( - self, - definition: FunctionDefinition, - fixtureinfo: fixtures.FuncFixtureInfo, - config: Config, - cls=None, - module=None, - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - - #: Access to the underlying :class:`_pytest.python.FunctionDefinition`. - self.definition = definition - - #: Access to the :class:`pytest.Config` object for the test session. - self.config = config - - #: The module object where the test function is defined in. - self.module = module - - #: Underlying Python test function. - self.function = definition.obj - - #: Set of fixture names required by the test function. - self.fixturenames = fixtureinfo.names_closure - - #: Class object where the test function is defined in or ``None``. - self.cls = cls - - self._arg2fixturedefs = fixtureinfo.name2fixturedefs - - # Result of parametrize(). - self._calls: list[CallSpec2] = [] - - def parametrize( - self, - argnames: str | Sequence[str], - argvalues: Iterable[ParameterSet | Sequence[object] | object], - indirect: bool | Sequence[str] = False, - ids: Iterable[object | None] | Callable[[Any], object | None] | None = None, - scope: _ScopeName | None = None, - *, - _param_mark: Mark | None = None, - ) -> None: - """Add new invocations to the underlying test function using the list - of argvalues for the given argnames. Parametrization is performed - during the collection phase. If you need to setup expensive resources - see about setting indirect to do it rather than at test setup time. - - Can be called multiple times per test function (but only on different - argument names), in which case each call parametrizes all previous - parametrizations, e.g. - - :: - - unparametrized: t - parametrize ["x", "y"]: t[x], t[y] - parametrize [1, 2]: t[x-1], t[x-2], t[y-1], t[y-2] - - :param argnames: - A comma-separated string denoting one or more argument names, or - a list/tuple of argument strings. - - :param argvalues: - The list of argvalues determines how often a test is invoked with - different argument values. - - If only one argname was specified argvalues is a list of values. - If N argnames were specified, argvalues must be a list of - N-tuples, where each tuple-element specifies a value for its - respective argname. - :type argvalues: Iterable[_pytest.mark.structures.ParameterSet | Sequence[object] | object] - :param indirect: - A list of arguments' names (subset of argnames) or a boolean. - If True the list contains all names from the argnames. Each - argvalue corresponding to an argname in this list will - be passed as request.param to its respective argname fixture - function so that it can perform more expensive setups during the - setup phase of a test rather than at collection time. - - :param ids: - Sequence of (or generator for) ids for ``argvalues``, - or a callable to return part of the id for each argvalue. - - With sequences (and generators like ``itertools.count()``) the - returned ids should be of type ``string``, ``int``, ``float``, - ``bool``, or ``None``. - They are mapped to the corresponding index in ``argvalues``. - ``None`` means to use the auto-generated id. - - If it is a callable it will be called for each entry in - ``argvalues``, and the return value is used as part of the - auto-generated id for the whole set (where parts are joined with - dashes ("-")). - This is useful to provide more specific ids for certain items, e.g. - dates. Returning ``None`` will use an auto-generated id. - - If no ids are provided they will be generated automatically from - the argvalues. - - :param scope: - If specified it denotes the scope of the parameters. - The scope is used for grouping tests by parameter instances. - It will also override any fixture-function defined scope, allowing - to set a dynamic scope using test context or configuration. - """ - argnames, parametersets = ParameterSet._for_parametrize( - argnames, - argvalues, - self.function, - self.config, - nodeid=self.definition.nodeid, - ) - del argvalues - - if "request" in argnames: - fail( - "'request' is a reserved name and cannot be used in @pytest.mark.parametrize", - pytrace=False, - ) - - if scope is not None: - scope_ = Scope.from_user( - scope, descr=f"parametrize() call in {self.function.__name__}" - ) - else: - scope_ = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) - - self._validate_if_using_arg_names(argnames, indirect) - - # Use any already (possibly) generated ids with parametrize Marks. - if _param_mark and _param_mark._param_ids_from: - generated_ids = _param_mark._param_ids_from._param_ids_generated - if generated_ids is not None: - ids = generated_ids - - ids = self._resolve_parameter_set_ids( - argnames, ids, parametersets, nodeid=self.definition.nodeid - ) - - # Store used (possibly generated) ids with parametrize Marks. - if _param_mark and _param_mark._param_ids_from and generated_ids is None: - object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) - - # Add funcargs as fixturedefs to fixtureinfo.arg2fixturedefs by registering - # artificial "pseudo" FixtureDef's so that later at test execution time we can - # rely on a proper FixtureDef to exist for fixture setup. - node = None - # If we have a scope that is higher than function, we need - # to make sure we only ever create an according fixturedef on - # a per-scope basis. We thus store and cache the fixturedef on the - # node related to the scope. - if scope_ is not Scope.Function: - collector = self.definition.parent - assert collector is not None - node = get_scope_node(collector, scope_) - if node is None: - # If used class scope and there is no class, use module-level - # collector (for now). - if scope_ is Scope.Class: - assert isinstance(collector, Module) - node = collector - # If used package scope and there is no package, use session - # (for now). - elif scope_ is Scope.Package: - node = collector.session - else: - assert False, f"Unhandled missing scope: {scope}" - if node is None: - name2pseudofixturedef = None - else: - default: dict[str, FixtureDef[Any]] = {} - name2pseudofixturedef = node.stash.setdefault( - name2pseudofixturedef_key, default - ) - arg_directness = self._resolve_args_directness(argnames, indirect) - for argname in argnames: - if arg_directness[argname] == "indirect": - continue - if name2pseudofixturedef is not None and argname in name2pseudofixturedef: - fixturedef = name2pseudofixturedef[argname] - else: - fixturedef = FixtureDef( - config=self.config, - baseid="", - argname=argname, - func=get_direct_param_fixture_func, - scope=scope_, - params=None, - ids=None, - _ispytest=True, - ) - if name2pseudofixturedef is not None: - name2pseudofixturedef[argname] = fixturedef - self._arg2fixturedefs[argname] = [fixturedef] - - # Create the new calls: if we are parametrize() multiple times (by applying the decorator - # more than once) then we accumulate those calls generating the cartesian product - # of all calls. - newcalls = [] - for callspec in self._calls or [CallSpec2()]: - for param_index, (param_id, param_set) in enumerate( - zip(ids, parametersets) - ): - newcallspec = callspec.setmulti( - argnames=argnames, - valset=param_set.values, - id=param_id, - marks=param_set.marks, - scope=scope_, - param_index=param_index, - ) - newcalls.append(newcallspec) - self._calls = newcalls - - def _resolve_parameter_set_ids( - self, - argnames: Sequence[str], - ids: Iterable[object | None] | Callable[[Any], object | None] | None, - parametersets: Sequence[ParameterSet], - nodeid: str, - ) -> list[str]: - """Resolve the actual ids for the given parameter sets. - - :param argnames: - Argument names passed to ``parametrize()``. - :param ids: - The `ids` parameter of the ``parametrize()`` call (see docs). - :param parametersets: - The parameter sets, each containing a set of values corresponding - to ``argnames``. - :param nodeid str: - The nodeid of the definition item that generated this - parametrization. - :returns: - List with ids for each parameter set given. - """ - if ids is None: - idfn = None - ids_ = None - elif callable(ids): - idfn = ids - ids_ = None - else: - idfn = None - ids_ = self._validate_ids(ids, parametersets, self.function.__name__) - id_maker = IdMaker( - argnames, - parametersets, - idfn, - ids_, - self.config, - nodeid=nodeid, - func_name=self.function.__name__, - ) - return id_maker.make_unique_parameterset_ids() - - def _validate_ids( - self, - ids: Iterable[object | None], - parametersets: Sequence[ParameterSet], - func_name: str, - ) -> list[object | None]: - try: - num_ids = len(ids) # type: ignore[arg-type] - except TypeError: - try: - iter(ids) - except TypeError as e: - raise TypeError("ids must be a callable or an iterable") from e - num_ids = len(parametersets) - - # num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849 - if num_ids != len(parametersets) and num_ids != 0: - msg = "In {}: {} parameter sets specified, with different number of ids: {}" - fail(msg.format(func_name, len(parametersets), num_ids), pytrace=False) - - return list(itertools.islice(ids, num_ids)) - - def _resolve_args_directness( - self, - argnames: Sequence[str], - indirect: bool | Sequence[str], - ) -> dict[str, Literal["indirect", "direct"]]: - """Resolve if each parametrized argument must be considered an indirect - parameter to a fixture of the same name, or a direct parameter to the - parametrized function, based on the ``indirect`` parameter of the - parametrized() call. - - :param argnames: - List of argument names passed to ``parametrize()``. - :param indirect: - Same as the ``indirect`` parameter of ``parametrize()``. - :returns - A dict mapping each arg name to either "indirect" or "direct". - """ - arg_directness: dict[str, Literal["indirect", "direct"]] - if isinstance(indirect, bool): - arg_directness = dict.fromkeys( - argnames, "indirect" if indirect else "direct" - ) - elif isinstance(indirect, Sequence): - arg_directness = dict.fromkeys(argnames, "direct") - for arg in indirect: - if arg not in argnames: - fail( - f"In {self.function.__name__}: indirect fixture '{arg}' doesn't exist", - pytrace=False, - ) - arg_directness[arg] = "indirect" - else: - fail( - f"In {self.function.__name__}: expected Sequence or boolean" - f" for indirect, got {type(indirect).__name__}", - pytrace=False, - ) - return arg_directness - - def _validate_if_using_arg_names( - self, - argnames: Sequence[str], - indirect: bool | Sequence[str], - ) -> None: - """Check if all argnames are being used, by default values, or directly/indirectly. - - :param List[str] argnames: List of argument names passed to ``parametrize()``. - :param indirect: Same as the ``indirect`` parameter of ``parametrize()``. - :raises ValueError: If validation fails. - """ - default_arg_names = set(get_default_arg_names(self.function)) - func_name = self.function.__name__ - for arg in argnames: - if arg not in self.fixturenames: - if arg in default_arg_names: - fail( - f"In {func_name}: function already takes an argument '{arg}' with a default value", - pytrace=False, - ) - else: - if isinstance(indirect, Sequence): - name = "fixture" if arg in indirect else "argument" - else: - name = "fixture" if indirect else "argument" - fail( - f"In {func_name}: function uses no {name} '{arg}'", - pytrace=False, - ) - - -def _find_parametrized_scope( - argnames: Sequence[str], - arg2fixturedefs: Mapping[str, Sequence[fixtures.FixtureDef[object]]], - indirect: bool | Sequence[str], -) -> Scope: - """Find the most appropriate scope for a parametrized call based on its arguments. - - When there's at least one direct argument, always use "function" scope. - - When a test function is parametrized and all its arguments are indirect - (e.g. fixtures), return the most narrow scope based on the fixtures used. - - Related to issue #1832, based on code posted by @Kingdread. - """ - if isinstance(indirect, Sequence): - all_arguments_are_fixtures = len(indirect) == len(argnames) - else: - all_arguments_are_fixtures = bool(indirect) - - if all_arguments_are_fixtures: - fixturedefs = arg2fixturedefs or {} - used_scopes = [ - fixturedef[-1]._scope - for name, fixturedef in fixturedefs.items() - if name in argnames - ] - # Takes the most narrow scope from used fixtures. - return min(used_scopes, default=Scope.Function) - - return Scope.Function - - -def _ascii_escaped_by_config(val: str | bytes, config: Config | None) -> str: - if config is None: - escape_option = False - else: - escape_option = config.getini( - "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" - ) - # TODO: If escaping is turned off and the user passes bytes, - # will return a bytes. For now we ignore this but the - # code *probably* doesn't handle this case. - return val if escape_option else ascii_escaped(val) # type: ignore - - -class Function(PyobjMixin, nodes.Item): - """Item responsible for setting up and executing a Python test function. - - :param name: - The full function name, including any decorations like those - added by parametrization (``my_func[my_param]``). - :param parent: - The parent Node. - :param config: - The pytest Config object. - :param callspec: - If given, this function has been parametrized and the callspec contains - meta information about the parametrization. - :param callobj: - If given, the object which will be called when the Function is invoked, - otherwise the callobj will be obtained from ``parent`` using ``originalname``. - :param keywords: - Keywords bound to the function object for "-k" matching. - :param session: - The pytest Session object. - :param fixtureinfo: - Fixture information already resolved at this fixture node.. - :param originalname: - The attribute name to use for accessing the underlying function object. - Defaults to ``name``. Set this if name is different from the original name, - for example when it contains decorations like those added by parametrization - (``my_func[my_param]``). - """ - - # Disable since functions handle it themselves. - _ALLOW_MARKERS = False - - def __init__( - self, - name: str, - parent, - config: Config | None = None, - callspec: CallSpec2 | None = None, - callobj=NOTSET, - keywords: Mapping[str, Any] | None = None, - session: Session | None = None, - fixtureinfo: FuncFixtureInfo | None = None, - originalname: str | None = None, - ) -> None: - super().__init__(name, parent, config=config, session=session) - - if callobj is not NOTSET: - self._obj = callobj - self._instance = getattr(callobj, "__self__", None) - - #: Original function name, without any decorations (for example - #: parametrization adds a ``"[...]"`` suffix to function names), used to access - #: the underlying function object from ``parent`` (in case ``callobj`` is not given - #: explicitly). - #: - #: .. versionadded:: 3.0 - self.originalname = originalname or name - - # Note: when FunctionDefinition is introduced, we should change ``originalname`` - # to a readonly property that returns FunctionDefinition.name. - - self.own_markers.extend(get_unpacked_marks(self.obj)) - if callspec: - self.callspec = callspec - self.own_markers.extend(callspec.marks) - - # todo: this is a hell of a hack - # https://github.com/pytest-dev/pytest/issues/4569 - # Note: the order of the updates is important here; indicates what - # takes priority (ctor argument over function attributes over markers). - # Take own_markers only; NodeKeywords handles parent traversal on its own. - self.keywords.update((mark.name, mark) for mark in self.own_markers) - self.keywords.update(self.obj.__dict__) - if keywords: - self.keywords.update(keywords) - - if fixtureinfo is None: - fm = self.session._fixturemanager - fixtureinfo = fm.getfixtureinfo(self, self.obj, self.cls) - self._fixtureinfo: FuncFixtureInfo = fixtureinfo - self.fixturenames = fixtureinfo.names_closure - self._initrequest() - - # todo: determine sound type limitations - @classmethod - def from_parent(cls, parent, **kw) -> Self: - """The public constructor.""" - return super().from_parent(parent=parent, **kw) - - def _initrequest(self) -> None: - self.funcargs: dict[str, object] = {} - self._request = fixtures.TopRequest(self, _ispytest=True) - - @property - def function(self): - """Underlying python 'function' object.""" - return getimfunc(self.obj) - - @property - def instance(self): - try: - return self._instance - except AttributeError: - if isinstance(self.parent, Class): - # Each Function gets a fresh class instance. - self._instance = self._getinstance() - else: - self._instance = None - return self._instance - - def _getinstance(self): - if isinstance(self.parent, Class): - # Each Function gets a fresh class instance. - return self.parent.newinstance() - else: - return None - - def _getobj(self): - instance = self.instance - if instance is not None: - parent_obj = instance - else: - assert self.parent is not None - parent_obj = self.parent.obj # type: ignore[attr-defined] - return getattr(parent_obj, self.originalname) - - @property - def _pyfuncitem(self): - """(compatonly) for code expecting pytest-2.2 style request objects.""" - return self - - def runtest(self) -> None: - """Execute the underlying test function.""" - self.ihook.pytest_pyfunc_call(pyfuncitem=self) - - def setup(self) -> None: - self._request._fillfixtures() - - def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: - if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False): - code = _pytest._code.Code.from_function(get_real_func(self.obj)) - path, firstlineno = code.path, code.firstlineno - traceback = excinfo.traceback - ntraceback = traceback.cut(path=path, firstlineno=firstlineno) - if ntraceback == traceback: - ntraceback = ntraceback.cut(path=path) - if ntraceback == traceback: - ntraceback = ntraceback.filter(filter_traceback) - if not ntraceback: - ntraceback = traceback - ntraceback = ntraceback.filter(excinfo) - - # issue364: mark all but first and last frames to - # only show a single-line message for each frame. - if self.config.getoption("tbstyle", "auto") == "auto": - if len(ntraceback) > 2: - ntraceback = Traceback( - ( - ntraceback[0], - *(t.with_repr_style("short") for t in ntraceback[1:-1]), - ntraceback[-1], - ) - ) - - return ntraceback - return excinfo.traceback - - # TODO: Type ignored -- breaks Liskov Substitution. - def repr_failure( # type: ignore[override] - self, - excinfo: ExceptionInfo[BaseException], - ) -> str | TerminalRepr: - style = self.config.getoption("tbstyle", "auto") - if style == "auto": - style = "long" - return self._repr_failure_py(excinfo, style=style) - - -class FunctionDefinition(Function): - """This class is a stop gap solution until we evolve to have actual function - definition nodes and manage to get rid of ``metafunc``.""" - - def runtest(self) -> None: - raise RuntimeError("function definitions are not supposed to be run as tests") - - setup = runtest diff --git a/.venv/lib/python3.12/site-packages/_pytest/python_api.py b/.venv/lib/python3.12/site-packages/_pytest/python_api.py deleted file mode 100644 index f0035f0c..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/python_api.py +++ /dev/null @@ -1,1028 +0,0 @@ -# mypy: allow-untyped-defs -from __future__ import annotations - -from collections.abc import Collection -from collections.abc import Sized -from decimal import Decimal -import math -from numbers import Complex -import pprint -import re -from types import TracebackType -from typing import Any -from typing import Callable -from typing import cast -from typing import ContextManager -from typing import final -from typing import Mapping -from typing import overload -from typing import Pattern -from typing import Sequence -from typing import Tuple -from typing import Type -from typing import TYPE_CHECKING -from typing import TypeVar - -import _pytest._code -from _pytest.outcomes import fail - - -if TYPE_CHECKING: - from numpy import ndarray - - -def _compare_approx( - full_object: object, - message_data: Sequence[tuple[str, str, str]], - number_of_elements: int, - different_ids: Sequence[object], - max_abs_diff: float, - max_rel_diff: float, -) -> list[str]: - message_list = list(message_data) - message_list.insert(0, ("Index", "Obtained", "Expected")) - max_sizes = [0, 0, 0] - for index, obtained, expected in message_list: - max_sizes[0] = max(max_sizes[0], len(index)) - max_sizes[1] = max(max_sizes[1], len(obtained)) - max_sizes[2] = max(max_sizes[2], len(expected)) - explanation = [ - f"comparison failed. Mismatched elements: {len(different_ids)} / {number_of_elements}:", - f"Max absolute difference: {max_abs_diff}", - f"Max relative difference: {max_rel_diff}", - ] + [ - f"{indexes:<{max_sizes[0]}} | {obtained:<{max_sizes[1]}} | {expected:<{max_sizes[2]}}" - for indexes, obtained, expected in message_list - ] - return explanation - - -# builtin pytest.approx helper - - -class ApproxBase: - """Provide shared utilities for making approximate comparisons between - numbers or sequences of numbers.""" - - # Tell numpy to use our `__eq__` operator instead of its. - __array_ufunc__ = None - __array_priority__ = 100 - - def __init__(self, expected, rel=None, abs=None, nan_ok: bool = False) -> None: - __tracebackhide__ = True - self.expected = expected - self.abs = abs - self.rel = rel - self.nan_ok = nan_ok - self._check_type() - - def __repr__(self) -> str: - raise NotImplementedError - - def _repr_compare(self, other_side: Any) -> list[str]: - return [ - "comparison failed", - f"Obtained: {other_side}", - f"Expected: {self}", - ] - - def __eq__(self, actual) -> bool: - return all( - a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual) - ) - - def __bool__(self): - __tracebackhide__ = True - raise AssertionError( - "approx() is not supported in a boolean context.\nDid you mean: `assert a == approx(b)`?" - ) - - # Ignore type because of https://github.com/python/mypy/issues/4266. - __hash__ = None # type: ignore - - def __ne__(self, actual) -> bool: - return not (actual == self) - - def _approx_scalar(self, x) -> ApproxScalar: - if isinstance(x, Decimal): - return ApproxDecimal(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) - return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) - - def _yield_comparisons(self, actual): - """Yield all the pairs of numbers to be compared. - - This is used to implement the `__eq__` method. - """ - raise NotImplementedError - - def _check_type(self) -> None: - """Raise a TypeError if the expected value is not a valid type.""" - # This is only a concern if the expected value is a sequence. In every - # other case, the approx() function ensures that the expected value has - # a numeric type. For this reason, the default is to do nothing. The - # classes that deal with sequences should reimplement this method to - # raise if there are any non-numeric elements in the sequence. - - -def _recursive_sequence_map(f, x): - """Recursively map a function over a sequence of arbitrary depth""" - if isinstance(x, (list, tuple)): - seq_type = type(x) - return seq_type(_recursive_sequence_map(f, xi) for xi in x) - elif _is_sequence_like(x): - return [_recursive_sequence_map(f, xi) for xi in x] - else: - return f(x) - - -class ApproxNumpy(ApproxBase): - """Perform approximate comparisons where the expected value is numpy array.""" - - def __repr__(self) -> str: - list_scalars = _recursive_sequence_map( - self._approx_scalar, self.expected.tolist() - ) - return f"approx({list_scalars!r})" - - def _repr_compare(self, other_side: ndarray | list[Any]) -> list[str]: - import itertools - import math - - def get_value_from_nested_list( - nested_list: list[Any], nd_index: tuple[Any, ...] - ) -> Any: - """ - Helper function to get the value out of a nested list, given an n-dimensional index. - This mimics numpy's indexing, but for raw nested python lists. - """ - value: Any = nested_list - for i in nd_index: - value = value[i] - return value - - np_array_shape = self.expected.shape - approx_side_as_seq = _recursive_sequence_map( - self._approx_scalar, self.expected.tolist() - ) - - # convert other_side to numpy array to ensure shape attribute is available - other_side_as_array = _as_numpy_array(other_side) - assert other_side_as_array is not None - - if np_array_shape != other_side_as_array.shape: - return [ - "Impossible to compare arrays with different shapes.", - f"Shapes: {np_array_shape} and {other_side_as_array.shape}", - ] - - number_of_elements = self.expected.size - max_abs_diff = -math.inf - max_rel_diff = -math.inf - different_ids = [] - for index in itertools.product(*(range(i) for i in np_array_shape)): - approx_value = get_value_from_nested_list(approx_side_as_seq, index) - other_value = get_value_from_nested_list(other_side_as_array, index) - if approx_value != other_value: - abs_diff = abs(approx_value.expected - other_value) - max_abs_diff = max(max_abs_diff, abs_diff) - if other_value == 0.0: - max_rel_diff = math.inf - else: - max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value)) - different_ids.append(index) - - message_data = [ - ( - str(index), - str(get_value_from_nested_list(other_side_as_array, index)), - str(get_value_from_nested_list(approx_side_as_seq, index)), - ) - for index in different_ids - ] - return _compare_approx( - self.expected, - message_data, - number_of_elements, - different_ids, - max_abs_diff, - max_rel_diff, - ) - - def __eq__(self, actual) -> bool: - import numpy as np - - # self.expected is supposed to always be an array here. - - if not np.isscalar(actual): - try: - actual = np.asarray(actual) - except Exception as e: - raise TypeError(f"cannot compare '{actual}' to numpy.ndarray") from e - - if not np.isscalar(actual) and actual.shape != self.expected.shape: - return False - - return super().__eq__(actual) - - def _yield_comparisons(self, actual): - import numpy as np - - # `actual` can either be a numpy array or a scalar, it is treated in - # `__eq__` before being passed to `ApproxBase.__eq__`, which is the - # only method that calls this one. - - if np.isscalar(actual): - for i in np.ndindex(self.expected.shape): - yield actual, self.expected[i].item() - else: - for i in np.ndindex(self.expected.shape): - yield actual[i].item(), self.expected[i].item() - - -class ApproxMapping(ApproxBase): - """Perform approximate comparisons where the expected value is a mapping - with numeric values (the keys can be anything).""" - - def __repr__(self) -> str: - return f"approx({({k: self._approx_scalar(v) for k, v in self.expected.items()})!r})" - - def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]: - import math - - approx_side_as_map = { - k: self._approx_scalar(v) for k, v in self.expected.items() - } - - number_of_elements = len(approx_side_as_map) - max_abs_diff = -math.inf - max_rel_diff = -math.inf - different_ids = [] - for (approx_key, approx_value), other_value in zip( - approx_side_as_map.items(), other_side.values() - ): - if approx_value != other_value: - if approx_value.expected is not None and other_value is not None: - try: - max_abs_diff = max( - max_abs_diff, abs(approx_value.expected - other_value) - ) - if approx_value.expected == 0.0: - max_rel_diff = math.inf - else: - max_rel_diff = max( - max_rel_diff, - abs( - (approx_value.expected - other_value) - / approx_value.expected - ), - ) - except ZeroDivisionError: - pass - different_ids.append(approx_key) - - message_data = [ - (str(key), str(other_side[key]), str(approx_side_as_map[key])) - for key in different_ids - ] - - return _compare_approx( - self.expected, - message_data, - number_of_elements, - different_ids, - max_abs_diff, - max_rel_diff, - ) - - def __eq__(self, actual) -> bool: - try: - if set(actual.keys()) != set(self.expected.keys()): - return False - except AttributeError: - return False - - return super().__eq__(actual) - - def _yield_comparisons(self, actual): - for k in self.expected.keys(): - yield actual[k], self.expected[k] - - def _check_type(self) -> None: - __tracebackhide__ = True - for key, value in self.expected.items(): - if isinstance(value, type(self.expected)): - msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}" - raise TypeError(msg.format(key, value, pprint.pformat(self.expected))) - - -class ApproxSequenceLike(ApproxBase): - """Perform approximate comparisons where the expected value is a sequence of numbers.""" - - def __repr__(self) -> str: - seq_type = type(self.expected) - if seq_type not in (tuple, list): - seq_type = list - return f"approx({seq_type(self._approx_scalar(x) for x in self.expected)!r})" - - def _repr_compare(self, other_side: Sequence[float]) -> list[str]: - import math - - if len(self.expected) != len(other_side): - return [ - "Impossible to compare lists with different sizes.", - f"Lengths: {len(self.expected)} and {len(other_side)}", - ] - - approx_side_as_map = _recursive_sequence_map(self._approx_scalar, self.expected) - - number_of_elements = len(approx_side_as_map) - max_abs_diff = -math.inf - max_rel_diff = -math.inf - different_ids = [] - for i, (approx_value, other_value) in enumerate( - zip(approx_side_as_map, other_side) - ): - if approx_value != other_value: - abs_diff = abs(approx_value.expected - other_value) - max_abs_diff = max(max_abs_diff, abs_diff) - if other_value == 0.0: - max_rel_diff = math.inf - else: - max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value)) - different_ids.append(i) - - message_data = [ - (str(i), str(other_side[i]), str(approx_side_as_map[i])) - for i in different_ids - ] - - return _compare_approx( - self.expected, - message_data, - number_of_elements, - different_ids, - max_abs_diff, - max_rel_diff, - ) - - def __eq__(self, actual) -> bool: - try: - if len(actual) != len(self.expected): - return False - except TypeError: - return False - return super().__eq__(actual) - - def _yield_comparisons(self, actual): - return zip(actual, self.expected) - - def _check_type(self) -> None: - __tracebackhide__ = True - for index, x in enumerate(self.expected): - if isinstance(x, type(self.expected)): - msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}" - raise TypeError(msg.format(x, index, pprint.pformat(self.expected))) - - -class ApproxScalar(ApproxBase): - """Perform approximate comparisons where the expected value is a single number.""" - - # Using Real should be better than this Union, but not possible yet: - # https://github.com/python/typeshed/pull/3108 - DEFAULT_ABSOLUTE_TOLERANCE: float | Decimal = 1e-12 - DEFAULT_RELATIVE_TOLERANCE: float | Decimal = 1e-6 - - def __repr__(self) -> str: - """Return a string communicating both the expected value and the - tolerance for the comparison being made. - - For example, ``1.0 ± 1e-6``, ``(3+4j) ± 5e-6 ∠ ±180°``. - """ - # Don't show a tolerance for values that aren't compared using - # tolerances, i.e. non-numerics and infinities. Need to call abs to - # handle complex numbers, e.g. (inf + 1j). - if ( - isinstance(self.expected, bool) - or (not isinstance(self.expected, (Complex, Decimal))) - or math.isinf(abs(self.expected) or isinstance(self.expected, bool)) - ): - return str(self.expected) - - # If a sensible tolerance can't be calculated, self.tolerance will - # raise a ValueError. In this case, display '???'. - try: - vetted_tolerance = f"{self.tolerance:.1e}" - if ( - isinstance(self.expected, Complex) - and self.expected.imag - and not math.isinf(self.tolerance) - ): - vetted_tolerance += " ∠ ±180°" - except ValueError: - vetted_tolerance = "???" - - return f"{self.expected} ± {vetted_tolerance}" - - def __eq__(self, actual) -> bool: - """Return whether the given value is equal to the expected value - within the pre-specified tolerance.""" - asarray = _as_numpy_array(actual) - if asarray is not None: - # Call ``__eq__()`` manually to prevent infinite-recursion with - # numpy<1.13. See #3748. - return all(self.__eq__(a) for a in asarray.flat) - - # Short-circuit exact equality, except for bool - if isinstance(self.expected, bool) and not isinstance(actual, bool): - return False - elif actual == self.expected: - return True - - # If either type is non-numeric, fall back to strict equality. - # NB: we need Complex, rather than just Number, to ensure that __abs__, - # __sub__, and __float__ are defined. Also, consider bool to be - # nonnumeric, even though it has the required arithmetic. - if isinstance(self.expected, bool) or not ( - isinstance(self.expected, (Complex, Decimal)) - and isinstance(actual, (Complex, Decimal)) - ): - return False - - # Allow the user to control whether NaNs are considered equal to each - # other or not. The abs() calls are for compatibility with complex - # numbers. - if math.isnan(abs(self.expected)): - return self.nan_ok and math.isnan(abs(actual)) - - # Infinity shouldn't be approximately equal to anything but itself, but - # if there's a relative tolerance, it will be infinite and infinity - # will seem approximately equal to everything. The equal-to-itself - # case would have been short circuited above, so here we can just - # return false if the expected value is infinite. The abs() call is - # for compatibility with complex numbers. - if math.isinf(abs(self.expected)): - return False - - # Return true if the two numbers are within the tolerance. - result: bool = abs(self.expected - actual) <= self.tolerance - return result - - # Ignore type because of https://github.com/python/mypy/issues/4266. - __hash__ = None # type: ignore - - @property - def tolerance(self): - """Return the tolerance for the comparison. - - This could be either an absolute tolerance or a relative tolerance, - depending on what the user specified or which would be larger. - """ - - def set_default(x, default): - return x if x is not None else default - - # Figure out what the absolute tolerance should be. ``self.abs`` is - # either None or a value specified by the user. - absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE) - - if absolute_tolerance < 0: - raise ValueError( - f"absolute tolerance can't be negative: {absolute_tolerance}" - ) - if math.isnan(absolute_tolerance): - raise ValueError("absolute tolerance can't be NaN.") - - # If the user specified an absolute tolerance but not a relative one, - # just return the absolute tolerance. - if self.rel is None: - if self.abs is not None: - return absolute_tolerance - - # Figure out what the relative tolerance should be. ``self.rel`` is - # either None or a value specified by the user. This is done after - # we've made sure the user didn't ask for an absolute tolerance only, - # because we don't want to raise errors about the relative tolerance if - # we aren't even going to use it. - relative_tolerance = set_default( - self.rel, self.DEFAULT_RELATIVE_TOLERANCE - ) * abs(self.expected) - - if relative_tolerance < 0: - raise ValueError( - f"relative tolerance can't be negative: {relative_tolerance}" - ) - if math.isnan(relative_tolerance): - raise ValueError("relative tolerance can't be NaN.") - - # Return the larger of the relative and absolute tolerances. - return max(relative_tolerance, absolute_tolerance) - - -class ApproxDecimal(ApproxScalar): - """Perform approximate comparisons where the expected value is a Decimal.""" - - DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12") - DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6") - - -def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: - """Assert that two numbers (or two ordered sequences of numbers) are equal to each other - within some tolerance. - - Due to the :doc:`python:tutorial/floatingpoint`, numbers that we - would intuitively expect to be equal are not always so:: - - >>> 0.1 + 0.2 == 0.3 - False - - This problem is commonly encountered when writing tests, e.g. when making - sure that floating-point values are what you expect them to be. One way to - deal with this problem is to assert that two floating-point numbers are - equal to within some appropriate tolerance:: - - >>> abs((0.1 + 0.2) - 0.3) < 1e-6 - True - - However, comparisons like this are tedious to write and difficult to - understand. Furthermore, absolute comparisons like the one above are - usually discouraged because there's no tolerance that works well for all - situations. ``1e-6`` is good for numbers around ``1``, but too small for - very big numbers and too big for very small ones. It's better to express - the tolerance as a fraction of the expected value, but relative comparisons - like that are even more difficult to write correctly and concisely. - - The ``approx`` class performs floating-point comparisons using a syntax - that's as intuitive as possible:: - - >>> from pytest import approx - >>> 0.1 + 0.2 == approx(0.3) - True - - The same syntax also works for ordered sequences of numbers:: - - >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6)) - True - - ``numpy`` arrays:: - - >>> import numpy as np # doctest: +SKIP - >>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP - True - - And for a ``numpy`` array against a scalar:: - - >>> import numpy as np # doctest: +SKIP - >>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3) # doctest: +SKIP - True - - Only ordered sequences are supported, because ``approx`` needs - to infer the relative position of the sequences without ambiguity. This means - ``sets`` and other unordered sequences are not supported. - - Finally, dictionary *values* can also be compared:: - - >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6}) - True - - The comparison will be true if both mappings have the same keys and their - respective values match the expected tolerances. - - **Tolerances** - - By default, ``approx`` considers numbers within a relative tolerance of - ``1e-6`` (i.e. one part in a million) of its expected value to be equal. - This treatment would lead to surprising results if the expected value was - ``0.0``, because nothing but ``0.0`` itself is relatively close to ``0.0``. - To handle this case less surprisingly, ``approx`` also considers numbers - within an absolute tolerance of ``1e-12`` of its expected value to be - equal. Infinity and NaN are special cases. Infinity is only considered - equal to itself, regardless of the relative tolerance. NaN is not - considered equal to anything by default, but you can make it be equal to - itself by setting the ``nan_ok`` argument to True. (This is meant to - facilitate comparing arrays that use NaN to mean "no data".) - - Both the relative and absolute tolerances can be changed by passing - arguments to the ``approx`` constructor:: - - >>> 1.0001 == approx(1) - False - >>> 1.0001 == approx(1, rel=1e-3) - True - >>> 1.0001 == approx(1, abs=1e-3) - True - - If you specify ``abs`` but not ``rel``, the comparison will not consider - the relative tolerance at all. In other words, two numbers that are within - the default relative tolerance of ``1e-6`` will still be considered unequal - if they exceed the specified absolute tolerance. If you specify both - ``abs`` and ``rel``, the numbers will be considered equal if either - tolerance is met:: - - >>> 1 + 1e-8 == approx(1) - True - >>> 1 + 1e-8 == approx(1, abs=1e-12) - False - >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12) - True - - You can also use ``approx`` to compare nonnumeric types, or dicts and - sequences containing nonnumeric types, in which case it falls back to - strict equality. This can be useful for comparing dicts and sequences that - can contain optional values:: - - >>> {"required": 1.0000005, "optional": None} == approx({"required": 1, "optional": None}) - True - >>> [None, 1.0000005] == approx([None,1]) - True - >>> ["foo", 1.0000005] == approx([None,1]) - False - - If you're thinking about using ``approx``, then you might want to know how - it compares to other good ways of comparing floating-point numbers. All of - these algorithms are based on relative and absolute tolerances and should - agree for the most part, but they do have meaningful differences: - - - ``math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)``: True if the relative - tolerance is met w.r.t. either ``a`` or ``b`` or if the absolute - tolerance is met. Because the relative tolerance is calculated w.r.t. - both ``a`` and ``b``, this test is symmetric (i.e. neither ``a`` nor - ``b`` is a "reference value"). You have to specify an absolute tolerance - if you want to compare to ``0.0`` because there is no tolerance by - default. More information: :py:func:`math.isclose`. - - - ``numpy.isclose(a, b, rtol=1e-5, atol=1e-8)``: True if the difference - between ``a`` and ``b`` is less that the sum of the relative tolerance - w.r.t. ``b`` and the absolute tolerance. Because the relative tolerance - is only calculated w.r.t. ``b``, this test is asymmetric and you can - think of ``b`` as the reference value. Support for comparing sequences - is provided by :py:func:`numpy.allclose`. More information: - :std:doc:`numpy:reference/generated/numpy.isclose`. - - - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b`` - are within an absolute tolerance of ``1e-7``. No relative tolerance is - considered , so this function is not appropriate for very large or very - small numbers. Also, it's only available in subclasses of ``unittest.TestCase`` - and it's ugly because it doesn't follow PEP8. More information: - :py:meth:`unittest.TestCase.assertAlmostEqual`. - - - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative - tolerance is met w.r.t. ``b`` or if the absolute tolerance is met. - Because the relative tolerance is only calculated w.r.t. ``b``, this test - is asymmetric and you can think of ``b`` as the reference value. In the - special case that you explicitly specify an absolute tolerance but not a - relative tolerance, only the absolute tolerance is considered. - - .. note:: - - ``approx`` can handle numpy arrays, but we recommend the - specialised test helpers in :std:doc:`numpy:reference/routines.testing` - if you need support for comparisons, NaNs, or ULP-based tolerances. - - To match strings using regex, you can use - `Matches `_ - from the - `re_assert package `_. - - .. warning:: - - .. versionchanged:: 3.2 - - In order to avoid inconsistent behavior, :py:exc:`TypeError` is - raised for ``>``, ``>=``, ``<`` and ``<=`` comparisons. - The example below illustrates the problem:: - - assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10) - assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10) - - In the second example one expects ``approx(0.1).__le__(0.1 + 1e-10)`` - to be called. But instead, ``approx(0.1).__lt__(0.1 + 1e-10)`` is used to - comparison. This is because the call hierarchy of rich comparisons - follows a fixed behavior. More information: :py:meth:`object.__ge__` - - .. versionchanged:: 3.7.1 - ``approx`` raises ``TypeError`` when it encounters a dict value or - sequence element of nonnumeric type. - - .. versionchanged:: 6.1.0 - ``approx`` falls back to strict equality for nonnumeric types instead - of raising ``TypeError``. - """ - # Delegate the comparison to a class that knows how to deal with the type - # of the expected value (e.g. int, float, list, dict, numpy.array, etc). - # - # The primary responsibility of these classes is to implement ``__eq__()`` - # and ``__repr__()``. The former is used to actually check if some - # "actual" value is equivalent to the given expected value within the - # allowed tolerance. The latter is used to show the user the expected - # value and tolerance, in the case that a test failed. - # - # The actual logic for making approximate comparisons can be found in - # ApproxScalar, which is used to compare individual numbers. All of the - # other Approx classes eventually delegate to this class. The ApproxBase - # class provides some convenient methods and overloads, but isn't really - # essential. - - __tracebackhide__ = True - - if isinstance(expected, Decimal): - cls: type[ApproxBase] = ApproxDecimal - elif isinstance(expected, Mapping): - cls = ApproxMapping - elif _is_numpy_array(expected): - expected = _as_numpy_array(expected) - cls = ApproxNumpy - elif _is_sequence_like(expected): - cls = ApproxSequenceLike - elif isinstance(expected, Collection) and not isinstance(expected, (str, bytes)): - msg = f"pytest.approx() only supports ordered sequences, but got: {expected!r}" - raise TypeError(msg) - else: - cls = ApproxScalar - - return cls(expected, rel, abs, nan_ok) - - -def _is_sequence_like(expected: object) -> bool: - return ( - hasattr(expected, "__getitem__") - and isinstance(expected, Sized) - and not isinstance(expected, (str, bytes)) - ) - - -def _is_numpy_array(obj: object) -> bool: - """ - Return true if the given object is implicitly convertible to ndarray, - and numpy is already imported. - """ - return _as_numpy_array(obj) is not None - - -def _as_numpy_array(obj: object) -> ndarray | None: - """ - Return an ndarray if the given object is implicitly convertible to ndarray, - and numpy is already imported, otherwise None. - """ - import sys - - np: Any = sys.modules.get("numpy") - if np is not None: - # avoid infinite recursion on numpy scalars, which have __array__ - if np.isscalar(obj): - return None - elif isinstance(obj, np.ndarray): - return obj - elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"): - return np.asarray(obj) - return None - - -# builtin pytest.raises helper - -E = TypeVar("E", bound=BaseException) - - -@overload -def raises( - expected_exception: type[E] | tuple[type[E], ...], - *, - match: str | Pattern[str] | None = ..., -) -> RaisesContext[E]: ... - - -@overload -def raises( - expected_exception: type[E] | tuple[type[E], ...], - func: Callable[..., Any], - *args: Any, - **kwargs: Any, -) -> _pytest._code.ExceptionInfo[E]: ... - - -def raises( - expected_exception: type[E] | tuple[type[E], ...], *args: Any, **kwargs: Any -) -> RaisesContext[E] | _pytest._code.ExceptionInfo[E]: - r"""Assert that a code block/function call raises an exception type, or one of its subclasses. - - :param expected_exception: - The expected exception type, or a tuple if one of multiple possible - exception types are expected. Note that subclasses of the passed exceptions - will also match. - - :kwparam str | re.Pattern[str] | None match: - If specified, a string containing a regular expression, - or a regular expression object, that is tested against the string - representation of the exception and its :pep:`678` `__notes__` - using :func:`re.search`. - - To match a literal string that may contain :ref:`special characters - `, the pattern can first be escaped with :func:`re.escape`. - - (This is only used when ``pytest.raises`` is used as a context manager, - and passed through to the function otherwise. - When using ``pytest.raises`` as a function, you can use: - ``pytest.raises(Exc, func, match="passed on").match("my pattern")``.) - - Use ``pytest.raises`` as a context manager, which will capture the exception of the given - type, or any of its subclasses:: - - >>> import pytest - >>> with pytest.raises(ZeroDivisionError): - ... 1/0 - - If the code block does not raise the expected exception (:class:`ZeroDivisionError` in the example - above), or no exception at all, the check will fail instead. - - You can also use the keyword argument ``match`` to assert that the - exception matches a text or regex:: - - >>> with pytest.raises(ValueError, match='must be 0 or None'): - ... raise ValueError("value must be 0 or None") - - >>> with pytest.raises(ValueError, match=r'must be \d+$'): - ... raise ValueError("value must be 42") - - The ``match`` argument searches the formatted exception string, which includes any - `PEP-678 `__ ``__notes__``: - - >>> with pytest.raises(ValueError, match=r"had a note added"): # doctest: +SKIP - ... e = ValueError("value must be 42") - ... e.add_note("had a note added") - ... raise e - - The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the - details of the captured exception:: - - >>> with pytest.raises(ValueError) as exc_info: - ... raise ValueError("value must be 42") - >>> assert exc_info.type is ValueError - >>> assert exc_info.value.args[0] == "value must be 42" - - .. warning:: - - Given that ``pytest.raises`` matches subclasses, be wary of using it to match :class:`Exception` like this:: - - with pytest.raises(Exception): # Careful, this will catch ANY exception raised. - some_function() - - Because :class:`Exception` is the base class of almost all exceptions, it is easy for this to hide - real bugs, where the user wrote this expecting a specific exception, but some other exception is being - raised due to a bug introduced during a refactoring. - - Avoid using ``pytest.raises`` to catch :class:`Exception` unless certain that you really want to catch - **any** exception raised. - - .. note:: - - When using ``pytest.raises`` as a context manager, it's worthwhile to - note that normal context manager rules apply and that the exception - raised *must* be the final line in the scope of the context manager. - Lines of code after that, within the scope of the context manager will - not be executed. For example:: - - >>> value = 15 - >>> with pytest.raises(ValueError) as exc_info: - ... if value > 10: - ... raise ValueError("value must be <= 10") - ... assert exc_info.type is ValueError # This will not execute. - - Instead, the following approach must be taken (note the difference in - scope):: - - >>> with pytest.raises(ValueError) as exc_info: - ... if value > 10: - ... raise ValueError("value must be <= 10") - ... - >>> assert exc_info.type is ValueError - - **Using with** ``pytest.mark.parametrize`` - - When using :ref:`pytest.mark.parametrize ref` - it is possible to parametrize tests such that - some runs raise an exception and others do not. - - See :ref:`parametrizing_conditional_raising` for an example. - - .. seealso:: - - :ref:`assertraises` for more examples and detailed discussion. - - **Legacy form** - - It is possible to specify a callable by passing a to-be-called lambda:: - - >>> raises(ZeroDivisionError, lambda: 1/0) - - - or you can specify an arbitrary callable with arguments:: - - >>> def f(x): return 1/x - ... - >>> raises(ZeroDivisionError, f, 0) - - >>> raises(ZeroDivisionError, f, x=0) - - - The form above is fully supported but discouraged for new code because the - context manager form is regarded as more readable and less error-prone. - - .. note:: - Similar to caught exception objects in Python, explicitly clearing - local references to returned ``ExceptionInfo`` objects can - help the Python interpreter speed up its garbage collection. - - Clearing those references breaks a reference cycle - (``ExceptionInfo`` --> caught exception --> frame stack raising - the exception --> current frame stack --> local variables --> - ``ExceptionInfo``) which makes Python keep all objects referenced - from that cycle (including all local variables in the current - frame) alive until the next cyclic garbage collection run. - More detailed information can be found in the official Python - documentation for :ref:`the try statement `. - """ - __tracebackhide__ = True - - if not expected_exception: - raise ValueError( - f"Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. " - f"Raising exceptions is already understood as failing the test, so you don't need " - f"any special code to say 'this should never raise an exception'." - ) - if isinstance(expected_exception, type): - expected_exceptions: tuple[type[E], ...] = (expected_exception,) - else: - expected_exceptions = expected_exception - for exc in expected_exceptions: - if not isinstance(exc, type) or not issubclass(exc, BaseException): - msg = "expected exception must be a BaseException type, not {}" # type: ignore[unreachable] - not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__ - raise TypeError(msg.format(not_a)) - - message = f"DID NOT RAISE {expected_exception}" - - if not args: - match: str | Pattern[str] | None = kwargs.pop("match", None) - if kwargs: - msg = "Unexpected keyword arguments passed to pytest.raises: " - msg += ", ".join(sorted(kwargs)) - msg += "\nUse context-manager form instead?" - raise TypeError(msg) - return RaisesContext(expected_exception, message, match) - else: - func = args[0] - if not callable(func): - raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") - try: - func(*args[1:], **kwargs) - except expected_exception as e: - return _pytest._code.ExceptionInfo.from_exception(e) - fail(message) - - -# This doesn't work with mypy for now. Use fail.Exception instead. -raises.Exception = fail.Exception # type: ignore - - -@final -class RaisesContext(ContextManager[_pytest._code.ExceptionInfo[E]]): - def __init__( - self, - expected_exception: type[E] | tuple[type[E], ...], - message: str, - match_expr: str | Pattern[str] | None = None, - ) -> None: - self.expected_exception = expected_exception - self.message = message - self.match_expr = match_expr - self.excinfo: _pytest._code.ExceptionInfo[E] | None = None - if self.match_expr is not None: - re_error = None - try: - re.compile(self.match_expr) - except re.error as e: - re_error = e - if re_error is not None: - fail(f"Invalid regex pattern provided to 'match': {re_error}") - - def __enter__(self) -> _pytest._code.ExceptionInfo[E]: - self.excinfo = _pytest._code.ExceptionInfo.for_later() - return self.excinfo - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool: - __tracebackhide__ = True - if exc_type is None: - fail(self.message) - assert self.excinfo is not None - if not issubclass(exc_type, self.expected_exception): - return False - # Cast to narrow the exception type now that it's verified. - exc_info = cast(Tuple[Type[E], E, TracebackType], (exc_type, exc_val, exc_tb)) - self.excinfo.fill_unfilled(exc_info) - if self.match_expr is not None: - self.excinfo.match(self.match_expr) - return True diff --git a/.venv/lib/python3.12/site-packages/_pytest/python_path.py b/.venv/lib/python3.12/site-packages/_pytest/python_path.py deleted file mode 100644 index 6e33c8a3..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/python_path.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -import sys - -import pytest -from pytest import Config -from pytest import Parser - - -def pytest_addoption(parser: Parser) -> None: - parser.addini("pythonpath", type="paths", help="Add paths to sys.path", default=[]) - - -@pytest.hookimpl(tryfirst=True) -def pytest_load_initial_conftests(early_config: Config) -> None: - # `pythonpath = a b` will set `sys.path` to `[a, b, x, y, z, ...]` - for path in reversed(early_config.getini("pythonpath")): - sys.path.insert(0, str(path)) - - -@pytest.hookimpl(trylast=True) -def pytest_unconfigure(config: Config) -> None: - for path in config.getini("pythonpath"): - path_str = str(path) - if path_str in sys.path: - sys.path.remove(path_str) diff --git a/.venv/lib/python3.12/site-packages/_pytest/recwarn.py b/.venv/lib/python3.12/site-packages/_pytest/recwarn.py deleted file mode 100644 index 0dc002ed..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/recwarn.py +++ /dev/null @@ -1,364 +0,0 @@ -# mypy: allow-untyped-defs -"""Record warnings during test function execution.""" - -from __future__ import annotations - -from pprint import pformat -import re -from types import TracebackType -from typing import Any -from typing import Callable -from typing import final -from typing import Generator -from typing import Iterator -from typing import overload -from typing import Pattern -from typing import TYPE_CHECKING -from typing import TypeVar - - -if TYPE_CHECKING: - from typing_extensions import Self - -import warnings - -from _pytest.deprecated import check_ispytest -from _pytest.fixtures import fixture -from _pytest.outcomes import Exit -from _pytest.outcomes import fail - - -T = TypeVar("T") - - -@fixture -def recwarn() -> Generator[WarningsRecorder]: - """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. - - See :ref:`warnings` for information on warning categories. - """ - wrec = WarningsRecorder(_ispytest=True) - with wrec: - warnings.simplefilter("default") - yield wrec - - -@overload -def deprecated_call(*, match: str | Pattern[str] | None = ...) -> WarningsRecorder: ... - - -@overload -def deprecated_call(func: Callable[..., T], *args: Any, **kwargs: Any) -> T: ... - - -def deprecated_call( - func: Callable[..., Any] | None = None, *args: Any, **kwargs: Any -) -> WarningsRecorder | Any: - """Assert that code produces a ``DeprecationWarning`` or ``PendingDeprecationWarning`` or ``FutureWarning``. - - This function can be used as a context manager:: - - >>> import warnings - >>> def api_call_v2(): - ... warnings.warn('use v3 of this api', DeprecationWarning) - ... return 200 - - >>> import pytest - >>> with pytest.deprecated_call(): - ... assert api_call_v2() == 200 - - It can also be used by passing a function and ``*args`` and ``**kwargs``, - in which case it will ensure calling ``func(*args, **kwargs)`` produces one of - the warnings types above. The return value is the return value of the function. - - In the context manager form you may use the keyword argument ``match`` to assert - that the warning matches a text or regex. - - The context manager produces a list of :class:`warnings.WarningMessage` objects, - one for each warning raised. - """ - __tracebackhide__ = True - if func is not None: - args = (func, *args) - return warns( - (DeprecationWarning, PendingDeprecationWarning, FutureWarning), *args, **kwargs - ) - - -@overload -def warns( - expected_warning: type[Warning] | tuple[type[Warning], ...] = ..., - *, - match: str | Pattern[str] | None = ..., -) -> WarningsChecker: ... - - -@overload -def warns( - expected_warning: type[Warning] | tuple[type[Warning], ...], - func: Callable[..., T], - *args: Any, - **kwargs: Any, -) -> T: ... - - -def warns( - expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, - *args: Any, - match: str | Pattern[str] | None = None, - **kwargs: Any, -) -> WarningsChecker | Any: - r"""Assert that code raises a particular class of warning. - - Specifically, the parameter ``expected_warning`` can be a warning class or tuple - of warning classes, and the code inside the ``with`` block must issue at least one - warning of that class or classes. - - This helper produces a list of :class:`warnings.WarningMessage` objects, one for - each warning emitted (regardless of whether it is an ``expected_warning`` or not). - Since pytest 8.0, unmatched warnings are also re-emitted when the context closes. - - This function can be used as a context manager:: - - >>> import pytest - >>> with pytest.warns(RuntimeWarning): - ... warnings.warn("my warning", RuntimeWarning) - - In the context manager form you may use the keyword argument ``match`` to assert - that the warning matches a text or regex:: - - >>> with pytest.warns(UserWarning, match='must be 0 or None'): - ... warnings.warn("value must be 0 or None", UserWarning) - - >>> with pytest.warns(UserWarning, match=r'must be \d+$'): - ... warnings.warn("value must be 42", UserWarning) - - >>> with pytest.warns(UserWarning): # catch re-emitted warning - ... with pytest.warns(UserWarning, match=r'must be \d+$'): - ... warnings.warn("this is not here", UserWarning) - Traceback (most recent call last): - ... - Failed: DID NOT WARN. No warnings of type ...UserWarning... were emitted... - - **Using with** ``pytest.mark.parametrize`` - - When using :ref:`pytest.mark.parametrize ref` it is possible to parametrize tests - such that some runs raise a warning and others do not. - - This could be achieved in the same way as with exceptions, see - :ref:`parametrizing_conditional_raising` for an example. - - """ - __tracebackhide__ = True - if not args: - if kwargs: - argnames = ", ".join(sorted(kwargs)) - raise TypeError( - f"Unexpected keyword arguments passed to pytest.warns: {argnames}" - "\nUse context-manager form instead?" - ) - return WarningsChecker(expected_warning, match_expr=match, _ispytest=True) - else: - func = args[0] - if not callable(func): - raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") - with WarningsChecker(expected_warning, _ispytest=True): - return func(*args[1:], **kwargs) - - -class WarningsRecorder(warnings.catch_warnings): # type:ignore[type-arg] - """A context manager to record raised warnings. - - Each recorded warning is an instance of :class:`warnings.WarningMessage`. - - Adapted from `warnings.catch_warnings`. - - .. note:: - ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated - differently; see :ref:`ensuring_function_triggers`. - - """ - - def __init__(self, *, _ispytest: bool = False) -> None: - check_ispytest(_ispytest) - super().__init__(record=True) - self._entered = False - self._list: list[warnings.WarningMessage] = [] - - @property - def list(self) -> list[warnings.WarningMessage]: - """The list of recorded warnings.""" - return self._list - - def __getitem__(self, i: int) -> warnings.WarningMessage: - """Get a recorded warning by index.""" - return self._list[i] - - def __iter__(self) -> Iterator[warnings.WarningMessage]: - """Iterate through the recorded warnings.""" - return iter(self._list) - - def __len__(self) -> int: - """The number of recorded warnings.""" - return len(self._list) - - def pop(self, cls: type[Warning] = Warning) -> warnings.WarningMessage: - """Pop the first recorded warning which is an instance of ``cls``, - but not an instance of a child class of any other match. - Raises ``AssertionError`` if there is no match. - """ - best_idx: int | None = None - for i, w in enumerate(self._list): - if w.category == cls: - return self._list.pop(i) # exact match, stop looking - if issubclass(w.category, cls) and ( - best_idx is None - or not issubclass(w.category, self._list[best_idx].category) - ): - best_idx = i - if best_idx is not None: - return self._list.pop(best_idx) - __tracebackhide__ = True - raise AssertionError(f"{cls!r} not found in warning list") - - def clear(self) -> None: - """Clear the list of recorded warnings.""" - self._list[:] = [] - - def __enter__(self) -> Self: - if self._entered: - __tracebackhide__ = True - raise RuntimeError(f"Cannot enter {self!r} twice") - _list = super().__enter__() - # record=True means it's None. - assert _list is not None - self._list = _list - warnings.simplefilter("always") - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - if not self._entered: - __tracebackhide__ = True - raise RuntimeError(f"Cannot exit {self!r} without entering first") - - super().__exit__(exc_type, exc_val, exc_tb) - - # Built-in catch_warnings does not reset entered state so we do it - # manually here for this context manager to become reusable. - self._entered = False - - -@final -class WarningsChecker(WarningsRecorder): - def __init__( - self, - expected_warning: type[Warning] | tuple[type[Warning], ...] = Warning, - match_expr: str | Pattern[str] | None = None, - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - super().__init__(_ispytest=True) - - msg = "exceptions must be derived from Warning, not %s" - if isinstance(expected_warning, tuple): - for exc in expected_warning: - if not issubclass(exc, Warning): - raise TypeError(msg % type(exc)) - expected_warning_tup = expected_warning - elif isinstance(expected_warning, type) and issubclass( - expected_warning, Warning - ): - expected_warning_tup = (expected_warning,) - else: - raise TypeError(msg % type(expected_warning)) - - self.expected_warning = expected_warning_tup - self.match_expr = match_expr - - def matches(self, warning: warnings.WarningMessage) -> bool: - assert self.expected_warning is not None - return issubclass(warning.category, self.expected_warning) and bool( - self.match_expr is None or re.search(self.match_expr, str(warning.message)) - ) - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - super().__exit__(exc_type, exc_val, exc_tb) - - __tracebackhide__ = True - - # BaseExceptions like pytest.{skip,fail,xfail,exit} or Ctrl-C within - # pytest.warns should *not* trigger "DID NOT WARN" and get suppressed - # when the warning doesn't happen. Control-flow exceptions should always - # propagate. - if exc_val is not None and ( - not isinstance(exc_val, Exception) - # Exit is an Exception, not a BaseException, for some reason. - or isinstance(exc_val, Exit) - ): - return - - def found_str() -> str: - return pformat([record.message for record in self], indent=2) - - try: - if not any(issubclass(w.category, self.expected_warning) for w in self): - fail( - f"DID NOT WARN. No warnings of type {self.expected_warning} were emitted.\n" - f" Emitted warnings: {found_str()}." - ) - elif not any(self.matches(w) for w in self): - fail( - f"DID NOT WARN. No warnings of type {self.expected_warning} matching the regex were emitted.\n" - f" Regex: {self.match_expr}\n" - f" Emitted warnings: {found_str()}." - ) - finally: - # Whether or not any warnings matched, we want to re-emit all unmatched warnings. - for w in self: - if not self.matches(w): - warnings.warn_explicit( - message=w.message, - category=w.category, - filename=w.filename, - lineno=w.lineno, - module=w.__module__, - source=w.source, - ) - - # Currently in Python it is possible to pass other types than an - # `str` message when creating `Warning` instances, however this - # causes an exception when :func:`warnings.filterwarnings` is used - # to filter those warnings. See - # https://github.com/python/cpython/issues/103577 for a discussion. - # While this can be considered a bug in CPython, we put guards in - # pytest as the error message produced without this check in place - # is confusing (#10865). - for w in self: - if type(w.message) is not UserWarning: - # If the warning was of an incorrect type then `warnings.warn()` - # creates a UserWarning. Any other warning must have been specified - # explicitly. - continue - if not w.message.args: - # UserWarning() without arguments must have been specified explicitly. - continue - msg = w.message.args[0] - if isinstance(msg, str): - continue - # It's possible that UserWarning was explicitly specified, and - # its first argument was not a string. But that case can't be - # distinguished from an invalid type. - raise TypeError( - f"Warning must be str or Warning, got {msg!r} (type {type(msg).__name__})" - ) diff --git a/.venv/lib/python3.12/site-packages/_pytest/reports.py b/.venv/lib/python3.12/site-packages/_pytest/reports.py deleted file mode 100644 index 77cbf773..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/reports.py +++ /dev/null @@ -1,636 +0,0 @@ -# mypy: allow-untyped-defs -from __future__ import annotations - -import dataclasses -from io import StringIO -import os -from pprint import pprint -from typing import Any -from typing import cast -from typing import final -from typing import Iterable -from typing import Iterator -from typing import Literal -from typing import Mapping -from typing import NoReturn -from typing import Sequence -from typing import TYPE_CHECKING - -from _pytest._code.code import ExceptionChainRepr -from _pytest._code.code import ExceptionInfo -from _pytest._code.code import ExceptionRepr -from _pytest._code.code import ReprEntry -from _pytest._code.code import ReprEntryNative -from _pytest._code.code import ReprExceptionInfo -from _pytest._code.code import ReprFileLocation -from _pytest._code.code import ReprFuncArgs -from _pytest._code.code import ReprLocals -from _pytest._code.code import ReprTraceback -from _pytest._code.code import TerminalRepr -from _pytest._io import TerminalWriter -from _pytest.config import Config -from _pytest.nodes import Collector -from _pytest.nodes import Item -from _pytest.outcomes import fail -from _pytest.outcomes import skip - - -if TYPE_CHECKING: - from typing_extensions import Self - - from _pytest.runner import CallInfo - - -def getworkerinfoline(node): - try: - return node._workerinfocache - except AttributeError: - d = node.workerinfo - ver = "{}.{}.{}".format(*d["version_info"][:3]) - node._workerinfocache = s = "[{}] {} -- Python {} {}".format( - d["id"], d["sysplatform"], ver, d["executable"] - ) - return s - - -class BaseReport: - when: str | None - location: tuple[str, int | None, str] | None - longrepr: ( - None | ExceptionInfo[BaseException] | tuple[str, int, str] | str | TerminalRepr - ) - sections: list[tuple[str, str]] - nodeid: str - outcome: Literal["passed", "failed", "skipped"] - - def __init__(self, **kw: Any) -> None: - self.__dict__.update(kw) - - if TYPE_CHECKING: - # Can have arbitrary fields given to __init__(). - def __getattr__(self, key: str) -> Any: ... - - def toterminal(self, out: TerminalWriter) -> None: - if hasattr(self, "node"): - worker_info = getworkerinfoline(self.node) - if worker_info: - out.line(worker_info) - - longrepr = self.longrepr - if longrepr is None: - return - - if hasattr(longrepr, "toterminal"): - longrepr_terminal = cast(TerminalRepr, longrepr) - longrepr_terminal.toterminal(out) - else: - try: - s = str(longrepr) - except UnicodeEncodeError: - s = "" - out.line(s) - - def get_sections(self, prefix: str) -> Iterator[tuple[str, str]]: - for name, content in self.sections: - if name.startswith(prefix): - yield prefix, content - - @property - def longreprtext(self) -> str: - """Read-only property that returns the full string representation of - ``longrepr``. - - .. versionadded:: 3.0 - """ - file = StringIO() - tw = TerminalWriter(file) - tw.hasmarkup = False - self.toterminal(tw) - exc = file.getvalue() - return exc.strip() - - @property - def caplog(self) -> str: - """Return captured log lines, if log capturing is enabled. - - .. versionadded:: 3.5 - """ - return "\n".join( - content for (prefix, content) in self.get_sections("Captured log") - ) - - @property - def capstdout(self) -> str: - """Return captured text from stdout, if capturing is enabled. - - .. versionadded:: 3.0 - """ - return "".join( - content for (prefix, content) in self.get_sections("Captured stdout") - ) - - @property - def capstderr(self) -> str: - """Return captured text from stderr, if capturing is enabled. - - .. versionadded:: 3.0 - """ - return "".join( - content for (prefix, content) in self.get_sections("Captured stderr") - ) - - @property - def passed(self) -> bool: - """Whether the outcome is passed.""" - return self.outcome == "passed" - - @property - def failed(self) -> bool: - """Whether the outcome is failed.""" - return self.outcome == "failed" - - @property - def skipped(self) -> bool: - """Whether the outcome is skipped.""" - return self.outcome == "skipped" - - @property - def fspath(self) -> str: - """The path portion of the reported node, as a string.""" - return self.nodeid.split("::")[0] - - @property - def count_towards_summary(self) -> bool: - """**Experimental** Whether this report should be counted towards the - totals shown at the end of the test session: "1 passed, 1 failure, etc". - - .. note:: - - This function is considered **experimental**, so beware that it is subject to changes - even in patch releases. - """ - return True - - @property - def head_line(self) -> str | None: - """**Experimental** The head line shown with longrepr output for this - report, more commonly during traceback representation during - failures:: - - ________ Test.foo ________ - - - In the example above, the head_line is "Test.foo". - - .. note:: - - This function is considered **experimental**, so beware that it is subject to changes - even in patch releases. - """ - if self.location is not None: - fspath, lineno, domain = self.location - return domain - return None - - def _get_verbose_word_with_markup( - self, config: Config, default_markup: Mapping[str, bool] - ) -> tuple[str, Mapping[str, bool]]: - _category, _short, verbose = config.hook.pytest_report_teststatus( - report=self, config=config - ) - - if isinstance(verbose, str): - return verbose, default_markup - - if isinstance(verbose, Sequence) and len(verbose) == 2: - word, markup = verbose - if isinstance(word, str) and isinstance(markup, Mapping): - return word, markup - - fail( # pragma: no cover - "pytest_report_teststatus() hook (from a plugin) returned " - f"an invalid verbose value: {verbose!r}.\nExpected either a string " - "or a tuple of (word, markup)." - ) - - def _to_json(self) -> dict[str, Any]: - """Return the contents of this report as a dict of builtin entries, - suitable for serialization. - - This was originally the serialize_report() function from xdist (ca03269). - - Experimental method. - """ - return _report_to_json(self) - - @classmethod - def _from_json(cls, reportdict: dict[str, object]) -> Self: - """Create either a TestReport or CollectReport, depending on the calling class. - - It is the callers responsibility to know which class to pass here. - - This was originally the serialize_report() function from xdist (ca03269). - - Experimental method. - """ - kwargs = _report_kwargs_from_json(reportdict) - return cls(**kwargs) - - -def _report_unserialization_failure( - type_name: str, report_class: type[BaseReport], reportdict -) -> NoReturn: - url = "https://github.com/pytest-dev/pytest/issues" - stream = StringIO() - pprint("-" * 100, stream=stream) - pprint(f"INTERNALERROR: Unknown entry type returned: {type_name}", stream=stream) - pprint(f"report_name: {report_class}", stream=stream) - pprint(reportdict, stream=stream) - pprint(f"Please report this bug at {url}", stream=stream) - pprint("-" * 100, stream=stream) - raise RuntimeError(stream.getvalue()) - - -@final -class TestReport(BaseReport): - """Basic test report object (also used for setup and teardown calls if - they fail). - - Reports can contain arbitrary extra attributes. - """ - - __test__ = False - # Defined by skipping plugin. - # xfail reason if xfailed, otherwise not defined. Use hasattr to distinguish. - wasxfail: str - - def __init__( - self, - nodeid: str, - location: tuple[str, int | None, str], - keywords: Mapping[str, Any], - outcome: Literal["passed", "failed", "skipped"], - longrepr: None - | ExceptionInfo[BaseException] - | tuple[str, int, str] - | str - | TerminalRepr, - when: Literal["setup", "call", "teardown"], - sections: Iterable[tuple[str, str]] = (), - duration: float = 0, - start: float = 0, - stop: float = 0, - user_properties: Iterable[tuple[str, object]] | None = None, - **extra, - ) -> None: - #: Normalized collection nodeid. - self.nodeid = nodeid - - #: A (filesystempath, lineno, domaininfo) tuple indicating the - #: actual location of a test item - it might be different from the - #: collected one e.g. if a method is inherited from a different module. - #: The filesystempath may be relative to ``config.rootdir``. - #: The line number is 0-based. - self.location: tuple[str, int | None, str] = location - - #: A name -> value dictionary containing all keywords and - #: markers associated with a test invocation. - self.keywords: Mapping[str, Any] = keywords - - #: Test outcome, always one of "passed", "failed", "skipped". - self.outcome = outcome - - #: None or a failure representation. - self.longrepr = longrepr - - #: One of 'setup', 'call', 'teardown' to indicate runtest phase. - self.when = when - - #: User properties is a list of tuples (name, value) that holds user - #: defined properties of the test. - self.user_properties = list(user_properties or []) - - #: Tuples of str ``(heading, content)`` with extra information - #: for the test report. Used by pytest to add text captured - #: from ``stdout``, ``stderr``, and intercepted logging events. May - #: be used by other plugins to add arbitrary information to reports. - self.sections = list(sections) - - #: Time it took to run just the test. - self.duration: float = duration - - #: The system time when the call started, in seconds since the epoch. - self.start: float = start - #: The system time when the call ended, in seconds since the epoch. - self.stop: float = stop - - self.__dict__.update(extra) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.nodeid!r} when={self.when!r} outcome={self.outcome!r}>" - - @classmethod - def from_item_and_call(cls, item: Item, call: CallInfo[None]) -> TestReport: - """Create and fill a TestReport with standard item and call info. - - :param item: The item. - :param call: The call info. - """ - when = call.when - # Remove "collect" from the Literal type -- only for collection calls. - assert when != "collect" - duration = call.duration - start = call.start - stop = call.stop - keywords = {x: 1 for x in item.keywords} - excinfo = call.excinfo - sections = [] - if not call.excinfo: - outcome: Literal["passed", "failed", "skipped"] = "passed" - longrepr: ( - None - | ExceptionInfo[BaseException] - | tuple[str, int, str] - | str - | TerminalRepr - ) = None - else: - if not isinstance(excinfo, ExceptionInfo): - outcome = "failed" - longrepr = excinfo - elif isinstance(excinfo.value, skip.Exception): - outcome = "skipped" - r = excinfo._getreprcrash() - assert ( - r is not None - ), "There should always be a traceback entry for skipping a test." - if excinfo.value._use_item_location: - path, line = item.reportinfo()[:2] - assert line is not None - longrepr = os.fspath(path), line + 1, r.message - else: - longrepr = (str(r.path), r.lineno, r.message) - else: - outcome = "failed" - if call.when == "call": - longrepr = item.repr_failure(excinfo) - else: # exception in setup or teardown - longrepr = item._repr_failure_py( - excinfo, style=item.config.getoption("tbstyle", "auto") - ) - for rwhen, key, content in item._report_sections: - sections.append((f"Captured {key} {rwhen}", content)) - return cls( - item.nodeid, - item.location, - keywords, - outcome, - longrepr, - when, - sections, - duration, - start, - stop, - user_properties=item.user_properties, - ) - - -@final -class CollectReport(BaseReport): - """Collection report object. - - Reports can contain arbitrary extra attributes. - """ - - when = "collect" - - def __init__( - self, - nodeid: str, - outcome: Literal["passed", "failed", "skipped"], - longrepr: None - | ExceptionInfo[BaseException] - | tuple[str, int, str] - | str - | TerminalRepr, - result: list[Item | Collector] | None, - sections: Iterable[tuple[str, str]] = (), - **extra, - ) -> None: - #: Normalized collection nodeid. - self.nodeid = nodeid - - #: Test outcome, always one of "passed", "failed", "skipped". - self.outcome = outcome - - #: None or a failure representation. - self.longrepr = longrepr - - #: The collected items and collection nodes. - self.result = result or [] - - #: Tuples of str ``(heading, content)`` with extra information - #: for the test report. Used by pytest to add text captured - #: from ``stdout``, ``stderr``, and intercepted logging events. May - #: be used by other plugins to add arbitrary information to reports. - self.sections = list(sections) - - self.__dict__.update(extra) - - @property - def location( # type:ignore[override] - self, - ) -> tuple[str, int | None, str] | None: - return (self.fspath, None, self.fspath) - - def __repr__(self) -> str: - return f"" - - -class CollectErrorRepr(TerminalRepr): - def __init__(self, msg: str) -> None: - self.longrepr = msg - - def toterminal(self, out: TerminalWriter) -> None: - out.line(self.longrepr, red=True) - - -def pytest_report_to_serializable( - report: CollectReport | TestReport, -) -> dict[str, Any] | None: - if isinstance(report, (TestReport, CollectReport)): - data = report._to_json() - data["$report_type"] = report.__class__.__name__ - return data - # TODO: Check if this is actually reachable. - return None # type: ignore[unreachable] - - -def pytest_report_from_serializable( - data: dict[str, Any], -) -> CollectReport | TestReport | None: - if "$report_type" in data: - if data["$report_type"] == "TestReport": - return TestReport._from_json(data) - elif data["$report_type"] == "CollectReport": - return CollectReport._from_json(data) - assert False, "Unknown report_type unserialize data: {}".format( - data["$report_type"] - ) - return None - - -def _report_to_json(report: BaseReport) -> dict[str, Any]: - """Return the contents of this report as a dict of builtin entries, - suitable for serialization. - - This was originally the serialize_report() function from xdist (ca03269). - """ - - def serialize_repr_entry( - entry: ReprEntry | ReprEntryNative, - ) -> dict[str, Any]: - data = dataclasses.asdict(entry) - for key, value in data.items(): - if hasattr(value, "__dict__"): - data[key] = dataclasses.asdict(value) - entry_data = {"type": type(entry).__name__, "data": data} - return entry_data - - def serialize_repr_traceback(reprtraceback: ReprTraceback) -> dict[str, Any]: - result = dataclasses.asdict(reprtraceback) - result["reprentries"] = [ - serialize_repr_entry(x) for x in reprtraceback.reprentries - ] - return result - - def serialize_repr_crash( - reprcrash: ReprFileLocation | None, - ) -> dict[str, Any] | None: - if reprcrash is not None: - return dataclasses.asdict(reprcrash) - else: - return None - - def serialize_exception_longrepr(rep: BaseReport) -> dict[str, Any]: - assert rep.longrepr is not None - # TODO: Investigate whether the duck typing is really necessary here. - longrepr = cast(ExceptionRepr, rep.longrepr) - result: dict[str, Any] = { - "reprcrash": serialize_repr_crash(longrepr.reprcrash), - "reprtraceback": serialize_repr_traceback(longrepr.reprtraceback), - "sections": longrepr.sections, - } - if isinstance(longrepr, ExceptionChainRepr): - result["chain"] = [] - for repr_traceback, repr_crash, description in longrepr.chain: - result["chain"].append( - ( - serialize_repr_traceback(repr_traceback), - serialize_repr_crash(repr_crash), - description, - ) - ) - else: - result["chain"] = None - return result - - d = report.__dict__.copy() - if hasattr(report.longrepr, "toterminal"): - if hasattr(report.longrepr, "reprtraceback") and hasattr( - report.longrepr, "reprcrash" - ): - d["longrepr"] = serialize_exception_longrepr(report) - else: - d["longrepr"] = str(report.longrepr) - else: - d["longrepr"] = report.longrepr - for name in d: - if isinstance(d[name], os.PathLike): - d[name] = os.fspath(d[name]) - elif name == "result": - d[name] = None # for now - return d - - -def _report_kwargs_from_json(reportdict: dict[str, Any]) -> dict[str, Any]: - """Return **kwargs that can be used to construct a TestReport or - CollectReport instance. - - This was originally the serialize_report() function from xdist (ca03269). - """ - - def deserialize_repr_entry(entry_data): - data = entry_data["data"] - entry_type = entry_data["type"] - if entry_type == "ReprEntry": - reprfuncargs = None - reprfileloc = None - reprlocals = None - if data["reprfuncargs"]: - reprfuncargs = ReprFuncArgs(**data["reprfuncargs"]) - if data["reprfileloc"]: - reprfileloc = ReprFileLocation(**data["reprfileloc"]) - if data["reprlocals"]: - reprlocals = ReprLocals(data["reprlocals"]["lines"]) - - reprentry: ReprEntry | ReprEntryNative = ReprEntry( - lines=data["lines"], - reprfuncargs=reprfuncargs, - reprlocals=reprlocals, - reprfileloc=reprfileloc, - style=data["style"], - ) - elif entry_type == "ReprEntryNative": - reprentry = ReprEntryNative(data["lines"]) - else: - _report_unserialization_failure(entry_type, TestReport, reportdict) - return reprentry - - def deserialize_repr_traceback(repr_traceback_dict): - repr_traceback_dict["reprentries"] = [ - deserialize_repr_entry(x) for x in repr_traceback_dict["reprentries"] - ] - return ReprTraceback(**repr_traceback_dict) - - def deserialize_repr_crash(repr_crash_dict: dict[str, Any] | None): - if repr_crash_dict is not None: - return ReprFileLocation(**repr_crash_dict) - else: - return None - - if ( - reportdict["longrepr"] - and "reprcrash" in reportdict["longrepr"] - and "reprtraceback" in reportdict["longrepr"] - ): - reprtraceback = deserialize_repr_traceback( - reportdict["longrepr"]["reprtraceback"] - ) - reprcrash = deserialize_repr_crash(reportdict["longrepr"]["reprcrash"]) - if reportdict["longrepr"]["chain"]: - chain = [] - for repr_traceback_data, repr_crash_data, description in reportdict[ - "longrepr" - ]["chain"]: - chain.append( - ( - deserialize_repr_traceback(repr_traceback_data), - deserialize_repr_crash(repr_crash_data), - description, - ) - ) - exception_info: ExceptionChainRepr | ReprExceptionInfo = ExceptionChainRepr( - chain - ) - else: - exception_info = ReprExceptionInfo( - reprtraceback=reprtraceback, - reprcrash=reprcrash, - ) - - for section in reportdict["longrepr"]["sections"]: - exception_info.addsection(*section) - reportdict["longrepr"] = exception_info - - return reportdict diff --git a/.venv/lib/python3.12/site-packages/_pytest/runner.py b/.venv/lib/python3.12/site-packages/_pytest/runner.py deleted file mode 100644 index 0b60301b..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/runner.py +++ /dev/null @@ -1,571 +0,0 @@ -# mypy: allow-untyped-defs -"""Basic collect and runtest protocol implementations.""" - -from __future__ import annotations - -import bdb -import dataclasses -import os -import sys -import types -from typing import Callable -from typing import cast -from typing import final -from typing import Generic -from typing import Literal -from typing import TYPE_CHECKING -from typing import TypeVar - -from .reports import BaseReport -from .reports import CollectErrorRepr -from .reports import CollectReport -from .reports import TestReport -from _pytest import timing -from _pytest._code.code import ExceptionChainRepr -from _pytest._code.code import ExceptionInfo -from _pytest._code.code import TerminalRepr -from _pytest.config.argparsing import Parser -from _pytest.deprecated import check_ispytest -from _pytest.nodes import Collector -from _pytest.nodes import Directory -from _pytest.nodes import Item -from _pytest.nodes import Node -from _pytest.outcomes import Exit -from _pytest.outcomes import OutcomeException -from _pytest.outcomes import Skipped -from _pytest.outcomes import TEST_OUTCOME - - -if sys.version_info < (3, 11): - from exceptiongroup import BaseExceptionGroup - -if TYPE_CHECKING: - from _pytest.main import Session - from _pytest.terminal import TerminalReporter - -# -# pytest plugin hooks. - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("terminal reporting", "Reporting", after="general") - group.addoption( - "--durations", - action="store", - type=int, - default=None, - metavar="N", - help="Show N slowest setup/test durations (N=0 for all)", - ) - group.addoption( - "--durations-min", - action="store", - type=float, - default=0.005, - metavar="N", - help="Minimal duration in seconds for inclusion in slowest list. " - "Default: 0.005.", - ) - - -def pytest_terminal_summary(terminalreporter: TerminalReporter) -> None: - durations = terminalreporter.config.option.durations - durations_min = terminalreporter.config.option.durations_min - verbose = terminalreporter.config.get_verbosity() - if durations is None: - return - tr = terminalreporter - dlist = [] - for replist in tr.stats.values(): - for rep in replist: - if hasattr(rep, "duration"): - dlist.append(rep) - if not dlist: - return - dlist.sort(key=lambda x: x.duration, reverse=True) - if not durations: - tr.write_sep("=", "slowest durations") - else: - tr.write_sep("=", f"slowest {durations} durations") - dlist = dlist[:durations] - - for i, rep in enumerate(dlist): - if verbose < 2 and rep.duration < durations_min: - tr.write_line("") - tr.write_line( - f"({len(dlist) - i} durations < {durations_min:g}s hidden. Use -vv to show these durations.)" - ) - break - tr.write_line(f"{rep.duration:02.2f}s {rep.when:<8} {rep.nodeid}") - - -def pytest_sessionstart(session: Session) -> None: - session._setupstate = SetupState() - - -def pytest_sessionfinish(session: Session) -> None: - session._setupstate.teardown_exact(None) - - -def pytest_runtest_protocol(item: Item, nextitem: Item | None) -> bool: - ihook = item.ihook - ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location) - runtestprotocol(item, nextitem=nextitem) - ihook.pytest_runtest_logfinish(nodeid=item.nodeid, location=item.location) - return True - - -def runtestprotocol( - item: Item, log: bool = True, nextitem: Item | None = None -) -> list[TestReport]: - hasrequest = hasattr(item, "_request") - if hasrequest and not item._request: # type: ignore[attr-defined] - # This only happens if the item is re-run, as is done by - # pytest-rerunfailures. - item._initrequest() # type: ignore[attr-defined] - rep = call_and_report(item, "setup", log) - reports = [rep] - if rep.passed: - if item.config.getoption("setupshow", False): - show_test_item(item) - if not item.config.getoption("setuponly", False): - reports.append(call_and_report(item, "call", log)) - # If the session is about to fail or stop, teardown everything - this is - # necessary to correctly report fixture teardown errors (see #11706) - if item.session.shouldfail or item.session.shouldstop: - nextitem = None - reports.append(call_and_report(item, "teardown", log, nextitem=nextitem)) - # After all teardown hooks have been called - # want funcargs and request info to go away. - if hasrequest: - item._request = False # type: ignore[attr-defined] - item.funcargs = None # type: ignore[attr-defined] - return reports - - -def show_test_item(item: Item) -> None: - """Show test function, parameters and the fixtures of the test item.""" - tw = item.config.get_terminal_writer() - tw.line() - tw.write(" " * 8) - tw.write(item.nodeid) - used_fixtures = sorted(getattr(item, "fixturenames", [])) - if used_fixtures: - tw.write(" (fixtures used: {})".format(", ".join(used_fixtures))) - tw.flush() - - -def pytest_runtest_setup(item: Item) -> None: - _update_current_test_var(item, "setup") - item.session._setupstate.setup(item) - - -def pytest_runtest_call(item: Item) -> None: - _update_current_test_var(item, "call") - try: - del sys.last_type - del sys.last_value - del sys.last_traceback - if sys.version_info >= (3, 12, 0): - del sys.last_exc # type:ignore[attr-defined] - except AttributeError: - pass - try: - item.runtest() - except Exception as e: - # Store trace info to allow postmortem debugging - sys.last_type = type(e) - sys.last_value = e - if sys.version_info >= (3, 12, 0): - sys.last_exc = e # type:ignore[attr-defined] - assert e.__traceback__ is not None - # Skip *this* frame - sys.last_traceback = e.__traceback__.tb_next - raise - - -def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None: - _update_current_test_var(item, "teardown") - item.session._setupstate.teardown_exact(nextitem) - _update_current_test_var(item, None) - - -def _update_current_test_var( - item: Item, when: Literal["setup", "call", "teardown"] | None -) -> None: - """Update :envvar:`PYTEST_CURRENT_TEST` to reflect the current item and stage. - - If ``when`` is None, delete ``PYTEST_CURRENT_TEST`` from the environment. - """ - var_name = "PYTEST_CURRENT_TEST" - if when: - value = f"{item.nodeid} ({when})" - # don't allow null bytes on environment variables (see #2644, #2957) - value = value.replace("\x00", "(null)") - os.environ[var_name] = value - else: - os.environ.pop(var_name) - - -def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str] | None: - if report.when in ("setup", "teardown"): - if report.failed: - # category, shortletter, verbose-word - return "error", "E", "ERROR" - elif report.skipped: - return "skipped", "s", "SKIPPED" - else: - return "", "", "" - return None - - -# -# Implementation - - -def call_and_report( - item: Item, when: Literal["setup", "call", "teardown"], log: bool = True, **kwds -) -> TestReport: - ihook = item.ihook - if when == "setup": - runtest_hook: Callable[..., None] = ihook.pytest_runtest_setup - elif when == "call": - runtest_hook = ihook.pytest_runtest_call - elif when == "teardown": - runtest_hook = ihook.pytest_runtest_teardown - else: - assert False, f"Unhandled runtest hook case: {when}" - reraise: tuple[type[BaseException], ...] = (Exit,) - if not item.config.getoption("usepdb", False): - reraise += (KeyboardInterrupt,) - call = CallInfo.from_call( - lambda: runtest_hook(item=item, **kwds), when=when, reraise=reraise - ) - report: TestReport = ihook.pytest_runtest_makereport(item=item, call=call) - if log: - ihook.pytest_runtest_logreport(report=report) - if check_interactive_exception(call, report): - ihook.pytest_exception_interact(node=item, call=call, report=report) - return report - - -def check_interactive_exception(call: CallInfo[object], report: BaseReport) -> bool: - """Check whether the call raised an exception that should be reported as - interactive.""" - if call.excinfo is None: - # Didn't raise. - return False - if hasattr(report, "wasxfail"): - # Exception was expected. - return False - if isinstance(call.excinfo.value, (Skipped, bdb.BdbQuit)): - # Special control flow exception. - return False - return True - - -TResult = TypeVar("TResult", covariant=True) - - -@final -@dataclasses.dataclass -class CallInfo(Generic[TResult]): - """Result/Exception info of a function invocation.""" - - _result: TResult | None - #: The captured exception of the call, if it raised. - excinfo: ExceptionInfo[BaseException] | None - #: The system time when the call started, in seconds since the epoch. - start: float - #: The system time when the call ended, in seconds since the epoch. - stop: float - #: The call duration, in seconds. - duration: float - #: The context of invocation: "collect", "setup", "call" or "teardown". - when: Literal["collect", "setup", "call", "teardown"] - - def __init__( - self, - result: TResult | None, - excinfo: ExceptionInfo[BaseException] | None, - start: float, - stop: float, - duration: float, - when: Literal["collect", "setup", "call", "teardown"], - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - self._result = result - self.excinfo = excinfo - self.start = start - self.stop = stop - self.duration = duration - self.when = when - - @property - def result(self) -> TResult: - """The return value of the call, if it didn't raise. - - Can only be accessed if excinfo is None. - """ - if self.excinfo is not None: - raise AttributeError(f"{self!r} has no valid result") - # The cast is safe because an exception wasn't raised, hence - # _result has the expected function return type (which may be - # None, that's why a cast and not an assert). - return cast(TResult, self._result) - - @classmethod - def from_call( - cls, - func: Callable[[], TResult], - when: Literal["collect", "setup", "call", "teardown"], - reraise: type[BaseException] | tuple[type[BaseException], ...] | None = None, - ) -> CallInfo[TResult]: - """Call func, wrapping the result in a CallInfo. - - :param func: - The function to call. Called without arguments. - :type func: Callable[[], _pytest.runner.TResult] - :param when: - The phase in which the function is called. - :param reraise: - Exception or exceptions that shall propagate if raised by the - function, instead of being wrapped in the CallInfo. - """ - excinfo = None - start = timing.time() - precise_start = timing.perf_counter() - try: - result: TResult | None = func() - except BaseException: - excinfo = ExceptionInfo.from_current() - if reraise is not None and isinstance(excinfo.value, reraise): - raise - result = None - # use the perf counter - precise_stop = timing.perf_counter() - duration = precise_stop - precise_start - stop = timing.time() - return cls( - start=start, - stop=stop, - duration=duration, - when=when, - result=result, - excinfo=excinfo, - _ispytest=True, - ) - - def __repr__(self) -> str: - if self.excinfo is None: - return f"" - return f"" - - -def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport: - return TestReport.from_item_and_call(item, call) - - -def pytest_make_collect_report(collector: Collector) -> CollectReport: - def collect() -> list[Item | Collector]: - # Before collecting, if this is a Directory, load the conftests. - # If a conftest import fails to load, it is considered a collection - # error of the Directory collector. This is why it's done inside of the - # CallInfo wrapper. - # - # Note: initial conftests are loaded early, not here. - if isinstance(collector, Directory): - collector.config.pluginmanager._loadconftestmodules( - collector.path, - collector.config.getoption("importmode"), - rootpath=collector.config.rootpath, - consider_namespace_packages=collector.config.getini( - "consider_namespace_packages" - ), - ) - - return list(collector.collect()) - - call = CallInfo.from_call( - collect, "collect", reraise=(KeyboardInterrupt, SystemExit) - ) - longrepr: None | tuple[str, int, str] | str | TerminalRepr = None - if not call.excinfo: - outcome: Literal["passed", "skipped", "failed"] = "passed" - else: - skip_exceptions = [Skipped] - unittest = sys.modules.get("unittest") - if unittest is not None: - skip_exceptions.append(unittest.SkipTest) - if isinstance(call.excinfo.value, tuple(skip_exceptions)): - outcome = "skipped" - r_ = collector._repr_failure_py(call.excinfo, "line") - assert isinstance(r_, ExceptionChainRepr), repr(r_) - r = r_.reprcrash - assert r - longrepr = (str(r.path), r.lineno, r.message) - else: - outcome = "failed" - errorinfo = collector.repr_failure(call.excinfo) - if not hasattr(errorinfo, "toterminal"): - assert isinstance(errorinfo, str) - errorinfo = CollectErrorRepr(errorinfo) - longrepr = errorinfo - result = call.result if not call.excinfo else None - rep = CollectReport(collector.nodeid, outcome, longrepr, result) - rep.call = call # type: ignore # see collect_one_node - return rep - - -class SetupState: - """Shared state for setting up/tearing down test items or collectors - in a session. - - Suppose we have a collection tree as follows: - - - - - - - - The SetupState maintains a stack. The stack starts out empty: - - [] - - During the setup phase of item1, setup(item1) is called. What it does - is: - - push session to stack, run session.setup() - push mod1 to stack, run mod1.setup() - push item1 to stack, run item1.setup() - - The stack is: - - [session, mod1, item1] - - While the stack is in this shape, it is allowed to add finalizers to - each of session, mod1, item1 using addfinalizer(). - - During the teardown phase of item1, teardown_exact(item2) is called, - where item2 is the next item to item1. What it does is: - - pop item1 from stack, run its teardowns - pop mod1 from stack, run its teardowns - - mod1 was popped because it ended its purpose with item1. The stack is: - - [session] - - During the setup phase of item2, setup(item2) is called. What it does - is: - - push mod2 to stack, run mod2.setup() - push item2 to stack, run item2.setup() - - Stack: - - [session, mod2, item2] - - During the teardown phase of item2, teardown_exact(None) is called, - because item2 is the last item. What it does is: - - pop item2 from stack, run its teardowns - pop mod2 from stack, run its teardowns - pop session from stack, run its teardowns - - Stack: - - [] - - The end! - """ - - def __init__(self) -> None: - # The stack is in the dict insertion order. - self.stack: dict[ - Node, - tuple[ - # Node's finalizers. - list[Callable[[], object]], - # Node's exception and original traceback, if its setup raised. - tuple[OutcomeException | Exception, types.TracebackType | None] | None, - ], - ] = {} - - def setup(self, item: Item) -> None: - """Setup objects along the collector chain to the item.""" - needed_collectors = item.listchain() - - # If a collector fails its setup, fail its entire subtree of items. - # The setup is not retried for each item - the same exception is used. - for col, (finalizers, exc) in self.stack.items(): - assert col in needed_collectors, "previous item was not torn down properly" - if exc: - raise exc[0].with_traceback(exc[1]) - - for col in needed_collectors[len(self.stack) :]: - assert col not in self.stack - # Push onto the stack. - self.stack[col] = ([col.teardown], None) - try: - col.setup() - except TEST_OUTCOME as exc: - self.stack[col] = (self.stack[col][0], (exc, exc.__traceback__)) - raise - - def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None: - """Attach a finalizer to the given node. - - The node must be currently active in the stack. - """ - assert node and not isinstance(node, tuple) - assert callable(finalizer) - assert node in self.stack, (node, self.stack) - self.stack[node][0].append(finalizer) - - def teardown_exact(self, nextitem: Item | None) -> None: - """Teardown the current stack up until reaching nodes that nextitem - also descends from. - - When nextitem is None (meaning we're at the last item), the entire - stack is torn down. - """ - needed_collectors = nextitem and nextitem.listchain() or [] - exceptions: list[BaseException] = [] - while self.stack: - if list(self.stack.keys()) == needed_collectors[: len(self.stack)]: - break - node, (finalizers, _) = self.stack.popitem() - these_exceptions = [] - while finalizers: - fin = finalizers.pop() - try: - fin() - except TEST_OUTCOME as e: - these_exceptions.append(e) - - if len(these_exceptions) == 1: - exceptions.extend(these_exceptions) - elif these_exceptions: - msg = f"errors while tearing down {node!r}" - exceptions.append(BaseExceptionGroup(msg, these_exceptions[::-1])) - - if len(exceptions) == 1: - raise exceptions[0] - elif exceptions: - raise BaseExceptionGroup("errors during test teardown", exceptions[::-1]) - if nextitem is None: - assert not self.stack - - -def collect_one_node(collector: Collector) -> CollectReport: - ihook = collector.ihook - ihook.pytest_collectstart(collector=collector) - rep: CollectReport = ihook.pytest_make_collect_report(collector=collector) - call = rep.__dict__.pop("call", None) - if call and check_interactive_exception(call, rep): - ihook.pytest_exception_interact(node=collector, call=call, report=rep) - return rep diff --git a/.venv/lib/python3.12/site-packages/_pytest/scope.py b/.venv/lib/python3.12/site-packages/_pytest/scope.py deleted file mode 100644 index 976a3ba2..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/scope.py +++ /dev/null @@ -1,91 +0,0 @@ -""" -Scope definition and related utilities. - -Those are defined here, instead of in the 'fixtures' module because -their use is spread across many other pytest modules, and centralizing it in 'fixtures' -would cause circular references. - -Also this makes the module light to import, as it should. -""" - -from __future__ import annotations - -from enum import Enum -from functools import total_ordering -from typing import Literal - - -_ScopeName = Literal["session", "package", "module", "class", "function"] - - -@total_ordering -class Scope(Enum): - """ - Represents one of the possible fixture scopes in pytest. - - Scopes are ordered from lower to higher, that is: - - ->>> higher ->>> - - Function < Class < Module < Package < Session - - <<<- lower <<<- - """ - - # Scopes need to be listed from lower to higher. - Function: _ScopeName = "function" - Class: _ScopeName = "class" - Module: _ScopeName = "module" - Package: _ScopeName = "package" - Session: _ScopeName = "session" - - def next_lower(self) -> Scope: - """Return the next lower scope.""" - index = _SCOPE_INDICES[self] - if index == 0: - raise ValueError(f"{self} is the lower-most scope") - return _ALL_SCOPES[index - 1] - - def next_higher(self) -> Scope: - """Return the next higher scope.""" - index = _SCOPE_INDICES[self] - if index == len(_SCOPE_INDICES) - 1: - raise ValueError(f"{self} is the upper-most scope") - return _ALL_SCOPES[index + 1] - - def __lt__(self, other: Scope) -> bool: - self_index = _SCOPE_INDICES[self] - other_index = _SCOPE_INDICES[other] - return self_index < other_index - - @classmethod - def from_user( - cls, scope_name: _ScopeName, descr: str, where: str | None = None - ) -> Scope: - """ - Given a scope name from the user, return the equivalent Scope enum. Should be used - whenever we want to convert a user provided scope name to its enum object. - - If the scope name is invalid, construct a user friendly message and call pytest.fail. - """ - from _pytest.outcomes import fail - - try: - # Holding this reference is necessary for mypy at the moment. - scope = Scope(scope_name) - except ValueError: - fail( - "{} {}got an unexpected scope value '{}'".format( - descr, f"from {where} " if where else "", scope_name - ), - pytrace=False, - ) - return scope - - -_ALL_SCOPES = list(Scope) -_SCOPE_INDICES = {scope: index for index, scope in enumerate(_ALL_SCOPES)} - - -# Ordered list of scopes which can contain many tests (in practice all except Function). -HIGH_SCOPES = [x for x in Scope if x is not Scope.Function] diff --git a/.venv/lib/python3.12/site-packages/_pytest/setuponly.py b/.venv/lib/python3.12/site-packages/_pytest/setuponly.py deleted file mode 100644 index de297f40..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/setuponly.py +++ /dev/null @@ -1,102 +0,0 @@ -from __future__ import annotations - -from typing import Generator - -from _pytest._io.saferepr import saferepr -from _pytest.config import Config -from _pytest.config import ExitCode -from _pytest.config.argparsing import Parser -from _pytest.fixtures import FixtureDef -from _pytest.fixtures import SubRequest -from _pytest.scope import Scope -import pytest - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("debugconfig") - group.addoption( - "--setuponly", - "--setup-only", - action="store_true", - help="Only setup fixtures, do not execute tests", - ) - group.addoption( - "--setupshow", - "--setup-show", - action="store_true", - help="Show setup of fixtures while executing tests", - ) - - -@pytest.hookimpl(wrapper=True) -def pytest_fixture_setup( - fixturedef: FixtureDef[object], request: SubRequest -) -> Generator[None, object, object]: - try: - return (yield) - finally: - if request.config.option.setupshow: - if hasattr(request, "param"): - # Save the fixture parameter so ._show_fixture_action() can - # display it now and during the teardown (in .finish()). - if fixturedef.ids: - if callable(fixturedef.ids): - param = fixturedef.ids(request.param) - else: - param = fixturedef.ids[request.param_index] - else: - param = request.param - fixturedef.cached_param = param # type: ignore[attr-defined] - _show_fixture_action(fixturedef, request.config, "SETUP") - - -def pytest_fixture_post_finalizer( - fixturedef: FixtureDef[object], request: SubRequest -) -> None: - if fixturedef.cached_result is not None: - config = request.config - if config.option.setupshow: - _show_fixture_action(fixturedef, request.config, "TEARDOWN") - if hasattr(fixturedef, "cached_param"): - del fixturedef.cached_param - - -def _show_fixture_action( - fixturedef: FixtureDef[object], config: Config, msg: str -) -> None: - capman = config.pluginmanager.getplugin("capturemanager") - if capman: - capman.suspend_global_capture() - - tw = config.get_terminal_writer() - tw.line() - # Use smaller indentation the higher the scope: Session = 0, Package = 1, etc. - scope_indent = list(reversed(Scope)).index(fixturedef._scope) - tw.write(" " * 2 * scope_indent) - tw.write( - "{step} {scope} {fixture}".format( # noqa: UP032 (Readability) - step=msg.ljust(8), # align the output to TEARDOWN - scope=fixturedef.scope[0].upper(), - fixture=fixturedef.argname, - ) - ) - - if msg == "SETUP": - deps = sorted(arg for arg in fixturedef.argnames if arg != "request") - if deps: - tw.write(" (fixtures used: {})".format(", ".join(deps))) - - if hasattr(fixturedef, "cached_param"): - tw.write(f"[{saferepr(fixturedef.cached_param, maxsize=42)}]") - - tw.flush() - - if capman: - capman.resume_global_capture() - - -@pytest.hookimpl(tryfirst=True) -def pytest_cmdline_main(config: Config) -> int | ExitCode | None: - if config.option.setuponly: - config.option.setupshow = True - return None diff --git a/.venv/lib/python3.12/site-packages/_pytest/setupplan.py b/.venv/lib/python3.12/site-packages/_pytest/setupplan.py deleted file mode 100644 index 4e124cce..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/setupplan.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -from _pytest.config import Config -from _pytest.config import ExitCode -from _pytest.config.argparsing import Parser -from _pytest.fixtures import FixtureDef -from _pytest.fixtures import SubRequest -import pytest - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("debugconfig") - group.addoption( - "--setupplan", - "--setup-plan", - action="store_true", - help="Show what fixtures and tests would be executed but " - "don't execute anything", - ) - - -@pytest.hookimpl(tryfirst=True) -def pytest_fixture_setup( - fixturedef: FixtureDef[object], request: SubRequest -) -> object | None: - # Will return a dummy fixture if the setuponly option is provided. - if request.config.option.setupplan: - my_cache_key = fixturedef.cache_key(request) - fixturedef.cached_result = (None, my_cache_key, None) - return fixturedef.cached_result - return None - - -@pytest.hookimpl(tryfirst=True) -def pytest_cmdline_main(config: Config) -> int | ExitCode | None: - if config.option.setupplan: - config.option.setuponly = True - config.option.setupshow = True - return None diff --git a/.venv/lib/python3.12/site-packages/_pytest/skipping.py b/.venv/lib/python3.12/site-packages/_pytest/skipping.py deleted file mode 100644 index 9818be2a..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/skipping.py +++ /dev/null @@ -1,301 +0,0 @@ -# mypy: allow-untyped-defs -"""Support for skip/xfail functions and markers.""" - -from __future__ import annotations - -from collections.abc import Mapping -import dataclasses -import os -import platform -import sys -import traceback -from typing import Generator -from typing import Optional - -from _pytest.config import Config -from _pytest.config import hookimpl -from _pytest.config.argparsing import Parser -from _pytest.mark.structures import Mark -from _pytest.nodes import Item -from _pytest.outcomes import fail -from _pytest.outcomes import skip -from _pytest.outcomes import xfail -from _pytest.reports import BaseReport -from _pytest.reports import TestReport -from _pytest.runner import CallInfo -from _pytest.stash import StashKey - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") - group.addoption( - "--runxfail", - action="store_true", - dest="runxfail", - default=False, - help="Report the results of xfail tests as if they were not marked", - ) - - parser.addini( - "xfail_strict", - "Default for the strict parameter of xfail " - "markers when not given explicitly (default: False)", - default=False, - type="bool", - ) - - -def pytest_configure(config: Config) -> None: - if config.option.runxfail: - # yay a hack - import pytest - - old = pytest.xfail - config.add_cleanup(lambda: setattr(pytest, "xfail", old)) - - def nop(*args, **kwargs): - pass - - nop.Exception = xfail.Exception # type: ignore[attr-defined] - setattr(pytest, "xfail", nop) - - config.addinivalue_line( - "markers", - "skip(reason=None): skip the given test function with an optional reason. " - 'Example: skip(reason="no way of currently testing this") skips the ' - "test.", - ) - config.addinivalue_line( - "markers", - "skipif(condition, ..., *, reason=...): " - "skip the given test function if any of the conditions evaluate to True. " - "Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. " - "See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif", - ) - config.addinivalue_line( - "markers", - "xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): " - "mark the test function as an expected failure if any of the conditions " - "evaluate to True. Optionally specify a reason for better reporting " - "and run=False if you don't even want to execute the test function. " - "If only specific exception(s) are expected, you can list them in " - "raises, and if the test fails in other ways, it will be reported as " - "a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail", - ) - - -def evaluate_condition(item: Item, mark: Mark, condition: object) -> tuple[bool, str]: - """Evaluate a single skipif/xfail condition. - - If an old-style string condition is given, it is eval()'d, otherwise the - condition is bool()'d. If this fails, an appropriately formatted pytest.fail - is raised. - - Returns (result, reason). The reason is only relevant if the result is True. - """ - # String condition. - if isinstance(condition, str): - globals_ = { - "os": os, - "sys": sys, - "platform": platform, - "config": item.config, - } - for dictionary in reversed( - item.ihook.pytest_markeval_namespace(config=item.config) - ): - if not isinstance(dictionary, Mapping): - raise ValueError( - f"pytest_markeval_namespace() needs to return a dict, got {dictionary!r}" - ) - globals_.update(dictionary) - if hasattr(item, "obj"): - globals_.update(item.obj.__globals__) - try: - filename = f"<{mark.name} condition>" - condition_code = compile(condition, filename, "eval") - result = eval(condition_code, globals_) - except SyntaxError as exc: - msglines = [ - f"Error evaluating {mark.name!r} condition", - " " + condition, - " " + " " * (exc.offset or 0) + "^", - "SyntaxError: invalid syntax", - ] - fail("\n".join(msglines), pytrace=False) - except Exception as exc: - msglines = [ - f"Error evaluating {mark.name!r} condition", - " " + condition, - *traceback.format_exception_only(type(exc), exc), - ] - fail("\n".join(msglines), pytrace=False) - - # Boolean condition. - else: - try: - result = bool(condition) - except Exception as exc: - msglines = [ - f"Error evaluating {mark.name!r} condition as a boolean", - *traceback.format_exception_only(type(exc), exc), - ] - fail("\n".join(msglines), pytrace=False) - - reason = mark.kwargs.get("reason", None) - if reason is None: - if isinstance(condition, str): - reason = "condition: " + condition - else: - # XXX better be checked at collection time - msg = ( - f"Error evaluating {mark.name!r}: " - + "you need to specify reason=STRING when using booleans as conditions." - ) - fail(msg, pytrace=False) - - return result, reason - - -@dataclasses.dataclass(frozen=True) -class Skip: - """The result of evaluate_skip_marks().""" - - reason: str = "unconditional skip" - - -def evaluate_skip_marks(item: Item) -> Skip | None: - """Evaluate skip and skipif marks on item, returning Skip if triggered.""" - for mark in item.iter_markers(name="skipif"): - if "condition" not in mark.kwargs: - conditions = mark.args - else: - conditions = (mark.kwargs["condition"],) - - # Unconditional. - if not conditions: - reason = mark.kwargs.get("reason", "") - return Skip(reason) - - # If any of the conditions are true. - for condition in conditions: - result, reason = evaluate_condition(item, mark, condition) - if result: - return Skip(reason) - - for mark in item.iter_markers(name="skip"): - try: - return Skip(*mark.args, **mark.kwargs) - except TypeError as e: - raise TypeError(str(e) + " - maybe you meant pytest.mark.skipif?") from None - - return None - - -@dataclasses.dataclass(frozen=True) -class Xfail: - """The result of evaluate_xfail_marks().""" - - __slots__ = ("reason", "run", "strict", "raises") - - reason: str - run: bool - strict: bool - raises: tuple[type[BaseException], ...] | None - - -def evaluate_xfail_marks(item: Item) -> Xfail | None: - """Evaluate xfail marks on item, returning Xfail if triggered.""" - for mark in item.iter_markers(name="xfail"): - run = mark.kwargs.get("run", True) - strict = mark.kwargs.get("strict", item.config.getini("xfail_strict")) - raises = mark.kwargs.get("raises", None) - if "condition" not in mark.kwargs: - conditions = mark.args - else: - conditions = (mark.kwargs["condition"],) - - # Unconditional. - if not conditions: - reason = mark.kwargs.get("reason", "") - return Xfail(reason, run, strict, raises) - - # If any of the conditions are true. - for condition in conditions: - result, reason = evaluate_condition(item, mark, condition) - if result: - return Xfail(reason, run, strict, raises) - - return None - - -# Saves the xfail mark evaluation. Can be refreshed during call if None. -xfailed_key = StashKey[Optional[Xfail]]() - - -@hookimpl(tryfirst=True) -def pytest_runtest_setup(item: Item) -> None: - skipped = evaluate_skip_marks(item) - if skipped: - raise skip.Exception(skipped.reason, _use_item_location=True) - - item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) - if xfailed and not item.config.option.runxfail and not xfailed.run: - xfail("[NOTRUN] " + xfailed.reason) - - -@hookimpl(wrapper=True) -def pytest_runtest_call(item: Item) -> Generator[None]: - xfailed = item.stash.get(xfailed_key, None) - if xfailed is None: - item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) - - if xfailed and not item.config.option.runxfail and not xfailed.run: - xfail("[NOTRUN] " + xfailed.reason) - - try: - return (yield) - finally: - # The test run may have added an xfail mark dynamically. - xfailed = item.stash.get(xfailed_key, None) - if xfailed is None: - item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) - - -@hookimpl(wrapper=True) -def pytest_runtest_makereport( - item: Item, call: CallInfo[None] -) -> Generator[None, TestReport, TestReport]: - rep = yield - xfailed = item.stash.get(xfailed_key, None) - if item.config.option.runxfail: - pass # don't interfere - elif call.excinfo and isinstance(call.excinfo.value, xfail.Exception): - assert call.excinfo.value.msg is not None - rep.wasxfail = "reason: " + call.excinfo.value.msg - rep.outcome = "skipped" - elif not rep.skipped and xfailed: - if call.excinfo: - raises = xfailed.raises - if raises is not None and not isinstance(call.excinfo.value, raises): - rep.outcome = "failed" - else: - rep.outcome = "skipped" - rep.wasxfail = xfailed.reason - elif call.when == "call": - if xfailed.strict: - rep.outcome = "failed" - rep.longrepr = "[XPASS(strict)] " + xfailed.reason - else: - rep.outcome = "passed" - rep.wasxfail = xfailed.reason - return rep - - -def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str] | None: - if hasattr(report, "wasxfail"): - if report.skipped: - return "xfailed", "x", "XFAIL" - elif report.passed: - return "xpassed", "X", "XPASS" - return None diff --git a/.venv/lib/python3.12/site-packages/_pytest/stash.py b/.venv/lib/python3.12/site-packages/_pytest/stash.py deleted file mode 100644 index 6a9ff884..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/stash.py +++ /dev/null @@ -1,116 +0,0 @@ -from __future__ import annotations - -from typing import Any -from typing import cast -from typing import Generic -from typing import TypeVar - - -__all__ = ["Stash", "StashKey"] - - -T = TypeVar("T") -D = TypeVar("D") - - -class StashKey(Generic[T]): - """``StashKey`` is an object used as a key to a :class:`Stash`. - - A ``StashKey`` is associated with the type ``T`` of the value of the key. - - A ``StashKey`` is unique and cannot conflict with another key. - - .. versionadded:: 7.0 - """ - - __slots__ = () - - -class Stash: - r"""``Stash`` is a type-safe heterogeneous mutable mapping that - allows keys and value types to be defined separately from - where it (the ``Stash``) is created. - - Usually you will be given an object which has a ``Stash``, for example - :class:`~pytest.Config` or a :class:`~_pytest.nodes.Node`: - - .. code-block:: python - - stash: Stash = some_object.stash - - If a module or plugin wants to store data in this ``Stash``, it creates - :class:`StashKey`\s for its keys (at the module level): - - .. code-block:: python - - # At the top-level of the module - some_str_key = StashKey[str]() - some_bool_key = StashKey[bool]() - - To store information: - - .. code-block:: python - - # Value type must match the key. - stash[some_str_key] = "value" - stash[some_bool_key] = True - - To retrieve the information: - - .. code-block:: python - - # The static type of some_str is str. - some_str = stash[some_str_key] - # The static type of some_bool is bool. - some_bool = stash[some_bool_key] - - .. versionadded:: 7.0 - """ - - __slots__ = ("_storage",) - - def __init__(self) -> None: - self._storage: dict[StashKey[Any], object] = {} - - def __setitem__(self, key: StashKey[T], value: T) -> None: - """Set a value for key.""" - self._storage[key] = value - - def __getitem__(self, key: StashKey[T]) -> T: - """Get the value for key. - - Raises ``KeyError`` if the key wasn't set before. - """ - return cast(T, self._storage[key]) - - def get(self, key: StashKey[T], default: D) -> T | D: - """Get the value for key, or return default if the key wasn't set - before.""" - try: - return self[key] - except KeyError: - return default - - def setdefault(self, key: StashKey[T], default: T) -> T: - """Return the value of key if already set, otherwise set the value - of key to default and return default.""" - try: - return self[key] - except KeyError: - self[key] = default - return default - - def __delitem__(self, key: StashKey[T]) -> None: - """Delete the value for key. - - Raises ``KeyError`` if the key wasn't set before. - """ - del self._storage[key] - - def __contains__(self, key: StashKey[T]) -> bool: - """Return whether key was set.""" - return key in self._storage - - def __len__(self) -> int: - """Return how many items exist in the stash.""" - return len(self._storage) diff --git a/.venv/lib/python3.12/site-packages/_pytest/stepwise.py b/.venv/lib/python3.12/site-packages/_pytest/stepwise.py deleted file mode 100644 index c7860808..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/stepwise.py +++ /dev/null @@ -1,125 +0,0 @@ -from __future__ import annotations - -from _pytest import nodes -from _pytest.cacheprovider import Cache -from _pytest.config import Config -from _pytest.config.argparsing import Parser -from _pytest.main import Session -from _pytest.reports import TestReport - - -STEPWISE_CACHE_DIR = "cache/stepwise" - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("general") - group.addoption( - "--sw", - "--stepwise", - action="store_true", - default=False, - dest="stepwise", - help="Exit on test failure and continue from last failing test next time", - ) - group.addoption( - "--sw-skip", - "--stepwise-skip", - action="store_true", - default=False, - dest="stepwise_skip", - help="Ignore the first failing test but stop on the next failing test. " - "Implicitly enables --stepwise.", - ) - - -def pytest_configure(config: Config) -> None: - if config.option.stepwise_skip: - # allow --stepwise-skip to work on its own merits. - config.option.stepwise = True - if config.getoption("stepwise"): - config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin") - - -def pytest_sessionfinish(session: Session) -> None: - if not session.config.getoption("stepwise"): - assert session.config.cache is not None - if hasattr(session.config, "workerinput"): - # Do not update cache if this process is a xdist worker to prevent - # race conditions (#10641). - return - # Clear the list of failing tests if the plugin is not active. - session.config.cache.set(STEPWISE_CACHE_DIR, []) - - -class StepwisePlugin: - def __init__(self, config: Config) -> None: - self.config = config - self.session: Session | None = None - self.report_status = "" - assert config.cache is not None - self.cache: Cache = config.cache - self.lastfailed: str | None = self.cache.get(STEPWISE_CACHE_DIR, None) - self.skip: bool = config.getoption("stepwise_skip") - - def pytest_sessionstart(self, session: Session) -> None: - self.session = session - - def pytest_collection_modifyitems( - self, config: Config, items: list[nodes.Item] - ) -> None: - if not self.lastfailed: - self.report_status = "no previously failed tests, not skipping." - return - - # check all item nodes until we find a match on last failed - failed_index = None - for index, item in enumerate(items): - if item.nodeid == self.lastfailed: - failed_index = index - break - - # If the previously failed test was not found among the test items, - # do not skip any tests. - if failed_index is None: - self.report_status = "previously failed test not found, not skipping." - else: - self.report_status = f"skipping {failed_index} already passed items." - deselected = items[:failed_index] - del items[:failed_index] - config.hook.pytest_deselected(items=deselected) - - def pytest_runtest_logreport(self, report: TestReport) -> None: - if report.failed: - if self.skip: - # Remove test from the failed ones (if it exists) and unset the skip option - # to make sure the following tests will not be skipped. - if report.nodeid == self.lastfailed: - self.lastfailed = None - - self.skip = False - else: - # Mark test as the last failing and interrupt the test session. - self.lastfailed = report.nodeid - assert self.session is not None - self.session.shouldstop = ( - "Test failed, continuing from this test next run." - ) - - else: - # If the test was actually run and did pass. - if report.when == "call": - # Remove test from the failed ones, if exists. - if report.nodeid == self.lastfailed: - self.lastfailed = None - - def pytest_report_collectionfinish(self) -> str | None: - if self.config.get_verbosity() >= 0 and self.report_status: - return f"stepwise: {self.report_status}" - return None - - def pytest_sessionfinish(self) -> None: - if hasattr(self.config, "workerinput"): - # Do not update cache if this process is a xdist worker to prevent - # race conditions (#10641). - return - self.cache.set(STEPWISE_CACHE_DIR, self.lastfailed) diff --git a/.venv/lib/python3.12/site-packages/_pytest/terminal.py b/.venv/lib/python3.12/site-packages/_pytest/terminal.py deleted file mode 100644 index ed267bf5..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/terminal.py +++ /dev/null @@ -1,1577 +0,0 @@ -# mypy: allow-untyped-defs -"""Terminal reporting of the full testing process. - -This is a good source for looking at the various reporting hooks. -""" - -from __future__ import annotations - -import argparse -from collections import Counter -import dataclasses -import datetime -from functools import partial -import inspect -from pathlib import Path -import platform -import sys -import textwrap -from typing import Any -from typing import Callable -from typing import ClassVar -from typing import final -from typing import Generator -from typing import Literal -from typing import Mapping -from typing import NamedTuple -from typing import Sequence -from typing import TextIO -from typing import TYPE_CHECKING -import warnings - -import pluggy - -from _pytest import nodes -from _pytest import timing -from _pytest._code import ExceptionInfo -from _pytest._code.code import ExceptionRepr -from _pytest._io import TerminalWriter -from _pytest._io.wcwidth import wcswidth -import _pytest._version -from _pytest.assertion.util import running_on_ci -from _pytest.config import _PluggyPlugin -from _pytest.config import Config -from _pytest.config import ExitCode -from _pytest.config import hookimpl -from _pytest.config.argparsing import Parser -from _pytest.nodes import Item -from _pytest.nodes import Node -from _pytest.pathlib import absolutepath -from _pytest.pathlib import bestrelpath -from _pytest.reports import BaseReport -from _pytest.reports import CollectReport -from _pytest.reports import TestReport - - -if TYPE_CHECKING: - from _pytest.main import Session - - -REPORT_COLLECTING_RESOLUTION = 0.5 - -KNOWN_TYPES = ( - "failed", - "passed", - "skipped", - "deselected", - "xfailed", - "xpassed", - "warnings", - "error", -) - -_REPORTCHARS_DEFAULT = "fE" - - -class MoreQuietAction(argparse.Action): - """A modified copy of the argparse count action which counts down and updates - the legacy quiet attribute at the same time. - - Used to unify verbosity handling. - """ - - def __init__( - self, - option_strings: Sequence[str], - dest: str, - default: object = None, - required: bool = False, - help: str | None = None, - ) -> None: - super().__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - default=default, - required=required, - help=help, - ) - - def __call__( - self, - parser: argparse.ArgumentParser, - namespace: argparse.Namespace, - values: str | Sequence[object] | None, - option_string: str | None = None, - ) -> None: - new_count = getattr(namespace, self.dest, 0) - 1 - setattr(namespace, self.dest, new_count) - # todo Deprecate config.quiet - namespace.quiet = getattr(namespace, "quiet", 0) + 1 - - -class TestShortLogReport(NamedTuple): - """Used to store the test status result category, shortletter and verbose word. - For example ``"rerun", "R", ("RERUN", {"yellow": True})``. - - :ivar category: - The class of result, for example ``“passed”``, ``“skipped”``, ``“error”``, or the empty string. - - :ivar letter: - The short letter shown as testing progresses, for example ``"."``, ``"s"``, ``"E"``, or the empty string. - - :ivar word: - Verbose word is shown as testing progresses in verbose mode, for example ``"PASSED"``, ``"SKIPPED"``, - ``"ERROR"``, or the empty string. - """ - - category: str - letter: str - word: str | tuple[str, Mapping[str, bool]] - - -def pytest_addoption(parser: Parser) -> None: - group = parser.getgroup("terminal reporting", "Reporting", after="general") - group._addoption( - "-v", - "--verbose", - action="count", - default=0, - dest="verbose", - help="Increase verbosity", - ) - group._addoption( - "--no-header", - action="store_true", - default=False, - dest="no_header", - help="Disable header", - ) - group._addoption( - "--no-summary", - action="store_true", - default=False, - dest="no_summary", - help="Disable summary", - ) - group._addoption( - "--no-fold-skipped", - action="store_false", - dest="fold_skipped", - default=True, - help="Do not fold skipped tests in short summary.", - ) - group._addoption( - "-q", - "--quiet", - action=MoreQuietAction, - default=0, - dest="verbose", - help="Decrease verbosity", - ) - group._addoption( - "--verbosity", - dest="verbose", - type=int, - default=0, - help="Set verbosity. Default: 0.", - ) - group._addoption( - "-r", - action="store", - dest="reportchars", - default=_REPORTCHARS_DEFAULT, - metavar="chars", - help="Show extra test summary info as specified by chars: (f)ailed, " - "(E)rror, (s)kipped, (x)failed, (X)passed, " - "(p)assed, (P)assed with output, (a)ll except passed (p/P), or (A)ll. " - "(w)arnings are enabled by default (see --disable-warnings), " - "'N' can be used to reset the list. (default: 'fE').", - ) - group._addoption( - "--disable-warnings", - "--disable-pytest-warnings", - default=False, - dest="disable_warnings", - action="store_true", - help="Disable warnings summary", - ) - group._addoption( - "-l", - "--showlocals", - action="store_true", - dest="showlocals", - default=False, - help="Show locals in tracebacks (disabled by default)", - ) - group._addoption( - "--no-showlocals", - action="store_false", - dest="showlocals", - help="Hide locals in tracebacks (negate --showlocals passed through addopts)", - ) - group._addoption( - "--tb", - metavar="style", - action="store", - dest="tbstyle", - default="auto", - choices=["auto", "long", "short", "no", "line", "native"], - help="Traceback print mode (auto/long/short/line/native/no)", - ) - group._addoption( - "--xfail-tb", - action="store_true", - dest="xfail_tb", - default=False, - help="Show tracebacks for xfail (as long as --tb != no)", - ) - group._addoption( - "--show-capture", - action="store", - dest="showcapture", - choices=["no", "stdout", "stderr", "log", "all"], - default="all", - help="Controls how captured stdout/stderr/log is shown on failed tests. " - "Default: all.", - ) - group._addoption( - "--fulltrace", - "--full-trace", - action="store_true", - default=False, - help="Don't cut any tracebacks (default is to cut)", - ) - group._addoption( - "--color", - metavar="color", - action="store", - dest="color", - default="auto", - choices=["yes", "no", "auto"], - help="Color terminal output (yes/no/auto)", - ) - group._addoption( - "--code-highlight", - default="yes", - choices=["yes", "no"], - help="Whether code should be highlighted (only if --color is also enabled). " - "Default: yes.", - ) - - parser.addini( - "console_output_style", - help='Console output: "classic", or with additional progress information ' - '("progress" (percentage) | "count" | "progress-even-when-capture-no" (forces ' - "progress even when capture=no)", - default="progress", - ) - Config._add_verbosity_ini( - parser, - Config.VERBOSITY_TEST_CASES, - help=( - "Specify a verbosity level for test case execution, overriding the main level. " - "Higher levels will provide more detailed information about each test case executed." - ), - ) - - -def pytest_configure(config: Config) -> None: - reporter = TerminalReporter(config, sys.stdout) - config.pluginmanager.register(reporter, "terminalreporter") - if config.option.debug or config.option.traceconfig: - - def mywriter(tags, args): - msg = " ".join(map(str, args)) - reporter.write_line("[traceconfig] " + msg) - - config.trace.root.setprocessor("pytest:config", mywriter) - - -def getreportopt(config: Config) -> str: - reportchars: str = config.option.reportchars - - old_aliases = {"F", "S"} - reportopts = "" - for char in reportchars: - if char in old_aliases: - char = char.lower() - if char == "a": - reportopts = "sxXEf" - elif char == "A": - reportopts = "PpsxXEf" - elif char == "N": - reportopts = "" - elif char not in reportopts: - reportopts += char - - if not config.option.disable_warnings and "w" not in reportopts: - reportopts = "w" + reportopts - elif config.option.disable_warnings and "w" in reportopts: - reportopts = reportopts.replace("w", "") - - return reportopts - - -@hookimpl(trylast=True) # after _pytest.runner -def pytest_report_teststatus(report: BaseReport) -> tuple[str, str, str]: - letter = "F" - if report.passed: - letter = "." - elif report.skipped: - letter = "s" - - outcome: str = report.outcome - if report.when in ("collect", "setup", "teardown") and outcome == "failed": - outcome = "error" - letter = "E" - - return outcome, letter, outcome.upper() - - -@dataclasses.dataclass -class WarningReport: - """Simple structure to hold warnings information captured by ``pytest_warning_recorded``. - - :ivar str message: - User friendly message about the warning. - :ivar str|None nodeid: - nodeid that generated the warning (see ``get_location``). - :ivar tuple fslocation: - File system location of the source of the warning (see ``get_location``). - """ - - message: str - nodeid: str | None = None - fslocation: tuple[str, int] | None = None - - count_towards_summary: ClassVar = True - - def get_location(self, config: Config) -> str | None: - """Return the more user-friendly information about the location of a warning, or None.""" - if self.nodeid: - return self.nodeid - if self.fslocation: - filename, linenum = self.fslocation - relpath = bestrelpath(config.invocation_params.dir, absolutepath(filename)) - return f"{relpath}:{linenum}" - return None - - -@final -class TerminalReporter: - def __init__(self, config: Config, file: TextIO | None = None) -> None: - import _pytest.config - - self.config = config - self._numcollected = 0 - self._session: Session | None = None - self._showfspath: bool | None = None - - self.stats: dict[str, list[Any]] = {} - self._main_color: str | None = None - self._known_types: list[str] | None = None - self.startpath = config.invocation_params.dir - if file is None: - file = sys.stdout - self._tw = _pytest.config.create_terminal_writer(config, file) - self._screen_width = self._tw.fullwidth - self.currentfspath: None | Path | str | int = None - self.reportchars = getreportopt(config) - self.foldskipped = config.option.fold_skipped - self.hasmarkup = self._tw.hasmarkup - self.isatty = file.isatty() - self._progress_nodeids_reported: set[str] = set() - self._show_progress_info = self._determine_show_progress_info() - self._collect_report_last_write: float | None = None - self._already_displayed_warnings: int | None = None - self._keyboardinterrupt_memo: ExceptionRepr | None = None - - def _determine_show_progress_info(self) -> Literal["progress", "count", False]: - """Return whether we should display progress information based on the current config.""" - # do not show progress if we are not capturing output (#3038) unless explicitly - # overridden by progress-even-when-capture-no - if ( - self.config.getoption("capture", "no") == "no" - and self.config.getini("console_output_style") - != "progress-even-when-capture-no" - ): - return False - # do not show progress if we are showing fixture setup/teardown - if self.config.getoption("setupshow", False): - return False - cfg: str = self.config.getini("console_output_style") - if cfg in {"progress", "progress-even-when-capture-no"}: - return "progress" - elif cfg == "count": - return "count" - else: - return False - - @property - def verbosity(self) -> int: - verbosity: int = self.config.option.verbose - return verbosity - - @property - def showheader(self) -> bool: - return self.verbosity >= 0 - - @property - def no_header(self) -> bool: - return bool(self.config.option.no_header) - - @property - def no_summary(self) -> bool: - return bool(self.config.option.no_summary) - - @property - def showfspath(self) -> bool: - if self._showfspath is None: - return self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) >= 0 - return self._showfspath - - @showfspath.setter - def showfspath(self, value: bool | None) -> None: - self._showfspath = value - - @property - def showlongtestinfo(self) -> bool: - return self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) > 0 - - def hasopt(self, char: str) -> bool: - char = {"xfailed": "x", "skipped": "s"}.get(char, char) - return char in self.reportchars - - def write_fspath_result(self, nodeid: str, res: str, **markup: bool) -> None: - fspath = self.config.rootpath / nodeid.split("::")[0] - if self.currentfspath is None or fspath != self.currentfspath: - if self.currentfspath is not None and self._show_progress_info: - self._write_progress_information_filling_space() - self.currentfspath = fspath - relfspath = bestrelpath(self.startpath, fspath) - self._tw.line() - self._tw.write(relfspath + " ") - self._tw.write(res, flush=True, **markup) - - def write_ensure_prefix(self, prefix: str, extra: str = "", **kwargs) -> None: - if self.currentfspath != prefix: - self._tw.line() - self.currentfspath = prefix - self._tw.write(prefix) - if extra: - self._tw.write(extra, **kwargs) - self.currentfspath = -2 - - def ensure_newline(self) -> None: - if self.currentfspath: - self._tw.line() - self.currentfspath = None - - def wrap_write( - self, - content: str, - *, - flush: bool = False, - margin: int = 8, - line_sep: str = "\n", - **markup: bool, - ) -> None: - """Wrap message with margin for progress info.""" - width_of_current_line = self._tw.width_of_current_line - wrapped = line_sep.join( - textwrap.wrap( - " " * width_of_current_line + content, - width=self._screen_width - margin, - drop_whitespace=True, - replace_whitespace=False, - ), - ) - wrapped = wrapped[width_of_current_line:] - self._tw.write(wrapped, flush=flush, **markup) - - def write(self, content: str, *, flush: bool = False, **markup: bool) -> None: - self._tw.write(content, flush=flush, **markup) - - def flush(self) -> None: - self._tw.flush() - - def write_line(self, line: str | bytes, **markup: bool) -> None: - if not isinstance(line, str): - line = str(line, errors="replace") - self.ensure_newline() - self._tw.line(line, **markup) - - def rewrite(self, line: str, **markup: bool) -> None: - """Rewinds the terminal cursor to the beginning and writes the given line. - - :param erase: - If True, will also add spaces until the full terminal width to ensure - previous lines are properly erased. - - The rest of the keyword arguments are markup instructions. - """ - erase = markup.pop("erase", False) - if erase: - fill_count = self._tw.fullwidth - len(line) - 1 - fill = " " * fill_count - else: - fill = "" - line = str(line) - self._tw.write("\r" + line + fill, **markup) - - def write_sep( - self, - sep: str, - title: str | None = None, - fullwidth: int | None = None, - **markup: bool, - ) -> None: - self.ensure_newline() - self._tw.sep(sep, title, fullwidth, **markup) - - def section(self, title: str, sep: str = "=", **kw: bool) -> None: - self._tw.sep(sep, title, **kw) - - def line(self, msg: str, **kw: bool) -> None: - self._tw.line(msg, **kw) - - def _add_stats(self, category: str, items: Sequence[Any]) -> None: - set_main_color = category not in self.stats - self.stats.setdefault(category, []).extend(items) - if set_main_color: - self._set_main_color() - - def pytest_internalerror(self, excrepr: ExceptionRepr) -> bool: - for line in str(excrepr).split("\n"): - self.write_line("INTERNALERROR> " + line) - return True - - def pytest_warning_recorded( - self, - warning_message: warnings.WarningMessage, - nodeid: str, - ) -> None: - from _pytest.warnings import warning_record_to_str - - fslocation = warning_message.filename, warning_message.lineno - message = warning_record_to_str(warning_message) - - warning_report = WarningReport( - fslocation=fslocation, message=message, nodeid=nodeid - ) - self._add_stats("warnings", [warning_report]) - - def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None: - if self.config.option.traceconfig: - msg = f"PLUGIN registered: {plugin}" - # XXX This event may happen during setup/teardown time - # which unfortunately captures our output here - # which garbles our output if we use self.write_line. - self.write_line(msg) - - def pytest_deselected(self, items: Sequence[Item]) -> None: - self._add_stats("deselected", items) - - def pytest_runtest_logstart( - self, nodeid: str, location: tuple[str, int | None, str] - ) -> None: - fspath, lineno, domain = location - # Ensure that the path is printed before the - # 1st test of a module starts running. - if self.showlongtestinfo: - line = self._locationline(nodeid, fspath, lineno, domain) - self.write_ensure_prefix(line, "") - self.flush() - elif self.showfspath: - self.write_fspath_result(nodeid, "") - self.flush() - - def pytest_runtest_logreport(self, report: TestReport) -> None: - self._tests_ran = True - rep = report - - res = TestShortLogReport( - *self.config.hook.pytest_report_teststatus(report=rep, config=self.config) - ) - category, letter, word = res.category, res.letter, res.word - if not isinstance(word, tuple): - markup = None - else: - word, markup = word - self._add_stats(category, [rep]) - if not letter and not word: - # Probably passed setup/teardown. - return - if markup is None: - was_xfail = hasattr(report, "wasxfail") - if rep.passed and not was_xfail: - markup = {"green": True} - elif rep.passed and was_xfail: - markup = {"yellow": True} - elif rep.failed: - markup = {"red": True} - elif rep.skipped: - markup = {"yellow": True} - else: - markup = {} - self._progress_nodeids_reported.add(rep.nodeid) - if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0: - self._tw.write(letter, **markup) - # When running in xdist, the logreport and logfinish of multiple - # items are interspersed, e.g. `logreport`, `logreport`, - # `logfinish`, `logfinish`. To avoid the "past edge" calculation - # from getting confused and overflowing (#7166), do the past edge - # printing here and not in logfinish, except for the 100% which - # should only be printed after all teardowns are finished. - if self._show_progress_info and not self._is_last_item: - self._write_progress_information_if_past_edge() - else: - line = self._locationline(rep.nodeid, *rep.location) - running_xdist = hasattr(rep, "node") - if not running_xdist: - self.write_ensure_prefix(line, word, **markup) - if rep.skipped or hasattr(report, "wasxfail"): - reason = _get_raw_skip_reason(rep) - if self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) < 2: - available_width = ( - (self._tw.fullwidth - self._tw.width_of_current_line) - - len(" [100%]") - - 1 - ) - formatted_reason = _format_trimmed( - " ({})", reason, available_width - ) - else: - formatted_reason = f" ({reason})" - - if reason and formatted_reason is not None: - self.wrap_write(formatted_reason) - if self._show_progress_info: - self._write_progress_information_filling_space() - else: - self.ensure_newline() - self._tw.write(f"[{rep.node.gateway.id}]") - if self._show_progress_info: - self._tw.write( - self._get_progress_information_message() + " ", cyan=True - ) - else: - self._tw.write(" ") - self._tw.write(word, **markup) - self._tw.write(" " + line) - self.currentfspath = -2 - self.flush() - - @property - def _is_last_item(self) -> bool: - assert self._session is not None - return len(self._progress_nodeids_reported) == self._session.testscollected - - @hookimpl(wrapper=True) - def pytest_runtestloop(self) -> Generator[None, object, object]: - result = yield - - # Write the final/100% progress -- deferred until the loop is complete. - if ( - self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) <= 0 - and self._show_progress_info - and self._progress_nodeids_reported - ): - self._write_progress_information_filling_space() - - return result - - def _get_progress_information_message(self) -> str: - assert self._session - collected = self._session.testscollected - if self._show_progress_info == "count": - if collected: - progress = len(self._progress_nodeids_reported) - counter_format = f"{{:{len(str(collected))}d}}" - format_string = f" [{counter_format}/{{}}]" - return format_string.format(progress, collected) - return f" [ {collected} / {collected} ]" - else: - if collected: - return ( - f" [{len(self._progress_nodeids_reported) * 100 // collected:3d}%]" - ) - return " [100%]" - - def _write_progress_information_if_past_edge(self) -> None: - w = self._width_of_current_line - if self._show_progress_info == "count": - assert self._session - num_tests = self._session.testscollected - progress_length = len(f" [{num_tests}/{num_tests}]") - else: - progress_length = len(" [100%]") - past_edge = w + progress_length + 1 >= self._screen_width - if past_edge: - main_color, _ = self._get_main_color() - msg = self._get_progress_information_message() - self._tw.write(msg + "\n", **{main_color: True}) - - def _write_progress_information_filling_space(self) -> None: - color, _ = self._get_main_color() - msg = self._get_progress_information_message() - w = self._width_of_current_line - fill = self._tw.fullwidth - w - 1 - self.write(msg.rjust(fill), flush=True, **{color: True}) - - @property - def _width_of_current_line(self) -> int: - """Return the width of the current line.""" - return self._tw.width_of_current_line - - def pytest_collection(self) -> None: - if self.isatty: - if self.config.option.verbose >= 0: - self.write("collecting ... ", flush=True, bold=True) - self._collect_report_last_write = timing.time() - elif self.config.option.verbose >= 1: - self.write("collecting ... ", flush=True, bold=True) - - def pytest_collectreport(self, report: CollectReport) -> None: - if report.failed: - self._add_stats("error", [report]) - elif report.skipped: - self._add_stats("skipped", [report]) - items = [x for x in report.result if isinstance(x, Item)] - self._numcollected += len(items) - if self.isatty: - self.report_collect() - - def report_collect(self, final: bool = False) -> None: - if self.config.option.verbose < 0: - return - - if not final: - # Only write "collecting" report every 0.5s. - t = timing.time() - if ( - self._collect_report_last_write is not None - and self._collect_report_last_write > t - REPORT_COLLECTING_RESOLUTION - ): - return - self._collect_report_last_write = t - - errors = len(self.stats.get("error", [])) - skipped = len(self.stats.get("skipped", [])) - deselected = len(self.stats.get("deselected", [])) - selected = self._numcollected - deselected - line = "collected " if final else "collecting " - line += ( - str(self._numcollected) + " item" + ("" if self._numcollected == 1 else "s") - ) - if errors: - line += " / %d error%s" % (errors, "s" if errors != 1 else "") - if deselected: - line += " / %d deselected" % deselected - if skipped: - line += " / %d skipped" % skipped - if self._numcollected > selected: - line += " / %d selected" % selected - if self.isatty: - self.rewrite(line, bold=True, erase=True) - if final: - self.write("\n") - else: - self.write_line(line) - - @hookimpl(trylast=True) - def pytest_sessionstart(self, session: Session) -> None: - self._session = session - self._sessionstarttime = timing.time() - if not self.showheader: - return - self.write_sep("=", "test session starts", bold=True) - verinfo = platform.python_version() - if not self.no_header: - msg = f"platform {sys.platform} -- Python {verinfo}" - pypy_version_info = getattr(sys, "pypy_version_info", None) - if pypy_version_info: - verinfo = ".".join(map(str, pypy_version_info[:3])) - msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" - msg += f", pytest-{_pytest._version.version}, pluggy-{pluggy.__version__}" - if ( - self.verbosity > 0 - or self.config.option.debug - or getattr(self.config.option, "pastebin", None) - ): - msg += " -- " + str(sys.executable) - self.write_line(msg) - lines = self.config.hook.pytest_report_header( - config=self.config, start_path=self.startpath - ) - self._write_report_lines_from_hooks(lines) - - def _write_report_lines_from_hooks( - self, lines: Sequence[str | Sequence[str]] - ) -> None: - for line_or_lines in reversed(lines): - if isinstance(line_or_lines, str): - self.write_line(line_or_lines) - else: - for line in line_or_lines: - self.write_line(line) - - def pytest_report_header(self, config: Config) -> list[str]: - result = [f"rootdir: {config.rootpath}"] - - if config.inipath: - result.append("configfile: " + bestrelpath(config.rootpath, config.inipath)) - - if config.args_source == Config.ArgsSource.TESTPATHS: - testpaths: list[str] = config.getini("testpaths") - result.append("testpaths: {}".format(", ".join(testpaths))) - - plugininfo = config.pluginmanager.list_plugin_distinfo() - if plugininfo: - result.append( - "plugins: {}".format(", ".join(_plugin_nameversions(plugininfo))) - ) - return result - - def pytest_collection_finish(self, session: Session) -> None: - self.report_collect(True) - - lines = self.config.hook.pytest_report_collectionfinish( - config=self.config, - start_path=self.startpath, - items=session.items, - ) - self._write_report_lines_from_hooks(lines) - - if self.config.getoption("collectonly"): - if session.items: - if self.config.option.verbose > -1: - self._tw.line("") - self._printcollecteditems(session.items) - - failed = self.stats.get("failed") - if failed: - self._tw.sep("!", "collection failures") - for rep in failed: - rep.toterminal(self._tw) - - def _printcollecteditems(self, items: Sequence[Item]) -> None: - test_cases_verbosity = self.config.get_verbosity(Config.VERBOSITY_TEST_CASES) - if test_cases_verbosity < 0: - if test_cases_verbosity < -1: - counts = Counter(item.nodeid.split("::", 1)[0] for item in items) - for name, count in sorted(counts.items()): - self._tw.line("%s: %d" % (name, count)) - else: - for item in items: - self._tw.line(item.nodeid) - return - stack: list[Node] = [] - indent = "" - for item in items: - needed_collectors = item.listchain()[1:] # strip root node - while stack: - if stack == needed_collectors[: len(stack)]: - break - stack.pop() - for col in needed_collectors[len(stack) :]: - stack.append(col) - indent = (len(stack) - 1) * " " - self._tw.line(f"{indent}{col}") - if test_cases_verbosity >= 1: - obj = getattr(col, "obj", None) - doc = inspect.getdoc(obj) if obj else None - if doc: - for line in doc.splitlines(): - self._tw.line("{}{}".format(indent + " ", line)) - - @hookimpl(wrapper=True) - def pytest_sessionfinish( - self, session: Session, exitstatus: int | ExitCode - ) -> Generator[None]: - result = yield - self._tw.line("") - summary_exit_codes = ( - ExitCode.OK, - ExitCode.TESTS_FAILED, - ExitCode.INTERRUPTED, - ExitCode.USAGE_ERROR, - ExitCode.NO_TESTS_COLLECTED, - ) - if exitstatus in summary_exit_codes and not self.no_summary: - self.config.hook.pytest_terminal_summary( - terminalreporter=self, exitstatus=exitstatus, config=self.config - ) - if session.shouldfail: - self.write_sep("!", str(session.shouldfail), red=True) - if exitstatus == ExitCode.INTERRUPTED: - self._report_keyboardinterrupt() - self._keyboardinterrupt_memo = None - elif session.shouldstop: - self.write_sep("!", str(session.shouldstop), red=True) - self.summary_stats() - return result - - @hookimpl(wrapper=True) - def pytest_terminal_summary(self) -> Generator[None]: - self.summary_errors() - self.summary_failures() - self.summary_xfailures() - self.summary_warnings() - self.summary_passes() - self.summary_xpasses() - try: - return (yield) - finally: - self.short_test_summary() - # Display any extra warnings from teardown here (if any). - self.summary_warnings() - - def pytest_keyboard_interrupt(self, excinfo: ExceptionInfo[BaseException]) -> None: - self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True) - - def pytest_unconfigure(self) -> None: - if self._keyboardinterrupt_memo is not None: - self._report_keyboardinterrupt() - - def _report_keyboardinterrupt(self) -> None: - excrepr = self._keyboardinterrupt_memo - assert excrepr is not None - assert excrepr.reprcrash is not None - msg = excrepr.reprcrash.message - self.write_sep("!", msg) - if "KeyboardInterrupt" in msg: - if self.config.option.fulltrace: - excrepr.toterminal(self._tw) - else: - excrepr.reprcrash.toterminal(self._tw) - self._tw.line( - "(to show a full traceback on KeyboardInterrupt use --full-trace)", - yellow=True, - ) - - def _locationline( - self, nodeid: str, fspath: str, lineno: int | None, domain: str - ) -> str: - def mkrel(nodeid: str) -> str: - line = self.config.cwd_relative_nodeid(nodeid) - if domain and line.endswith(domain): - line = line[: -len(domain)] - values = domain.split("[") - values[0] = values[0].replace(".", "::") # don't replace '.' in params - line += "[".join(values) - return line - - # fspath comes from testid which has a "/"-normalized path. - if fspath: - res = mkrel(nodeid) - if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace( - "\\", nodes.SEP - ): - res += " <- " + bestrelpath(self.startpath, Path(fspath)) - else: - res = "[location]" - return res + " " - - def _getfailureheadline(self, rep): - head_line = rep.head_line - if head_line: - return head_line - return "test session" # XXX? - - def _getcrashline(self, rep): - try: - return str(rep.longrepr.reprcrash) - except AttributeError: - try: - return str(rep.longrepr)[:50] - except AttributeError: - return "" - - # - # Summaries for sessionfinish. - # - def getreports(self, name: str): - return [x for x in self.stats.get(name, ()) if not hasattr(x, "_pdbshown")] - - def summary_warnings(self) -> None: - if self.hasopt("w"): - all_warnings: list[WarningReport] | None = self.stats.get("warnings") - if not all_warnings: - return - - final = self._already_displayed_warnings is not None - if final: - warning_reports = all_warnings[self._already_displayed_warnings :] - else: - warning_reports = all_warnings - self._already_displayed_warnings = len(warning_reports) - if not warning_reports: - return - - reports_grouped_by_message: dict[str, list[WarningReport]] = {} - for wr in warning_reports: - reports_grouped_by_message.setdefault(wr.message, []).append(wr) - - def collapsed_location_report(reports: list[WarningReport]) -> str: - locations = [] - for w in reports: - location = w.get_location(self.config) - if location: - locations.append(location) - - if len(locations) < 10: - return "\n".join(map(str, locations)) - - counts_by_filename = Counter( - str(loc).split("::", 1)[0] for loc in locations - ) - return "\n".join( - "{}: {} warning{}".format(k, v, "s" if v > 1 else "") - for k, v in counts_by_filename.items() - ) - - title = "warnings summary (final)" if final else "warnings summary" - self.write_sep("=", title, yellow=True, bold=False) - for message, message_reports in reports_grouped_by_message.items(): - maybe_location = collapsed_location_report(message_reports) - if maybe_location: - self._tw.line(maybe_location) - lines = message.splitlines() - indented = "\n".join(" " + x for x in lines) - message = indented.rstrip() - else: - message = message.rstrip() - self._tw.line(message) - self._tw.line() - self._tw.line( - "-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html" - ) - - def summary_passes(self) -> None: - self.summary_passes_combined("passed", "PASSES", "P") - - def summary_xpasses(self) -> None: - self.summary_passes_combined("xpassed", "XPASSES", "X") - - def summary_passes_combined( - self, which_reports: str, sep_title: str, needed_opt: str - ) -> None: - if self.config.option.tbstyle != "no": - if self.hasopt(needed_opt): - reports: list[TestReport] = self.getreports(which_reports) - if not reports: - return - self.write_sep("=", sep_title) - for rep in reports: - if rep.sections: - msg = self._getfailureheadline(rep) - self.write_sep("_", msg, green=True, bold=True) - self._outrep_summary(rep) - self._handle_teardown_sections(rep.nodeid) - - def _get_teardown_reports(self, nodeid: str) -> list[TestReport]: - reports = self.getreports("") - return [ - report - for report in reports - if report.when == "teardown" and report.nodeid == nodeid - ] - - def _handle_teardown_sections(self, nodeid: str) -> None: - for report in self._get_teardown_reports(nodeid): - self.print_teardown_sections(report) - - def print_teardown_sections(self, rep: TestReport) -> None: - showcapture = self.config.option.showcapture - if showcapture == "no": - return - for secname, content in rep.sections: - if showcapture != "all" and showcapture not in secname: - continue - if "teardown" in secname: - self._tw.sep("-", secname) - if content[-1:] == "\n": - content = content[:-1] - self._tw.line(content) - - def summary_failures(self) -> None: - style = self.config.option.tbstyle - self.summary_failures_combined("failed", "FAILURES", style=style) - - def summary_xfailures(self) -> None: - show_tb = self.config.option.xfail_tb - style = self.config.option.tbstyle if show_tb else "no" - self.summary_failures_combined("xfailed", "XFAILURES", style=style) - - def summary_failures_combined( - self, - which_reports: str, - sep_title: str, - *, - style: str, - needed_opt: str | None = None, - ) -> None: - if style != "no": - if not needed_opt or self.hasopt(needed_opt): - reports: list[BaseReport] = self.getreports(which_reports) - if not reports: - return - self.write_sep("=", sep_title) - if style == "line": - for rep in reports: - line = self._getcrashline(rep) - self.write_line(line) - else: - for rep in reports: - msg = self._getfailureheadline(rep) - self.write_sep("_", msg, red=True, bold=True) - self._outrep_summary(rep) - self._handle_teardown_sections(rep.nodeid) - - def summary_errors(self) -> None: - if self.config.option.tbstyle != "no": - reports: list[BaseReport] = self.getreports("error") - if not reports: - return - self.write_sep("=", "ERRORS") - for rep in self.stats["error"]: - msg = self._getfailureheadline(rep) - if rep.when == "collect": - msg = "ERROR collecting " + msg - else: - msg = f"ERROR at {rep.when} of {msg}" - self.write_sep("_", msg, red=True, bold=True) - self._outrep_summary(rep) - - def _outrep_summary(self, rep: BaseReport) -> None: - rep.toterminal(self._tw) - showcapture = self.config.option.showcapture - if showcapture == "no": - return - for secname, content in rep.sections: - if showcapture != "all" and showcapture not in secname: - continue - self._tw.sep("-", secname) - if content[-1:] == "\n": - content = content[:-1] - self._tw.line(content) - - def summary_stats(self) -> None: - if self.verbosity < -1: - return - - session_duration = timing.time() - self._sessionstarttime - (parts, main_color) = self.build_summary_stats_line() - line_parts = [] - - display_sep = self.verbosity >= 0 - if display_sep: - fullwidth = self._tw.fullwidth - for text, markup in parts: - with_markup = self._tw.markup(text, **markup) - if display_sep: - fullwidth += len(with_markup) - len(text) - line_parts.append(with_markup) - msg = ", ".join(line_parts) - - main_markup = {main_color: True} - duration = f" in {format_session_duration(session_duration)}" - duration_with_markup = self._tw.markup(duration, **main_markup) - if display_sep: - fullwidth += len(duration_with_markup) - len(duration) - msg += duration_with_markup - - if display_sep: - markup_for_end_sep = self._tw.markup("", **main_markup) - if markup_for_end_sep.endswith("\x1b[0m"): - markup_for_end_sep = markup_for_end_sep[:-4] - fullwidth += len(markup_for_end_sep) - msg += markup_for_end_sep - - if display_sep: - self.write_sep("=", msg, fullwidth=fullwidth, **main_markup) - else: - self.write_line(msg, **main_markup) - - def short_test_summary(self) -> None: - if not self.reportchars: - return - - def show_simple(lines: list[str], *, stat: str) -> None: - failed = self.stats.get(stat, []) - if not failed: - return - config = self.config - for rep in failed: - color = _color_for_type.get(stat, _color_for_type_default) - line = _get_line_with_reprcrash_message( - config, rep, self._tw, {color: True} - ) - lines.append(line) - - def show_xfailed(lines: list[str]) -> None: - xfailed = self.stats.get("xfailed", []) - for rep in xfailed: - verbose_word, verbose_markup = rep._get_verbose_word_with_markup( - self.config, {_color_for_type["warnings"]: True} - ) - markup_word = self._tw.markup(verbose_word, **verbose_markup) - nodeid = _get_node_id_with_markup(self._tw, self.config, rep) - line = f"{markup_word} {nodeid}" - reason = rep.wasxfail - if reason: - line += " - " + str(reason) - - lines.append(line) - - def show_xpassed(lines: list[str]) -> None: - xpassed = self.stats.get("xpassed", []) - for rep in xpassed: - verbose_word, verbose_markup = rep._get_verbose_word_with_markup( - self.config, {_color_for_type["warnings"]: True} - ) - markup_word = self._tw.markup(verbose_word, **verbose_markup) - nodeid = _get_node_id_with_markup(self._tw, self.config, rep) - line = f"{markup_word} {nodeid}" - reason = rep.wasxfail - if reason: - line += " - " + str(reason) - lines.append(line) - - def show_skipped_folded(lines: list[str]) -> None: - skipped: list[CollectReport] = self.stats.get("skipped", []) - fskips = _folded_skips(self.startpath, skipped) if skipped else [] - if not fskips: - return - verbose_word, verbose_markup = skipped[0]._get_verbose_word_with_markup( - self.config, {_color_for_type["warnings"]: True} - ) - markup_word = self._tw.markup(verbose_word, **verbose_markup) - prefix = "Skipped: " - for num, fspath, lineno, reason in fskips: - if reason.startswith(prefix): - reason = reason[len(prefix) :] - if lineno is not None: - lines.append( - "%s [%d] %s:%d: %s" % (markup_word, num, fspath, lineno, reason) - ) - else: - lines.append("%s [%d] %s: %s" % (markup_word, num, fspath, reason)) - - def show_skipped_unfolded(lines: list[str]) -> None: - skipped: list[CollectReport] = self.stats.get("skipped", []) - - for rep in skipped: - assert rep.longrepr is not None - assert isinstance(rep.longrepr, tuple), (rep, rep.longrepr) - assert len(rep.longrepr) == 3, (rep, rep.longrepr) - - verbose_word, verbose_markup = rep._get_verbose_word_with_markup( - self.config, {_color_for_type["warnings"]: True} - ) - markup_word = self._tw.markup(verbose_word, **verbose_markup) - nodeid = _get_node_id_with_markup(self._tw, self.config, rep) - line = f"{markup_word} {nodeid}" - reason = rep.longrepr[2] - if reason: - line += " - " + str(reason) - lines.append(line) - - def show_skipped(lines: list[str]) -> None: - if self.foldskipped: - show_skipped_folded(lines) - else: - show_skipped_unfolded(lines) - - REPORTCHAR_ACTIONS: Mapping[str, Callable[[list[str]], None]] = { - "x": show_xfailed, - "X": show_xpassed, - "f": partial(show_simple, stat="failed"), - "s": show_skipped, - "p": partial(show_simple, stat="passed"), - "E": partial(show_simple, stat="error"), - } - - lines: list[str] = [] - for char in self.reportchars: - action = REPORTCHAR_ACTIONS.get(char) - if action: # skipping e.g. "P" (passed with output) here. - action(lines) - - if lines: - self.write_sep("=", "short test summary info", cyan=True, bold=True) - for line in lines: - self.write_line(line) - - def _get_main_color(self) -> tuple[str, list[str]]: - if self._main_color is None or self._known_types is None or self._is_last_item: - self._set_main_color() - assert self._main_color - assert self._known_types - return self._main_color, self._known_types - - def _determine_main_color(self, unknown_type_seen: bool) -> str: - stats = self.stats - if "failed" in stats or "error" in stats: - main_color = "red" - elif "warnings" in stats or "xpassed" in stats or unknown_type_seen: - main_color = "yellow" - elif "passed" in stats or not self._is_last_item: - main_color = "green" - else: - main_color = "yellow" - return main_color - - def _set_main_color(self) -> None: - unknown_types: list[str] = [] - for found_type in self.stats: - if found_type: # setup/teardown reports have an empty key, ignore them - if found_type not in KNOWN_TYPES and found_type not in unknown_types: - unknown_types.append(found_type) - self._known_types = list(KNOWN_TYPES) + unknown_types - self._main_color = self._determine_main_color(bool(unknown_types)) - - def build_summary_stats_line(self) -> tuple[list[tuple[str, dict[str, bool]]], str]: - """ - Build the parts used in the last summary stats line. - - The summary stats line is the line shown at the end, "=== 12 passed, 2 errors in Xs===". - - This function builds a list of the "parts" that make up for the text in that line, in - the example above it would be: - - [ - ("12 passed", {"green": True}), - ("2 errors", {"red": True} - ] - - That last dict for each line is a "markup dictionary", used by TerminalWriter to - color output. - - The final color of the line is also determined by this function, and is the second - element of the returned tuple. - """ - if self.config.getoption("collectonly"): - return self._build_collect_only_summary_stats_line() - else: - return self._build_normal_summary_stats_line() - - def _get_reports_to_display(self, key: str) -> list[Any]: - """Get test/collection reports for the given status key, such as `passed` or `error`.""" - reports = self.stats.get(key, []) - return [x for x in reports if getattr(x, "count_towards_summary", True)] - - def _build_normal_summary_stats_line( - self, - ) -> tuple[list[tuple[str, dict[str, bool]]], str]: - main_color, known_types = self._get_main_color() - parts = [] - - for key in known_types: - reports = self._get_reports_to_display(key) - if reports: - count = len(reports) - color = _color_for_type.get(key, _color_for_type_default) - markup = {color: True, "bold": color == main_color} - parts.append(("%d %s" % pluralize(count, key), markup)) - - if not parts: - parts = [("no tests ran", {_color_for_type_default: True})] - - return parts, main_color - - def _build_collect_only_summary_stats_line( - self, - ) -> tuple[list[tuple[str, dict[str, bool]]], str]: - deselected = len(self._get_reports_to_display("deselected")) - errors = len(self._get_reports_to_display("error")) - - if self._numcollected == 0: - parts = [("no tests collected", {"yellow": True})] - main_color = "yellow" - - elif deselected == 0: - main_color = "green" - collected_output = "%d %s collected" % pluralize(self._numcollected, "test") - parts = [(collected_output, {main_color: True})] - else: - all_tests_were_deselected = self._numcollected == deselected - if all_tests_were_deselected: - main_color = "yellow" - collected_output = f"no tests collected ({deselected} deselected)" - else: - main_color = "green" - selected = self._numcollected - deselected - collected_output = f"{selected}/{self._numcollected} tests collected ({deselected} deselected)" - - parts = [(collected_output, {main_color: True})] - - if errors: - main_color = _color_for_type["error"] - parts += [("%d %s" % pluralize(errors, "error"), {main_color: True})] - - return parts, main_color - - -def _get_node_id_with_markup(tw: TerminalWriter, config: Config, rep: BaseReport): - nodeid = config.cwd_relative_nodeid(rep.nodeid) - path, *parts = nodeid.split("::") - if parts: - parts_markup = tw.markup("::".join(parts), bold=True) - return path + "::" + parts_markup - else: - return path - - -def _format_trimmed(format: str, msg: str, available_width: int) -> str | None: - """Format msg into format, ellipsizing it if doesn't fit in available_width. - - Returns None if even the ellipsis can't fit. - """ - # Only use the first line. - i = msg.find("\n") - if i != -1: - msg = msg[:i] - - ellipsis = "..." - format_width = wcswidth(format.format("")) - if format_width + len(ellipsis) > available_width: - return None - - if format_width + wcswidth(msg) > available_width: - available_width -= len(ellipsis) - msg = msg[:available_width] - while format_width + wcswidth(msg) > available_width: - msg = msg[:-1] - msg += ellipsis - - return format.format(msg) - - -def _get_line_with_reprcrash_message( - config: Config, rep: BaseReport, tw: TerminalWriter, word_markup: dict[str, bool] -) -> str: - """Get summary line for a report, trying to add reprcrash message.""" - verbose_word, verbose_markup = rep._get_verbose_word_with_markup( - config, word_markup - ) - word = tw.markup(verbose_word, **verbose_markup) - node = _get_node_id_with_markup(tw, config, rep) - - line = f"{word} {node}" - line_width = wcswidth(line) - - try: - # Type ignored intentionally -- possible AttributeError expected. - msg = rep.longrepr.reprcrash.message # type: ignore[union-attr] - except AttributeError: - pass - else: - if running_on_ci() or config.option.verbose >= 2: - msg = f" - {msg}" - else: - available_width = tw.fullwidth - line_width - msg = _format_trimmed(" - {}", msg, available_width) - if msg is not None: - line += msg - - return line - - -def _folded_skips( - startpath: Path, - skipped: Sequence[CollectReport], -) -> list[tuple[int, str, int | None, str]]: - d: dict[tuple[str, int | None, str], list[CollectReport]] = {} - for event in skipped: - assert event.longrepr is not None - assert isinstance(event.longrepr, tuple), (event, event.longrepr) - assert len(event.longrepr) == 3, (event, event.longrepr) - fspath, lineno, reason = event.longrepr - # For consistency, report all fspaths in relative form. - fspath = bestrelpath(startpath, Path(fspath)) - keywords = getattr(event, "keywords", {}) - # Folding reports with global pytestmark variable. - # This is a workaround, because for now we cannot identify the scope of a skip marker - # TODO: Revisit after marks scope would be fixed. - if ( - event.when == "setup" - and "skip" in keywords - and "pytestmark" not in keywords - ): - key: tuple[str, int | None, str] = (fspath, None, reason) - else: - key = (fspath, lineno, reason) - d.setdefault(key, []).append(event) - values: list[tuple[int, str, int | None, str]] = [] - for key, events in d.items(): - values.append((len(events), *key)) - return values - - -_color_for_type = { - "failed": "red", - "error": "red", - "warnings": "yellow", - "passed": "green", -} -_color_for_type_default = "yellow" - - -def pluralize(count: int, noun: str) -> tuple[int, str]: - # No need to pluralize words such as `failed` or `passed`. - if noun not in ["error", "warnings", "test"]: - return count, noun - - # The `warnings` key is plural. To avoid API breakage, we keep it that way but - # set it to singular here so we can determine plurality in the same way as we do - # for `error`. - noun = noun.replace("warnings", "warning") - - return count, noun + "s" if count != 1 else noun - - -def _plugin_nameversions(plugininfo) -> list[str]: - values: list[str] = [] - for plugin, dist in plugininfo: - # Gets us name and version! - name = f"{dist.project_name}-{dist.version}" - # Questionable convenience, but it keeps things short. - if name.startswith("pytest-"): - name = name[7:] - # We decided to print python package names they can have more than one plugin. - if name not in values: - values.append(name) - return values - - -def format_session_duration(seconds: float) -> str: - """Format the given seconds in a human readable manner to show in the final summary.""" - if seconds < 60: - return f"{seconds:.2f}s" - else: - dt = datetime.timedelta(seconds=int(seconds)) - return f"{seconds:.2f}s ({dt})" - - -def _get_raw_skip_reason(report: TestReport) -> str: - """Get the reason string of a skip/xfail/xpass test report. - - The string is just the part given by the user. - """ - if hasattr(report, "wasxfail"): - reason = report.wasxfail - if reason.startswith("reason: "): - reason = reason[len("reason: ") :] - return reason - else: - assert report.skipped - assert isinstance(report.longrepr, tuple) - _, _, reason = report.longrepr - if reason.startswith("Skipped: "): - reason = reason[len("Skipped: ") :] - elif reason == "Skipped": - reason = "" - return reason diff --git a/.venv/lib/python3.12/site-packages/_pytest/threadexception.py b/.venv/lib/python3.12/site-packages/_pytest/threadexception.py deleted file mode 100644 index c1ed8038..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/threadexception.py +++ /dev/null @@ -1,97 +0,0 @@ -from __future__ import annotations - -import threading -import traceback -from types import TracebackType -from typing import Any -from typing import Callable -from typing import Generator -from typing import TYPE_CHECKING -import warnings - -import pytest - - -if TYPE_CHECKING: - from typing_extensions import Self - - -# Copied from cpython/Lib/test/support/threading_helper.py, with modifications. -class catch_threading_exception: - """Context manager catching threading.Thread exception using - threading.excepthook. - - Storing exc_value using a custom hook can create a reference cycle. The - reference cycle is broken explicitly when the context manager exits. - - Storing thread using a custom hook can resurrect it if it is set to an - object which is being finalized. Exiting the context manager clears the - stored object. - - Usage: - with threading_helper.catch_threading_exception() as cm: - # code spawning a thread which raises an exception - ... - # check the thread exception: use cm.args - ... - # cm.args attribute no longer exists at this point - # (to break a reference cycle) - """ - - def __init__(self) -> None: - self.args: threading.ExceptHookArgs | None = None - self._old_hook: Callable[[threading.ExceptHookArgs], Any] | None = None - - def _hook(self, args: threading.ExceptHookArgs) -> None: - self.args = args - - def __enter__(self) -> Self: - self._old_hook = threading.excepthook - threading.excepthook = self._hook - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - assert self._old_hook is not None - threading.excepthook = self._old_hook - self._old_hook = None - del self.args - - -def thread_exception_runtest_hook() -> Generator[None]: - with catch_threading_exception() as cm: - try: - yield - finally: - if cm.args: - thread_name = ( - "" if cm.args.thread is None else cm.args.thread.name - ) - msg = f"Exception in thread {thread_name}\n\n" - msg += "".join( - traceback.format_exception( - cm.args.exc_type, - cm.args.exc_value, - cm.args.exc_traceback, - ) - ) - warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg)) - - -@pytest.hookimpl(wrapper=True, trylast=True) -def pytest_runtest_setup() -> Generator[None]: - yield from thread_exception_runtest_hook() - - -@pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_call() -> Generator[None]: - yield from thread_exception_runtest_hook() - - -@pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_teardown() -> Generator[None]: - yield from thread_exception_runtest_hook() diff --git a/.venv/lib/python3.12/site-packages/_pytest/timing.py b/.venv/lib/python3.12/site-packages/_pytest/timing.py deleted file mode 100644 index b23c7f69..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/timing.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Indirection for time functions. - -We intentionally grab some "time" functions internally to avoid tests mocking "time" to affect -pytest runtime information (issue #185). - -Fixture "mock_timing" also interacts with this module for pytest's own tests. -""" - -from __future__ import annotations - -from time import perf_counter -from time import sleep -from time import time - - -__all__ = ["perf_counter", "sleep", "time"] diff --git a/.venv/lib/python3.12/site-packages/_pytest/tmpdir.py b/.venv/lib/python3.12/site-packages/_pytest/tmpdir.py deleted file mode 100644 index 1731a4b8..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/tmpdir.py +++ /dev/null @@ -1,314 +0,0 @@ -# mypy: allow-untyped-defs -"""Support for providing temporary directories to test functions.""" - -from __future__ import annotations - -import dataclasses -import os -from pathlib import Path -import re -from shutil import rmtree -import tempfile -from typing import Any -from typing import Dict -from typing import final -from typing import Generator -from typing import Literal - -from .pathlib import cleanup_dead_symlinks -from .pathlib import LOCK_TIMEOUT -from .pathlib import make_numbered_dir -from .pathlib import make_numbered_dir_with_cleanup -from .pathlib import rm_rf -from _pytest.compat import get_user_id -from _pytest.config import Config -from _pytest.config import ExitCode -from _pytest.config import hookimpl -from _pytest.config.argparsing import Parser -from _pytest.deprecated import check_ispytest -from _pytest.fixtures import fixture -from _pytest.fixtures import FixtureRequest -from _pytest.monkeypatch import MonkeyPatch -from _pytest.nodes import Item -from _pytest.reports import TestReport -from _pytest.stash import StashKey - - -tmppath_result_key = StashKey[Dict[str, bool]]() -RetentionType = Literal["all", "failed", "none"] - - -@final -@dataclasses.dataclass -class TempPathFactory: - """Factory for temporary directories under the common base temp directory, - as discussed at :ref:`temporary directory location and retention`. - """ - - _given_basetemp: Path | None - # pluggy TagTracerSub, not currently exposed, so Any. - _trace: Any - _basetemp: Path | None - _retention_count: int - _retention_policy: RetentionType - - def __init__( - self, - given_basetemp: Path | None, - retention_count: int, - retention_policy: RetentionType, - trace, - basetemp: Path | None = None, - *, - _ispytest: bool = False, - ) -> None: - check_ispytest(_ispytest) - if given_basetemp is None: - self._given_basetemp = None - else: - # Use os.path.abspath() to get absolute path instead of resolve() as it - # does not work the same in all platforms (see #4427). - # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012). - self._given_basetemp = Path(os.path.abspath(str(given_basetemp))) - self._trace = trace - self._retention_count = retention_count - self._retention_policy = retention_policy - self._basetemp = basetemp - - @classmethod - def from_config( - cls, - config: Config, - *, - _ispytest: bool = False, - ) -> TempPathFactory: - """Create a factory according to pytest configuration. - - :meta private: - """ - check_ispytest(_ispytest) - count = int(config.getini("tmp_path_retention_count")) - if count < 0: - raise ValueError( - f"tmp_path_retention_count must be >= 0. Current input: {count}." - ) - - policy = config.getini("tmp_path_retention_policy") - if policy not in ("all", "failed", "none"): - raise ValueError( - f"tmp_path_retention_policy must be either all, failed, none. Current input: {policy}." - ) - - return cls( - given_basetemp=config.option.basetemp, - trace=config.trace.get("tmpdir"), - retention_count=count, - retention_policy=policy, - _ispytest=True, - ) - - def _ensure_relative_to_basetemp(self, basename: str) -> str: - basename = os.path.normpath(basename) - if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp(): - raise ValueError(f"{basename} is not a normalized and relative path") - return basename - - def mktemp(self, basename: str, numbered: bool = True) -> Path: - """Create a new temporary directory managed by the factory. - - :param basename: - Directory base name, must be a relative path. - - :param numbered: - If ``True``, ensure the directory is unique by adding a numbered - suffix greater than any existing one: ``basename="foo-"`` and ``numbered=True`` - means that this function will create directories named ``"foo-0"``, - ``"foo-1"``, ``"foo-2"`` and so on. - - :returns: - The path to the new directory. - """ - basename = self._ensure_relative_to_basetemp(basename) - if not numbered: - p = self.getbasetemp().joinpath(basename) - p.mkdir(mode=0o700) - else: - p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700) - self._trace("mktemp", p) - return p - - def getbasetemp(self) -> Path: - """Return the base temporary directory, creating it if needed. - - :returns: - The base temporary directory. - """ - if self._basetemp is not None: - return self._basetemp - - if self._given_basetemp is not None: - basetemp = self._given_basetemp - if basetemp.exists(): - rm_rf(basetemp) - basetemp.mkdir(mode=0o700) - basetemp = basetemp.resolve() - else: - from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") - temproot = Path(from_env or tempfile.gettempdir()).resolve() - user = get_user() or "unknown" - # use a sub-directory in the temproot to speed-up - # make_numbered_dir() call - rootdir = temproot.joinpath(f"pytest-of-{user}") - try: - rootdir.mkdir(mode=0o700, exist_ok=True) - except OSError: - # getuser() likely returned illegal characters for the platform, use unknown back off mechanism - rootdir = temproot.joinpath("pytest-of-unknown") - rootdir.mkdir(mode=0o700, exist_ok=True) - # Because we use exist_ok=True with a predictable name, make sure - # we are the owners, to prevent any funny business (on unix, where - # temproot is usually shared). - # Also, to keep things private, fixup any world-readable temp - # rootdir's permissions. Historically 0o755 was used, so we can't - # just error out on this, at least for a while. - uid = get_user_id() - if uid is not None: - rootdir_stat = rootdir.stat() - if rootdir_stat.st_uid != uid: - raise OSError( - f"The temporary directory {rootdir} is not owned by the current user. " - "Fix this and try again." - ) - if (rootdir_stat.st_mode & 0o077) != 0: - os.chmod(rootdir, rootdir_stat.st_mode & ~0o077) - keep = self._retention_count - if self._retention_policy == "none": - keep = 0 - basetemp = make_numbered_dir_with_cleanup( - prefix="pytest-", - root=rootdir, - keep=keep, - lock_timeout=LOCK_TIMEOUT, - mode=0o700, - ) - assert basetemp is not None, basetemp - self._basetemp = basetemp - self._trace("new basetemp", basetemp) - return basetemp - - -def get_user() -> str | None: - """Return the current user name, or None if getuser() does not work - in the current environment (see #1010).""" - try: - # In some exotic environments, getpass may not be importable. - import getpass - - return getpass.getuser() - except (ImportError, OSError, KeyError): - return None - - -def pytest_configure(config: Config) -> None: - """Create a TempPathFactory and attach it to the config object. - - This is to comply with existing plugins which expect the handler to be - available at pytest_configure time, but ideally should be moved entirely - to the tmp_path_factory session fixture. - """ - mp = MonkeyPatch() - config.add_cleanup(mp.undo) - _tmp_path_factory = TempPathFactory.from_config(config, _ispytest=True) - mp.setattr(config, "_tmp_path_factory", _tmp_path_factory, raising=False) - - -def pytest_addoption(parser: Parser) -> None: - parser.addini( - "tmp_path_retention_count", - help="How many sessions should we keep the `tmp_path` directories, according to `tmp_path_retention_policy`.", - default=3, - ) - - parser.addini( - "tmp_path_retention_policy", - help="Controls which directories created by the `tmp_path` fixture are kept around, based on test outcome. " - "(all/failed/none)", - default="all", - ) - - -@fixture(scope="session") -def tmp_path_factory(request: FixtureRequest) -> TempPathFactory: - """Return a :class:`pytest.TempPathFactory` instance for the test session.""" - # Set dynamically by pytest_configure() above. - return request.config._tmp_path_factory # type: ignore - - -def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path: - name = request.node.name - name = re.sub(r"[\W]", "_", name) - MAXVAL = 30 - name = name[:MAXVAL] - return factory.mktemp(name, numbered=True) - - -@fixture -def tmp_path( - request: FixtureRequest, tmp_path_factory: TempPathFactory -) -> Generator[Path]: - """Return a temporary directory (as :class:`pathlib.Path` object) - which is unique to each test function invocation. - The temporary directory is created as a subdirectory - of the base temporary directory, with configurable retention, - as discussed in :ref:`temporary directory location and retention`. - """ - path = _mk_tmp(request, tmp_path_factory) - yield path - - # Remove the tmpdir if the policy is "failed" and the test passed. - tmp_path_factory: TempPathFactory = request.session.config._tmp_path_factory # type: ignore - policy = tmp_path_factory._retention_policy - result_dict = request.node.stash[tmppath_result_key] - - if policy == "failed" and result_dict.get("call", True): - # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, - # permissions, etc, in which case we ignore it. - rmtree(path, ignore_errors=True) - - del request.node.stash[tmppath_result_key] - - -def pytest_sessionfinish(session, exitstatus: int | ExitCode): - """After each session, remove base directory if all the tests passed, - the policy is "failed", and the basetemp is not specified by a user. - """ - tmp_path_factory: TempPathFactory = session.config._tmp_path_factory - basetemp = tmp_path_factory._basetemp - if basetemp is None: - return - - policy = tmp_path_factory._retention_policy - if ( - exitstatus == 0 - and policy == "failed" - and tmp_path_factory._given_basetemp is None - ): - if basetemp.is_dir(): - # We do a "best effort" to remove files, but it might not be possible due to some leaked resource, - # permissions, etc, in which case we ignore it. - rmtree(basetemp, ignore_errors=True) - - # Remove dead symlinks. - if basetemp.is_dir(): - cleanup_dead_symlinks(basetemp) - - -@hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_makereport( - item: Item, call -) -> Generator[None, TestReport, TestReport]: - rep = yield - assert rep.when is not None - empty: dict[str, bool] = {} - item.stash.setdefault(tmppath_result_key, empty)[rep.when] = rep.passed - return rep diff --git a/.venv/lib/python3.12/site-packages/_pytest/unittest.py b/.venv/lib/python3.12/site-packages/_pytest/unittest.py deleted file mode 100644 index 8cecd4f9..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/unittest.py +++ /dev/null @@ -1,435 +0,0 @@ -# mypy: allow-untyped-defs -"""Discover and run std-library "unittest" style tests.""" - -from __future__ import annotations - -import inspect -import sys -import traceback -import types -from typing import Any -from typing import Callable -from typing import Generator -from typing import Iterable -from typing import Tuple -from typing import Type -from typing import TYPE_CHECKING -from typing import Union - -import _pytest._code -from _pytest.compat import is_async_function -from _pytest.config import hookimpl -from _pytest.fixtures import FixtureRequest -from _pytest.nodes import Collector -from _pytest.nodes import Item -from _pytest.outcomes import exit -from _pytest.outcomes import fail -from _pytest.outcomes import skip -from _pytest.outcomes import xfail -from _pytest.python import Class -from _pytest.python import Function -from _pytest.python import Module -from _pytest.runner import CallInfo -import pytest - - -if sys.version_info[:2] < (3, 11): - from exceptiongroup import ExceptionGroup - -if TYPE_CHECKING: - import unittest - - import twisted.trial.unittest - - -_SysExcInfoType = Union[ - Tuple[Type[BaseException], BaseException, types.TracebackType], - Tuple[None, None, None], -] - - -def pytest_pycollect_makeitem( - collector: Module | Class, name: str, obj: object -) -> UnitTestCase | None: - try: - # Has unittest been imported? - ut = sys.modules["unittest"] - # Is obj a subclass of unittest.TestCase? - # Type ignored because `ut` is an opaque module. - if not issubclass(obj, ut.TestCase): # type: ignore - return None - except Exception: - return None - # Is obj a concrete class? - # Abstract classes can't be instantiated so no point collecting them. - if inspect.isabstract(obj): - return None - # Yes, so let's collect it. - return UnitTestCase.from_parent(collector, name=name, obj=obj) - - -class UnitTestCase(Class): - # Marker for fixturemanger.getfixtureinfo() - # to declare that our children do not support funcargs. - nofuncargs = True - - def newinstance(self): - # TestCase __init__ takes the method (test) name. The TestCase - # constructor treats the name "runTest" as a special no-op, so it can be - # used when a dummy instance is needed. While unittest.TestCase has a - # default, some subclasses omit the default (#9610), so always supply - # it. - return self.obj("runTest") - - def collect(self) -> Iterable[Item | Collector]: - from unittest import TestLoader - - cls = self.obj - if not getattr(cls, "__test__", True): - return - - skipped = _is_skipped(cls) - if not skipped: - self._register_unittest_setup_method_fixture(cls) - self._register_unittest_setup_class_fixture(cls) - self._register_setup_class_fixture() - - self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid) - - loader = TestLoader() - foundsomething = False - for name in loader.getTestCaseNames(self.obj): - x = getattr(self.obj, name) - if not getattr(x, "__test__", True): - continue - yield TestCaseFunction.from_parent(self, name=name) - foundsomething = True - - if not foundsomething: - runtest = getattr(self.obj, "runTest", None) - if runtest is not None: - ut = sys.modules.get("twisted.trial.unittest", None) - if ut is None or runtest != ut.TestCase.runTest: - yield TestCaseFunction.from_parent(self, name="runTest") - - def _register_unittest_setup_class_fixture(self, cls: type) -> None: - """Register an auto-use fixture to invoke setUpClass and - tearDownClass (#517).""" - setup = getattr(cls, "setUpClass", None) - teardown = getattr(cls, "tearDownClass", None) - if setup is None and teardown is None: - return None - cleanup = getattr(cls, "doClassCleanups", lambda: None) - - def process_teardown_exceptions() -> None: - # tearDown_exceptions is a list set in the class containing exc_infos for errors during - # teardown for the class. - exc_infos = getattr(cls, "tearDown_exceptions", None) - if not exc_infos: - return - exceptions = [exc for (_, exc, _) in exc_infos] - # If a single exception, raise it directly as this provides a more readable - # error (hopefully this will improve in #12255). - if len(exceptions) == 1: - raise exceptions[0] - else: - raise ExceptionGroup("Unittest class cleanup errors", exceptions) - - def unittest_setup_class_fixture( - request: FixtureRequest, - ) -> Generator[None]: - cls = request.cls - if _is_skipped(cls): - reason = cls.__unittest_skip_why__ - raise pytest.skip.Exception(reason, _use_item_location=True) - if setup is not None: - try: - setup() - # unittest does not call the cleanup function for every BaseException, so we - # follow this here. - except Exception: - cleanup() - process_teardown_exceptions() - raise - yield - try: - if teardown is not None: - teardown() - finally: - cleanup() - process_teardown_exceptions() - - self.session._fixturemanager._register_fixture( - # Use a unique name to speed up lookup. - name=f"_unittest_setUpClass_fixture_{cls.__qualname__}", - func=unittest_setup_class_fixture, - nodeid=self.nodeid, - scope="class", - autouse=True, - ) - - def _register_unittest_setup_method_fixture(self, cls: type) -> None: - """Register an auto-use fixture to invoke setup_method and - teardown_method (#517).""" - setup = getattr(cls, "setup_method", None) - teardown = getattr(cls, "teardown_method", None) - if setup is None and teardown is None: - return None - - def unittest_setup_method_fixture( - request: FixtureRequest, - ) -> Generator[None]: - self = request.instance - if _is_skipped(self): - reason = self.__unittest_skip_why__ - raise pytest.skip.Exception(reason, _use_item_location=True) - if setup is not None: - setup(self, request.function) - yield - if teardown is not None: - teardown(self, request.function) - - self.session._fixturemanager._register_fixture( - # Use a unique name to speed up lookup. - name=f"_unittest_setup_method_fixture_{cls.__qualname__}", - func=unittest_setup_method_fixture, - nodeid=self.nodeid, - scope="function", - autouse=True, - ) - - -class TestCaseFunction(Function): - nofuncargs = True - _excinfo: list[_pytest._code.ExceptionInfo[BaseException]] | None = None - - def _getinstance(self): - assert isinstance(self.parent, UnitTestCase) - return self.parent.obj(self.name) - - # Backward compat for pytest-django; can be removed after pytest-django - # updates + some slack. - @property - def _testcase(self): - return self.instance - - def setup(self) -> None: - # A bound method to be called during teardown() if set (see 'runtest()'). - self._explicit_tearDown: Callable[[], None] | None = None - super().setup() - - def teardown(self) -> None: - if self._explicit_tearDown is not None: - self._explicit_tearDown() - self._explicit_tearDown = None - self._obj = None - del self._instance - super().teardown() - - def startTest(self, testcase: unittest.TestCase) -> None: - pass - - def _addexcinfo(self, rawexcinfo: _SysExcInfoType) -> None: - # Unwrap potential exception info (see twisted trial support below). - rawexcinfo = getattr(rawexcinfo, "_rawexcinfo", rawexcinfo) - try: - excinfo = _pytest._code.ExceptionInfo[BaseException].from_exc_info( - rawexcinfo # type: ignore[arg-type] - ) - # Invoke the attributes to trigger storing the traceback - # trial causes some issue there. - _ = excinfo.value - _ = excinfo.traceback - except TypeError: - try: - try: - values = traceback.format_exception(*rawexcinfo) - values.insert( - 0, - "NOTE: Incompatible Exception Representation, " - "displaying natively:\n\n", - ) - fail("".join(values), pytrace=False) - except (fail.Exception, KeyboardInterrupt): - raise - except BaseException: - fail( - "ERROR: Unknown Incompatible Exception " - f"representation:\n{rawexcinfo!r}", - pytrace=False, - ) - except KeyboardInterrupt: - raise - except fail.Exception: - excinfo = _pytest._code.ExceptionInfo.from_current() - self.__dict__.setdefault("_excinfo", []).append(excinfo) - - def addError( - self, testcase: unittest.TestCase, rawexcinfo: _SysExcInfoType - ) -> None: - try: - if isinstance(rawexcinfo[1], exit.Exception): - exit(rawexcinfo[1].msg) - except TypeError: - pass - self._addexcinfo(rawexcinfo) - - def addFailure( - self, testcase: unittest.TestCase, rawexcinfo: _SysExcInfoType - ) -> None: - self._addexcinfo(rawexcinfo) - - def addSkip(self, testcase: unittest.TestCase, reason: str) -> None: - try: - raise pytest.skip.Exception(reason, _use_item_location=True) - except skip.Exception: - self._addexcinfo(sys.exc_info()) - - def addExpectedFailure( - self, - testcase: unittest.TestCase, - rawexcinfo: _SysExcInfoType, - reason: str = "", - ) -> None: - try: - xfail(str(reason)) - except xfail.Exception: - self._addexcinfo(sys.exc_info()) - - def addUnexpectedSuccess( - self, - testcase: unittest.TestCase, - reason: twisted.trial.unittest.Todo | None = None, - ) -> None: - msg = "Unexpected success" - if reason: - msg += f": {reason.reason}" - # Preserve unittest behaviour - fail the test. Explicitly not an XPASS. - try: - fail(msg, pytrace=False) - except fail.Exception: - self._addexcinfo(sys.exc_info()) - - def addSuccess(self, testcase: unittest.TestCase) -> None: - pass - - def stopTest(self, testcase: unittest.TestCase) -> None: - pass - - def addDuration(self, testcase: unittest.TestCase, elapsed: float) -> None: - pass - - def runtest(self) -> None: - from _pytest.debugging import maybe_wrap_pytest_function_for_tracing - - testcase = self.instance - assert testcase is not None - - maybe_wrap_pytest_function_for_tracing(self) - - # Let the unittest framework handle async functions. - if is_async_function(self.obj): - testcase(result=self) - else: - # When --pdb is given, we want to postpone calling tearDown() otherwise - # when entering the pdb prompt, tearDown() would have probably cleaned up - # instance variables, which makes it difficult to debug. - # Arguably we could always postpone tearDown(), but this changes the moment where the - # TestCase instance interacts with the results object, so better to only do it - # when absolutely needed. - # We need to consider if the test itself is skipped, or the whole class. - assert isinstance(self.parent, UnitTestCase) - skipped = _is_skipped(self.obj) or _is_skipped(self.parent.obj) - if self.config.getoption("usepdb") and not skipped: - self._explicit_tearDown = testcase.tearDown - setattr(testcase, "tearDown", lambda *args: None) - - # We need to update the actual bound method with self.obj, because - # wrap_pytest_function_for_tracing replaces self.obj by a wrapper. - setattr(testcase, self.name, self.obj) - try: - testcase(result=self) - finally: - delattr(testcase, self.name) - - def _traceback_filter( - self, excinfo: _pytest._code.ExceptionInfo[BaseException] - ) -> _pytest._code.Traceback: - traceback = super()._traceback_filter(excinfo) - ntraceback = traceback.filter( - lambda x: not x.frame.f_globals.get("__unittest"), - ) - if not ntraceback: - ntraceback = traceback - return ntraceback - - -@hookimpl(tryfirst=True) -def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None: - if isinstance(item, TestCaseFunction): - if item._excinfo: - call.excinfo = item._excinfo.pop(0) - try: - del call.result - except AttributeError: - pass - - # Convert unittest.SkipTest to pytest.skip. - # This is actually only needed for nose, which reuses unittest.SkipTest for - # its own nose.SkipTest. For unittest TestCases, SkipTest is already - # handled internally, and doesn't reach here. - unittest = sys.modules.get("unittest") - if unittest and call.excinfo and isinstance(call.excinfo.value, unittest.SkipTest): - excinfo = call.excinfo - call2 = CallInfo[None].from_call( - lambda: pytest.skip(str(excinfo.value)), call.when - ) - call.excinfo = call2.excinfo - - -# Twisted trial support. -classImplements_has_run = False - - -@hookimpl(wrapper=True) -def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: - if isinstance(item, TestCaseFunction) and "twisted.trial.unittest" in sys.modules: - ut: Any = sys.modules["twisted.python.failure"] - global classImplements_has_run - Failure__init__ = ut.Failure.__init__ - if not classImplements_has_run: - from twisted.trial.itrial import IReporter - from zope.interface import classImplements - - classImplements(TestCaseFunction, IReporter) - classImplements_has_run = True - - def excstore( - self, exc_value=None, exc_type=None, exc_tb=None, captureVars=None - ): - if exc_value is None: - self._rawexcinfo = sys.exc_info() - else: - if exc_type is None: - exc_type = type(exc_value) - self._rawexcinfo = (exc_type, exc_value, exc_tb) - try: - Failure__init__( - self, exc_value, exc_type, exc_tb, captureVars=captureVars - ) - except TypeError: - Failure__init__(self, exc_value, exc_type, exc_tb) - - ut.Failure.__init__ = excstore - try: - res = yield - finally: - ut.Failure.__init__ = Failure__init__ - else: - res = yield - return res - - -def _is_skipped(obj) -> bool: - """Return True if the given object has been marked with @unittest.skip.""" - return bool(getattr(obj, "__unittest_skip__", False)) diff --git a/.venv/lib/python3.12/site-packages/_pytest/unraisableexception.py b/.venv/lib/python3.12/site-packages/_pytest/unraisableexception.py deleted file mode 100644 index 77a2de20..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/unraisableexception.py +++ /dev/null @@ -1,100 +0,0 @@ -from __future__ import annotations - -import sys -import traceback -from types import TracebackType -from typing import Any -from typing import Callable -from typing import Generator -from typing import TYPE_CHECKING -import warnings - -import pytest - - -if TYPE_CHECKING: - from typing_extensions import Self - - -# Copied from cpython/Lib/test/support/__init__.py, with modifications. -class catch_unraisable_exception: - """Context manager catching unraisable exception using sys.unraisablehook. - - Storing the exception value (cm.unraisable.exc_value) creates a reference - cycle. The reference cycle is broken explicitly when the context manager - exits. - - Storing the object (cm.unraisable.object) can resurrect it if it is set to - an object which is being finalized. Exiting the context manager clears the - stored object. - - Usage: - with catch_unraisable_exception() as cm: - # code creating an "unraisable exception" - ... - # check the unraisable exception: use cm.unraisable - ... - # cm.unraisable attribute no longer exists at this point - # (to break a reference cycle) - """ - - def __init__(self) -> None: - self.unraisable: sys.UnraisableHookArgs | None = None - self._old_hook: Callable[[sys.UnraisableHookArgs], Any] | None = None - - def _hook(self, unraisable: sys.UnraisableHookArgs) -> None: - # Storing unraisable.object can resurrect an object which is being - # finalized. Storing unraisable.exc_value creates a reference cycle. - self.unraisable = unraisable - - def __enter__(self) -> Self: - self._old_hook = sys.unraisablehook - sys.unraisablehook = self._hook - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - assert self._old_hook is not None - sys.unraisablehook = self._old_hook - self._old_hook = None - del self.unraisable - - -def unraisable_exception_runtest_hook() -> Generator[None]: - with catch_unraisable_exception() as cm: - try: - yield - finally: - if cm.unraisable: - if cm.unraisable.err_msg is not None: - err_msg = cm.unraisable.err_msg - else: - err_msg = "Exception ignored in" - msg = f"{err_msg}: {cm.unraisable.object!r}\n\n" - msg += "".join( - traceback.format_exception( - cm.unraisable.exc_type, - cm.unraisable.exc_value, - cm.unraisable.exc_traceback, - ) - ) - warnings.warn(pytest.PytestUnraisableExceptionWarning(msg)) - - -@pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_setup() -> Generator[None]: - yield from unraisable_exception_runtest_hook() - - -@pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_call() -> Generator[None]: - yield from unraisable_exception_runtest_hook() - - -@pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_teardown() -> Generator[None]: - yield from unraisable_exception_runtest_hook() diff --git a/.venv/lib/python3.12/site-packages/_pytest/warning_types.py b/.venv/lib/python3.12/site-packages/_pytest/warning_types.py deleted file mode 100644 index 4ab14e48..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/warning_types.py +++ /dev/null @@ -1,166 +0,0 @@ -from __future__ import annotations - -import dataclasses -import inspect -from types import FunctionType -from typing import Any -from typing import final -from typing import Generic -from typing import TypeVar -import warnings - - -class PytestWarning(UserWarning): - """Base class for all warnings emitted by pytest.""" - - __module__ = "pytest" - - -@final -class PytestAssertRewriteWarning(PytestWarning): - """Warning emitted by the pytest assert rewrite module.""" - - __module__ = "pytest" - - -@final -class PytestCacheWarning(PytestWarning): - """Warning emitted by the cache plugin in various situations.""" - - __module__ = "pytest" - - -@final -class PytestConfigWarning(PytestWarning): - """Warning emitted for configuration issues.""" - - __module__ = "pytest" - - -@final -class PytestCollectionWarning(PytestWarning): - """Warning emitted when pytest is not able to collect a file or symbol in a module.""" - - __module__ = "pytest" - - -class PytestDeprecationWarning(PytestWarning, DeprecationWarning): - """Warning class for features that will be removed in a future version.""" - - __module__ = "pytest" - - -class PytestRemovedIn9Warning(PytestDeprecationWarning): - """Warning class for features that will be removed in pytest 9.""" - - __module__ = "pytest" - - -class PytestReturnNotNoneWarning(PytestWarning): - """Warning emitted when a test function is returning value other than None.""" - - __module__ = "pytest" - - -@final -class PytestExperimentalApiWarning(PytestWarning, FutureWarning): - """Warning category used to denote experiments in pytest. - - Use sparingly as the API might change or even be removed completely in a - future version. - """ - - __module__ = "pytest" - - @classmethod - def simple(cls, apiname: str) -> PytestExperimentalApiWarning: - return cls(f"{apiname} is an experimental api that may change over time") - - -@final -class PytestUnhandledCoroutineWarning(PytestReturnNotNoneWarning): - """Warning emitted for an unhandled coroutine. - - A coroutine was encountered when collecting test functions, but was not - handled by any async-aware plugin. - Coroutine test functions are not natively supported. - """ - - __module__ = "pytest" - - -@final -class PytestUnknownMarkWarning(PytestWarning): - """Warning emitted on use of unknown markers. - - See :ref:`mark` for details. - """ - - __module__ = "pytest" - - -@final -class PytestUnraisableExceptionWarning(PytestWarning): - """An unraisable exception was reported. - - Unraisable exceptions are exceptions raised in :meth:`__del__ ` - implementations and similar situations when the exception cannot be raised - as normal. - """ - - __module__ = "pytest" - - -@final -class PytestUnhandledThreadExceptionWarning(PytestWarning): - """An unhandled exception occurred in a :class:`~threading.Thread`. - - Such exceptions don't propagate normally. - """ - - __module__ = "pytest" - - -_W = TypeVar("_W", bound=PytestWarning) - - -@final -@dataclasses.dataclass -class UnformattedWarning(Generic[_W]): - """A warning meant to be formatted during runtime. - - This is used to hold warnings that need to format their message at runtime, - as opposed to a direct message. - """ - - category: type[_W] - template: str - - def format(self, **kwargs: Any) -> _W: - """Return an instance of the warning category, formatted with given kwargs.""" - return self.category(self.template.format(**kwargs)) - - -def warn_explicit_for(method: FunctionType, message: PytestWarning) -> None: - """ - Issue the warning :param:`message` for the definition of the given :param:`method` - - this helps to log warnings for functions defined prior to finding an issue with them - (like hook wrappers being marked in a legacy mechanism) - """ - lineno = method.__code__.co_firstlineno - filename = inspect.getfile(method) - module = method.__module__ - mod_globals = method.__globals__ - try: - warnings.warn_explicit( - message, - type(message), - filename=filename, - module=module, - registry=mod_globals.setdefault("__warningregistry__", {}), - lineno=lineno, - ) - except Warning as w: - # If warnings are errors (e.g. -Werror), location information gets lost, so we add it to the message. - raise type(w)(f"{w}\n at {filename}:{lineno}") from None diff --git a/.venv/lib/python3.12/site-packages/_pytest/warnings.py b/.venv/lib/python3.12/site-packages/_pytest/warnings.py deleted file mode 100644 index eeb47726..00000000 --- a/.venv/lib/python3.12/site-packages/_pytest/warnings.py +++ /dev/null @@ -1,151 +0,0 @@ -# mypy: allow-untyped-defs -from __future__ import annotations - -from contextlib import contextmanager -import sys -from typing import Generator -from typing import Literal -import warnings - -from _pytest.config import apply_warning_filters -from _pytest.config import Config -from _pytest.config import parse_warning_filter -from _pytest.main import Session -from _pytest.nodes import Item -from _pytest.terminal import TerminalReporter -import pytest - - -def pytest_configure(config: Config) -> None: - config.addinivalue_line( - "markers", - "filterwarnings(warning): add a warning filter to the given test. " - "see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings ", - ) - - -@contextmanager -def catch_warnings_for_item( - config: Config, - ihook, - when: Literal["config", "collect", "runtest"], - item: Item | None, -) -> Generator[None]: - """Context manager that catches warnings generated in the contained execution block. - - ``item`` can be None if we are not in the context of an item execution. - - Each warning captured triggers the ``pytest_warning_recorded`` hook. - """ - config_filters = config.getini("filterwarnings") - cmdline_filters = config.known_args_namespace.pythonwarnings or [] - with warnings.catch_warnings(record=True) as log: - # mypy can't infer that record=True means log is not None; help it. - assert log is not None - - if not sys.warnoptions: - # If user is not explicitly configuring warning filters, show deprecation warnings by default (#2908). - warnings.filterwarnings("always", category=DeprecationWarning) - warnings.filterwarnings("always", category=PendingDeprecationWarning) - - # To be enabled in pytest 9.0.0. - # warnings.filterwarnings("error", category=pytest.PytestRemovedIn9Warning) - - apply_warning_filters(config_filters, cmdline_filters) - - # apply filters from "filterwarnings" marks - nodeid = "" if item is None else item.nodeid - if item is not None: - for mark in item.iter_markers(name="filterwarnings"): - for arg in mark.args: - warnings.filterwarnings(*parse_warning_filter(arg, escape=False)) - - try: - yield - finally: - for warning_message in log: - ihook.pytest_warning_recorded.call_historic( - kwargs=dict( - warning_message=warning_message, - nodeid=nodeid, - when=when, - location=None, - ) - ) - - -def warning_record_to_str(warning_message: warnings.WarningMessage) -> str: - """Convert a warnings.WarningMessage to a string.""" - warn_msg = warning_message.message - msg = warnings.formatwarning( - str(warn_msg), - warning_message.category, - warning_message.filename, - warning_message.lineno, - warning_message.line, - ) - if warning_message.source is not None: - try: - import tracemalloc - except ImportError: - pass - else: - tb = tracemalloc.get_object_traceback(warning_message.source) - if tb is not None: - formatted_tb = "\n".join(tb.format()) - # Use a leading new line to better separate the (large) output - # from the traceback to the previous warning text. - msg += f"\nObject allocated at:\n{formatted_tb}" - else: - # No need for a leading new line. - url = "https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings" - msg += "Enable tracemalloc to get traceback where the object was allocated.\n" - msg += f"See {url} for more info." - return msg - - -@pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]: - with catch_warnings_for_item( - config=item.config, ihook=item.ihook, when="runtest", item=item - ): - return (yield) - - -@pytest.hookimpl(wrapper=True, tryfirst=True) -def pytest_collection(session: Session) -> Generator[None, object, object]: - config = session.config - with catch_warnings_for_item( - config=config, ihook=config.hook, when="collect", item=None - ): - return (yield) - - -@pytest.hookimpl(wrapper=True) -def pytest_terminal_summary( - terminalreporter: TerminalReporter, -) -> Generator[None]: - config = terminalreporter.config - with catch_warnings_for_item( - config=config, ihook=config.hook, when="config", item=None - ): - return (yield) - - -@pytest.hookimpl(wrapper=True) -def pytest_sessionfinish(session: Session) -> Generator[None]: - config = session.config - with catch_warnings_for_item( - config=config, ihook=config.hook, when="config", item=None - ): - return (yield) - - -@pytest.hookimpl(wrapper=True) -def pytest_load_initial_conftests( - early_config: Config, -) -> Generator[None]: - with catch_warnings_for_item( - config=early_config, ihook=early_config.hook, when="config", item=None - ): - return (yield) diff --git a/.venv/lib/python3.12/site-packages/_yaml/__init__.py b/.venv/lib/python3.12/site-packages/_yaml/__init__.py deleted file mode 100644 index 7baa8c4b..00000000 --- a/.venv/lib/python3.12/site-packages/_yaml/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# This is a stub package designed to roughly emulate the _yaml -# extension module, which previously existed as a standalone module -# and has been moved into the `yaml` package namespace. -# It does not perfectly mimic its old counterpart, but should get -# close enough for anyone who's relying on it even when they shouldn't. -import yaml - -# in some circumstances, the yaml module we imoprted may be from a different version, so we need -# to tread carefully when poking at it here (it may not have the attributes we expect) -if not getattr(yaml, '__with_libyaml__', False): - from sys import version_info - - exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError - raise exc("No module named '_yaml'") -else: - from yaml._yaml import * - import warnings - warnings.warn( - 'The _yaml extension module is now located at yaml._yaml' - ' and its location is subject to change. To use the' - ' LibYAML-based parser and emitter, import from `yaml`:' - ' `from yaml import CLoader as Loader, CDumper as Dumper`.', - DeprecationWarning - ) - del warnings - # Don't `del yaml` here because yaml is actually an existing - # namespace member of _yaml. - -__name__ = '_yaml' -# If the module is top-level (i.e. not a part of any specific package) -# then the attribute should be set to ''. -# https://docs.python.org/3.8/library/types.html -__package__ = '' diff --git a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/METADATA b/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/METADATA deleted file mode 100644 index 9bf7a9e8..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/METADATA +++ /dev/null @@ -1,145 +0,0 @@ -Metadata-Version: 2.4 -Name: annotated-doc -Version: 0.0.4 -Summary: Document parameters, class attributes, return types, and variables inline, with Annotated. -Author-Email: =?utf-8?q?Sebasti=C3=A1n_Ram=C3=ADrez?= -License-Expression: MIT -License-File: LICENSE -Classifier: Intended Audience :: Information Technology -Classifier: Intended Audience :: System Administrators -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python -Classifier: Topic :: Internet -Classifier: Topic :: Software Development :: Libraries :: Application Frameworks -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Software Development -Classifier: Typing :: Typed -Classifier: Development Status :: 4 - Beta -Classifier: Intended Audience :: Developers -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Project-URL: Homepage, https://github.com/fastapi/annotated-doc -Project-URL: Documentation, https://github.com/fastapi/annotated-doc -Project-URL: Repository, https://github.com/fastapi/annotated-doc -Project-URL: Issues, https://github.com/fastapi/annotated-doc/issues -Project-URL: Changelog, https://github.com/fastapi/annotated-doc/release-notes.md -Requires-Python: >=3.8 -Description-Content-Type: text/markdown - -# Annotated Doc - -Document parameters, class attributes, return types, and variables inline, with `Annotated`. - - - Test - - - Coverage - - - Package version - - - Supported Python versions - - -## Installation - -```bash -pip install annotated-doc -``` - -Or with `uv`: - -```Python -uv add annotated-doc -``` - -## Usage - -Import `Doc` and pass a single literal string with the documentation for the specific parameter, class attribute, return type, or variable. - -For example, to document a parameter `name` in a function `hi` you could do: - -```Python -from typing import Annotated - -from annotated_doc import Doc - -def hi(name: Annotated[str, Doc("Who to say hi to")]) -> None: - print(f"Hi, {name}!") -``` - -You can also use it to document class attributes: - -```Python -from typing import Annotated - -from annotated_doc import Doc - -class User: - name: Annotated[str, Doc("The user's name")] - age: Annotated[int, Doc("The user's age")] -``` - -The same way, you could document return types and variables, or anything that could have a type annotation with `Annotated`. - -## Who Uses This - -`annotated-doc` was made for: - -* [FastAPI](https://fastapi.tiangolo.com/) -* [Typer](https://typer.tiangolo.com/) -* [SQLModel](https://sqlmodel.tiangolo.com/) -* [Asyncer](https://asyncer.tiangolo.com/) - -`annotated-doc` is supported by [griffe-typingdoc](https://github.com/mkdocstrings/griffe-typingdoc), which powers reference documentation like the one in the [FastAPI Reference](https://fastapi.tiangolo.com/reference/). - -## Reasons not to use `annotated-doc` - -You are already comfortable with one of the existing docstring formats, like: - -* Sphinx -* numpydoc -* Google -* Keras - -Your team is already comfortable using them. - -You prefer having the documentation about parameters all together in a docstring, separated from the code defining them. - -You care about a specific set of users, using one specific editor, and that editor already has support for the specific docstring format you use. - -## Reasons to use `annotated-doc` - -* No micro-syntax to learn for newcomers, it’s **just Python** syntax. -* **Editing** would be already fully supported by default by any editor (current or future) supporting Python syntax, including syntax errors, syntax highlighting, etc. -* **Rendering** would be relatively straightforward to implement by static tools (tools that don't need runtime execution), as the information can be extracted from the AST they normally already create. -* **Deduplication of information**: the name of a parameter would be defined in a single place, not duplicated inside of a docstring. -* **Elimination** of the possibility of having **inconsistencies** when removing a parameter or class variable and **forgetting to remove** its documentation. -* **Minimization** of the probability of adding a new parameter or class variable and **forgetting to add its documentation**. -* **Elimination** of the possibility of having **inconsistencies** between the **name** of a parameter in the **signature** and the name in the docstring when it is renamed. -* **Access** to the documentation string for each symbol at **runtime**, including existing (older) Python versions. -* A more formalized way to document other symbols, like type aliases, that could use Annotated. -* **Support** for apps using FastAPI, Typer and others. -* **AI Accessibility**: AI tools will have an easier way understanding each parameter as the distance from documentation to parameter is much closer. - -## History - -I ([@tiangolo](https://github.com/tiangolo)) originally wanted for this to be part of the Python standard library (in [PEP 727](https://peps.python.org/pep-0727/)), but the proposal was withdrawn as there was a fair amount of negative feedback and opposition. - -The conclusion was that this was better done as an external effort, in a third-party library. - -So, here it is, with a simpler approach, as a third-party library, in a way that can be used by others, starting with FastAPI and friends. - -## License - -This project is licensed under the terms of the MIT license. diff --git a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/RECORD b/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/RECORD deleted file mode 100644 index 549e005a..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/RECORD +++ /dev/null @@ -1,11 +0,0 @@ -annotated_doc-0.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -annotated_doc-0.0.4.dist-info/METADATA,sha256=Irm5KJua33dY2qKKAjJ-OhKaVBVIfwFGej_dSe3Z1TU,6566 -annotated_doc-0.0.4.dist-info/RECORD,, -annotated_doc-0.0.4.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90 -annotated_doc-0.0.4.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34 -annotated_doc-0.0.4.dist-info/licenses/LICENSE,sha256=__Fwd5pqy_ZavbQFwIfxzuF4ZpHkqWpANFF-SlBKDN8,1086 -annotated_doc/__init__.py,sha256=VuyxxUe80kfEyWnOrCx_Bk8hybo3aKo6RYBlkBBYW8k,52 -annotated_doc/__pycache__/__init__.cpython-312.pyc,, -annotated_doc/__pycache__/main.cpython-312.pyc,, -annotated_doc/main.py,sha256=5Zfvxv80SwwLqpRW73AZyZyiM4bWma9QWRbp_cgD20s,1075 -annotated_doc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/WHEEL deleted file mode 100644 index 045c8acd..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: pdm-backend (2.4.5) -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/entry_points.txt deleted file mode 100644 index c3ad4726..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/entry_points.txt +++ /dev/null @@ -1,4 +0,0 @@ -[console_scripts] - -[gui_scripts] - diff --git a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/licenses/LICENSE deleted file mode 100644 index 7a254464..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_doc-0.0.4.dist-info/licenses/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2025 Sebastián Ramírez - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/annotated_doc/__init__.py b/.venv/lib/python3.12/site-packages/annotated_doc/__init__.py deleted file mode 100644 index a0152a7d..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_doc/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .main import Doc as Doc - -__version__ = "0.0.4" diff --git a/.venv/lib/python3.12/site-packages/annotated_doc/main.py b/.venv/lib/python3.12/site-packages/annotated_doc/main.py deleted file mode 100644 index 7063c59e..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_doc/main.py +++ /dev/null @@ -1,36 +0,0 @@ -class Doc: - """Define the documentation of a type annotation using `Annotated`, to be - used in class attributes, function and method parameters, return values, - and variables. - - The value should be a positional-only string literal to allow static tools - like editors and documentation generators to use it. - - This complements docstrings. - - The string value passed is available in the attribute `documentation`. - - Example: - - ```Python - from typing import Annotated - from annotated_doc import Doc - - def hi(name: Annotated[str, Doc("Who to say hi to")]) -> None: - print(f"Hi, {name}!") - ``` - """ - - def __init__(self, documentation: str, /) -> None: - self.documentation = documentation - - def __repr__(self) -> str: - return f"Doc({self.documentation!r})" - - def __hash__(self) -> int: - return hash(self.documentation) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Doc): - return NotImplemented - return self.documentation == other.documentation diff --git a/.venv/lib/python3.12/site-packages/annotated_doc/py.typed b/.venv/lib/python3.12/site-packages/annotated_doc/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/METADATA deleted file mode 100644 index 3ac05cfd..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/METADATA +++ /dev/null @@ -1,295 +0,0 @@ -Metadata-Version: 2.3 -Name: annotated-types -Version: 0.7.0 -Summary: Reusable constraint types to use with typing.Annotated -Project-URL: Homepage, https://github.com/annotated-types/annotated-types -Project-URL: Source, https://github.com/annotated-types/annotated-types -Project-URL: Changelog, https://github.com/annotated-types/annotated-types/releases -Author-email: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, Samuel Colvin , Zac Hatfield-Dodds -License-File: LICENSE -Classifier: Development Status :: 4 - Beta -Classifier: Environment :: Console -Classifier: Environment :: MacOS X -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: Information Technology -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: POSIX :: Linux -Classifier: Operating System :: Unix -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Typing :: Typed -Requires-Python: >=3.8 -Requires-Dist: typing-extensions>=4.0.0; python_version < '3.9' -Description-Content-Type: text/markdown - -# annotated-types - -[![CI](https://github.com/annotated-types/annotated-types/workflows/CI/badge.svg?event=push)](https://github.com/annotated-types/annotated-types/actions?query=event%3Apush+branch%3Amain+workflow%3ACI) -[![pypi](https://img.shields.io/pypi/v/annotated-types.svg)](https://pypi.python.org/pypi/annotated-types) -[![versions](https://img.shields.io/pypi/pyversions/annotated-types.svg)](https://github.com/annotated-types/annotated-types) -[![license](https://img.shields.io/github/license/annotated-types/annotated-types.svg)](https://github.com/annotated-types/annotated-types/blob/main/LICENSE) - -[PEP-593](https://peps.python.org/pep-0593/) added `typing.Annotated` as a way of -adding context-specific metadata to existing types, and specifies that -`Annotated[T, x]` _should_ be treated as `T` by any tool or library without special -logic for `x`. - -This package provides metadata objects which can be used to represent common -constraints such as upper and lower bounds on scalar values and collection sizes, -a `Predicate` marker for runtime checks, and -descriptions of how we intend these metadata to be interpreted. In some cases, -we also note alternative representations which do not require this package. - -## Install - -```bash -pip install annotated-types -``` - -## Examples - -```python -from typing import Annotated -from annotated_types import Gt, Len, Predicate - -class MyClass: - age: Annotated[int, Gt(18)] # Valid: 19, 20, ... - # Invalid: 17, 18, "19", 19.0, ... - factors: list[Annotated[int, Predicate(is_prime)]] # Valid: 2, 3, 5, 7, 11, ... - # Invalid: 4, 8, -2, 5.0, "prime", ... - - my_list: Annotated[list[int], Len(0, 10)] # Valid: [], [10, 20, 30, 40, 50] - # Invalid: (1, 2), ["abc"], [0] * 20 -``` - -## Documentation - -_While `annotated-types` avoids runtime checks for performance, users should not -construct invalid combinations such as `MultipleOf("non-numeric")` or `Annotated[int, Len(3)]`. -Downstream implementors may choose to raise an error, emit a warning, silently ignore -a metadata item, etc., if the metadata objects described below are used with an -incompatible type - or for any other reason!_ - -### Gt, Ge, Lt, Le - -Express inclusive and/or exclusive bounds on orderable values - which may be numbers, -dates, times, strings, sets, etc. Note that the boundary value need not be of the -same type that was annotated, so long as they can be compared: `Annotated[int, Gt(1.5)]` -is fine, for example, and implies that the value is an integer x such that `x > 1.5`. - -We suggest that implementors may also interpret `functools.partial(operator.le, 1.5)` -as being equivalent to `Gt(1.5)`, for users who wish to avoid a runtime dependency on -the `annotated-types` package. - -To be explicit, these types have the following meanings: - -* `Gt(x)` - value must be "Greater Than" `x` - equivalent to exclusive minimum -* `Ge(x)` - value must be "Greater than or Equal" to `x` - equivalent to inclusive minimum -* `Lt(x)` - value must be "Less Than" `x` - equivalent to exclusive maximum -* `Le(x)` - value must be "Less than or Equal" to `x` - equivalent to inclusive maximum - -### Interval - -`Interval(gt, ge, lt, le)` allows you to specify an upper and lower bound with a single -metadata object. `None` attributes should be ignored, and non-`None` attributes -treated as per the single bounds above. - -### MultipleOf - -`MultipleOf(multiple_of=x)` might be interpreted in two ways: - -1. Python semantics, implying `value % multiple_of == 0`, or -2. [JSONschema semantics](https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.2.1), - where `int(value / multiple_of) == value / multiple_of`. - -We encourage users to be aware of these two common interpretations and their -distinct behaviours, especially since very large or non-integer numbers make -it easy to cause silent data corruption due to floating-point imprecision. - -We encourage libraries to carefully document which interpretation they implement. - -### MinLen, MaxLen, Len - -`Len()` implies that `min_length <= len(value) <= max_length` - lower and upper bounds are inclusive. - -As well as `Len()` which can optionally include upper and lower bounds, we also -provide `MinLen(x)` and `MaxLen(y)` which are equivalent to `Len(min_length=x)` -and `Len(max_length=y)` respectively. - -`Len`, `MinLen`, and `MaxLen` may be used with any type which supports `len(value)`. - -Examples of usage: - -* `Annotated[list, MaxLen(10)]` (or `Annotated[list, Len(max_length=10))`) - list must have a length of 10 or less -* `Annotated[str, MaxLen(10)]` - string must have a length of 10 or less -* `Annotated[list, MinLen(3))` (or `Annotated[list, Len(min_length=3))`) - list must have a length of 3 or more -* `Annotated[list, Len(4, 6)]` - list must have a length of 4, 5, or 6 -* `Annotated[list, Len(8, 8)]` - list must have a length of exactly 8 - -#### Changed in v0.4.0 - -* `min_inclusive` has been renamed to `min_length`, no change in meaning -* `max_exclusive` has been renamed to `max_length`, upper bound is now **inclusive** instead of **exclusive** -* The recommendation that slices are interpreted as `Len` has been removed due to ambiguity and different semantic - meaning of the upper bound in slices vs. `Len` - -See [issue #23](https://github.com/annotated-types/annotated-types/issues/23) for discussion. - -### Timezone - -`Timezone` can be used with a `datetime` or a `time` to express which timezones -are allowed. `Annotated[datetime, Timezone(None)]` must be a naive datetime. -`Timezone[...]` ([literal ellipsis](https://docs.python.org/3/library/constants.html#Ellipsis)) -expresses that any timezone-aware datetime is allowed. You may also pass a specific -timezone string or [`tzinfo`](https://docs.python.org/3/library/datetime.html#tzinfo-objects) -object such as `Timezone(timezone.utc)` or `Timezone("Africa/Abidjan")` to express that you only -allow a specific timezone, though we note that this is often a symptom of fragile design. - -#### Changed in v0.x.x - -* `Timezone` accepts [`tzinfo`](https://docs.python.org/3/library/datetime.html#tzinfo-objects) objects instead of - `timezone`, extending compatibility to [`zoneinfo`](https://docs.python.org/3/library/zoneinfo.html) and third party libraries. - -### Unit - -`Unit(unit: str)` expresses that the annotated numeric value is the magnitude of -a quantity with the specified unit. For example, `Annotated[float, Unit("m/s")]` -would be a float representing a velocity in meters per second. - -Please note that `annotated_types` itself makes no attempt to parse or validate -the unit string in any way. That is left entirely to downstream libraries, -such as [`pint`](https://pint.readthedocs.io) or -[`astropy.units`](https://docs.astropy.org/en/stable/units/). - -An example of how a library might use this metadata: - -```python -from annotated_types import Unit -from typing import Annotated, TypeVar, Callable, Any, get_origin, get_args - -# given a type annotated with a unit: -Meters = Annotated[float, Unit("m")] - - -# you can cast the annotation to a specific unit type with any -# callable that accepts a string and returns the desired type -T = TypeVar("T") -def cast_unit(tp: Any, unit_cls: Callable[[str], T]) -> T | None: - if get_origin(tp) is Annotated: - for arg in get_args(tp): - if isinstance(arg, Unit): - return unit_cls(arg.unit) - return None - - -# using `pint` -import pint -pint_unit = cast_unit(Meters, pint.Unit) - - -# using `astropy.units` -import astropy.units as u -astropy_unit = cast_unit(Meters, u.Unit) -``` - -### Predicate - -`Predicate(func: Callable)` expresses that `func(value)` is truthy for valid values. -Users should prefer the statically inspectable metadata above, but if you need -the full power and flexibility of arbitrary runtime predicates... here it is. - -For some common constraints, we provide generic types: - -* `IsLower = Annotated[T, Predicate(str.islower)]` -* `IsUpper = Annotated[T, Predicate(str.isupper)]` -* `IsDigit = Annotated[T, Predicate(str.isdigit)]` -* `IsFinite = Annotated[T, Predicate(math.isfinite)]` -* `IsNotFinite = Annotated[T, Predicate(Not(math.isfinite))]` -* `IsNan = Annotated[T, Predicate(math.isnan)]` -* `IsNotNan = Annotated[T, Predicate(Not(math.isnan))]` -* `IsInfinite = Annotated[T, Predicate(math.isinf)]` -* `IsNotInfinite = Annotated[T, Predicate(Not(math.isinf))]` - -so that you can write e.g. `x: IsFinite[float] = 2.0` instead of the longer -(but exactly equivalent) `x: Annotated[float, Predicate(math.isfinite)] = 2.0`. - -Some libraries might have special logic to handle known or understandable predicates, -for example by checking for `str.isdigit` and using its presence to both call custom -logic to enforce digit-only strings, and customise some generated external schema. -Users are therefore encouraged to avoid indirection like `lambda s: s.lower()`, in -favor of introspectable methods such as `str.lower` or `re.compile("pattern").search`. - -To enable basic negation of commonly used predicates like `math.isnan` without introducing introspection that makes it impossible for implementers to introspect the predicate we provide a `Not` wrapper that simply negates the predicate in an introspectable manner. Several of the predicates listed above are created in this manner. - -We do not specify what behaviour should be expected for predicates that raise -an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently -skip invalid constraints, or statically raise an error; or it might try calling it -and then propagate or discard the resulting -`TypeError: descriptor 'isdigit' for 'str' objects doesn't apply to a 'int' object` -exception. We encourage libraries to document the behaviour they choose. - -### Doc - -`doc()` can be used to add documentation information in `Annotated`, for function and method parameters, variables, class attributes, return types, and any place where `Annotated` can be used. - -It expects a value that can be statically analyzed, as the main use case is for static analysis, editors, documentation generators, and similar tools. - -It returns a `DocInfo` class with a single attribute `documentation` containing the value passed to `doc()`. - -This is the early adopter's alternative form of the [`typing-doc` proposal](https://github.com/tiangolo/fastapi/blob/typing-doc/typing_doc.md). - -### Integrating downstream types with `GroupedMetadata` - -Implementers may choose to provide a convenience wrapper that groups multiple pieces of metadata. -This can help reduce verbosity and cognitive overhead for users. -For example, an implementer like Pydantic might provide a `Field` or `Meta` type that accepts keyword arguments and transforms these into low-level metadata: - -```python -from dataclasses import dataclass -from typing import Iterator -from annotated_types import GroupedMetadata, Ge - -@dataclass -class Field(GroupedMetadata): - ge: int | None = None - description: str | None = None - - def __iter__(self) -> Iterator[object]: - # Iterating over a GroupedMetadata object should yield annotated-types - # constraint metadata objects which describe it as fully as possible, - # and may include other unknown objects too. - if self.ge is not None: - yield Ge(self.ge) - if self.description is not None: - yield Description(self.description) -``` - -Libraries consuming annotated-types constraints should check for `GroupedMetadata` and unpack it by iterating over the object and treating the results as if they had been "unpacked" in the `Annotated` type. The same logic should be applied to the [PEP 646 `Unpack` type](https://peps.python.org/pep-0646/), so that `Annotated[T, Field(...)]`, `Annotated[T, Unpack[Field(...)]]` and `Annotated[T, *Field(...)]` are all treated consistently. - -Libraries consuming annotated-types should also ignore any metadata they do not recongize that came from unpacking a `GroupedMetadata`, just like they ignore unrecognized metadata in `Annotated` itself. - -Our own `annotated_types.Interval` class is a `GroupedMetadata` which unpacks itself into `Gt`, `Lt`, etc., so this is not an abstract concern. Similarly, `annotated_types.Len` is a `GroupedMetadata` which unpacks itself into `MinLen` (optionally) and `MaxLen`. - -### Consuming metadata - -We intend to not be prescriptive as to _how_ the metadata and constraints are used, but as an example of how one might parse constraints from types annotations see our [implementation in `test_main.py`](https://github.com/annotated-types/annotated-types/blob/f59cf6d1b5255a0fe359b93896759a180bec30ae/tests/test_main.py#L94-L103). - -It is up to the implementer to determine how this metadata is used. -You could use the metadata for runtime type checking, for generating schemas or to generate example data, amongst other use cases. - -## Design & History - -This package was designed at the PyCon 2022 sprints by the maintainers of Pydantic -and Hypothesis, with the goal of making it as easy as possible for end-users to -provide more informative annotations for use by runtime libraries. - -It is deliberately minimal, and following PEP-593 allows considerable downstream -discretion in what (if anything!) they choose to support. Nonetheless, we expect -that staying simple and covering _only_ the most common use-cases will give users -and maintainers the best experience we can. If you'd like more constraints for your -types - follow our lead, by defining them and documenting them downstream! diff --git a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/RECORD deleted file mode 100644 index a66e2783..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/RECORD +++ /dev/null @@ -1,10 +0,0 @@ -annotated_types-0.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -annotated_types-0.7.0.dist-info/METADATA,sha256=7ltqxksJJ0wCYFGBNIQCWTlWQGeAH0hRFdnK3CB895E,15046 -annotated_types-0.7.0.dist-info/RECORD,, -annotated_types-0.7.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87 -annotated_types-0.7.0.dist-info/licenses/LICENSE,sha256=_hBJiEsaDZNCkB6I4H8ykl0ksxIdmXK2poBfuYJLCV0,1083 -annotated_types/__init__.py,sha256=RynLsRKUEGI0KimXydlD1fZEfEzWwDo0Uon3zOKhG1Q,13819 -annotated_types/__pycache__/__init__.cpython-312.pyc,, -annotated_types/__pycache__/test_cases.cpython-312.pyc,, -annotated_types/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -annotated_types/test_cases.py,sha256=zHFX6EpcMbGJ8FzBYDbO56bPwx_DYIVSKbZM-4B3_lg,6421 diff --git a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/WHEEL deleted file mode 100644 index 516596c7..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.24.2 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE deleted file mode 100644 index d99323a9..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2022 the contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/annotated_types/__init__.py b/.venv/lib/python3.12/site-packages/annotated_types/__init__.py deleted file mode 100644 index 74e0deea..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_types/__init__.py +++ /dev/null @@ -1,432 +0,0 @@ -import math -import sys -import types -from dataclasses import dataclass -from datetime import tzinfo -from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, SupportsFloat, SupportsIndex, TypeVar, Union - -if sys.version_info < (3, 8): - from typing_extensions import Protocol, runtime_checkable -else: - from typing import Protocol, runtime_checkable - -if sys.version_info < (3, 9): - from typing_extensions import Annotated, Literal -else: - from typing import Annotated, Literal - -if sys.version_info < (3, 10): - EllipsisType = type(Ellipsis) - KW_ONLY = {} - SLOTS = {} -else: - from types import EllipsisType - - KW_ONLY = {"kw_only": True} - SLOTS = {"slots": True} - - -__all__ = ( - 'BaseMetadata', - 'GroupedMetadata', - 'Gt', - 'Ge', - 'Lt', - 'Le', - 'Interval', - 'MultipleOf', - 'MinLen', - 'MaxLen', - 'Len', - 'Timezone', - 'Predicate', - 'LowerCase', - 'UpperCase', - 'IsDigits', - 'IsFinite', - 'IsNotFinite', - 'IsNan', - 'IsNotNan', - 'IsInfinite', - 'IsNotInfinite', - 'doc', - 'DocInfo', - '__version__', -) - -__version__ = '0.7.0' - - -T = TypeVar('T') - - -# arguments that start with __ are considered -# positional only -# see https://peps.python.org/pep-0484/#positional-only-arguments - - -class SupportsGt(Protocol): - def __gt__(self: T, __other: T) -> bool: - ... - - -class SupportsGe(Protocol): - def __ge__(self: T, __other: T) -> bool: - ... - - -class SupportsLt(Protocol): - def __lt__(self: T, __other: T) -> bool: - ... - - -class SupportsLe(Protocol): - def __le__(self: T, __other: T) -> bool: - ... - - -class SupportsMod(Protocol): - def __mod__(self: T, __other: T) -> T: - ... - - -class SupportsDiv(Protocol): - def __div__(self: T, __other: T) -> T: - ... - - -class BaseMetadata: - """Base class for all metadata. - - This exists mainly so that implementers - can do `isinstance(..., BaseMetadata)` while traversing field annotations. - """ - - __slots__ = () - - -@dataclass(frozen=True, **SLOTS) -class Gt(BaseMetadata): - """Gt(gt=x) implies that the value must be greater than x. - - It can be used with any type that supports the ``>`` operator, - including numbers, dates and times, strings, sets, and so on. - """ - - gt: SupportsGt - - -@dataclass(frozen=True, **SLOTS) -class Ge(BaseMetadata): - """Ge(ge=x) implies that the value must be greater than or equal to x. - - It can be used with any type that supports the ``>=`` operator, - including numbers, dates and times, strings, sets, and so on. - """ - - ge: SupportsGe - - -@dataclass(frozen=True, **SLOTS) -class Lt(BaseMetadata): - """Lt(lt=x) implies that the value must be less than x. - - It can be used with any type that supports the ``<`` operator, - including numbers, dates and times, strings, sets, and so on. - """ - - lt: SupportsLt - - -@dataclass(frozen=True, **SLOTS) -class Le(BaseMetadata): - """Le(le=x) implies that the value must be less than or equal to x. - - It can be used with any type that supports the ``<=`` operator, - including numbers, dates and times, strings, sets, and so on. - """ - - le: SupportsLe - - -@runtime_checkable -class GroupedMetadata(Protocol): - """A grouping of multiple objects, like typing.Unpack. - - `GroupedMetadata` on its own is not metadata and has no meaning. - All of the constraints and metadata should be fully expressable - in terms of the `BaseMetadata`'s returned by `GroupedMetadata.__iter__()`. - - Concrete implementations should override `GroupedMetadata.__iter__()` - to add their own metadata. - For example: - - >>> @dataclass - >>> class Field(GroupedMetadata): - >>> gt: float | None = None - >>> description: str | None = None - ... - >>> def __iter__(self) -> Iterable[object]: - >>> if self.gt is not None: - >>> yield Gt(self.gt) - >>> if self.description is not None: - >>> yield Description(self.gt) - - Also see the implementation of `Interval` below for an example. - - Parsers should recognize this and unpack it so that it can be used - both with and without unpacking: - - - `Annotated[int, Field(...)]` (parser must unpack Field) - - `Annotated[int, *Field(...)]` (PEP-646) - """ # noqa: trailing-whitespace - - @property - def __is_annotated_types_grouped_metadata__(self) -> Literal[True]: - return True - - def __iter__(self) -> Iterator[object]: - ... - - if not TYPE_CHECKING: - __slots__ = () # allow subclasses to use slots - - def __init_subclass__(cls, *args: Any, **kwargs: Any) -> None: - # Basic ABC like functionality without the complexity of an ABC - super().__init_subclass__(*args, **kwargs) - if cls.__iter__ is GroupedMetadata.__iter__: - raise TypeError("Can't subclass GroupedMetadata without implementing __iter__") - - def __iter__(self) -> Iterator[object]: # noqa: F811 - raise NotImplementedError # more helpful than "None has no attribute..." type errors - - -@dataclass(frozen=True, **KW_ONLY, **SLOTS) -class Interval(GroupedMetadata): - """Interval can express inclusive or exclusive bounds with a single object. - - It accepts keyword arguments ``gt``, ``ge``, ``lt``, and/or ``le``, which - are interpreted the same way as the single-bound constraints. - """ - - gt: Union[SupportsGt, None] = None - ge: Union[SupportsGe, None] = None - lt: Union[SupportsLt, None] = None - le: Union[SupportsLe, None] = None - - def __iter__(self) -> Iterator[BaseMetadata]: - """Unpack an Interval into zero or more single-bounds.""" - if self.gt is not None: - yield Gt(self.gt) - if self.ge is not None: - yield Ge(self.ge) - if self.lt is not None: - yield Lt(self.lt) - if self.le is not None: - yield Le(self.le) - - -@dataclass(frozen=True, **SLOTS) -class MultipleOf(BaseMetadata): - """MultipleOf(multiple_of=x) might be interpreted in two ways: - - 1. Python semantics, implying ``value % multiple_of == 0``, or - 2. JSONschema semantics, where ``int(value / multiple_of) == value / multiple_of`` - - We encourage users to be aware of these two common interpretations, - and libraries to carefully document which they implement. - """ - - multiple_of: Union[SupportsDiv, SupportsMod] - - -@dataclass(frozen=True, **SLOTS) -class MinLen(BaseMetadata): - """ - MinLen() implies minimum inclusive length, - e.g. ``len(value) >= min_length``. - """ - - min_length: Annotated[int, Ge(0)] - - -@dataclass(frozen=True, **SLOTS) -class MaxLen(BaseMetadata): - """ - MaxLen() implies maximum inclusive length, - e.g. ``len(value) <= max_length``. - """ - - max_length: Annotated[int, Ge(0)] - - -@dataclass(frozen=True, **SLOTS) -class Len(GroupedMetadata): - """ - Len() implies that ``min_length <= len(value) <= max_length``. - - Upper bound may be omitted or ``None`` to indicate no upper length bound. - """ - - min_length: Annotated[int, Ge(0)] = 0 - max_length: Optional[Annotated[int, Ge(0)]] = None - - def __iter__(self) -> Iterator[BaseMetadata]: - """Unpack a Len into zone or more single-bounds.""" - if self.min_length > 0: - yield MinLen(self.min_length) - if self.max_length is not None: - yield MaxLen(self.max_length) - - -@dataclass(frozen=True, **SLOTS) -class Timezone(BaseMetadata): - """Timezone(tz=...) requires a datetime to be aware (or ``tz=None``, naive). - - ``Annotated[datetime, Timezone(None)]`` must be a naive datetime. - ``Timezone[...]`` (the ellipsis literal) expresses that the datetime must be - tz-aware but any timezone is allowed. - - You may also pass a specific timezone string or tzinfo object such as - ``Timezone(timezone.utc)`` or ``Timezone("Africa/Abidjan")`` to express that - you only allow a specific timezone, though we note that this is often - a symptom of poor design. - """ - - tz: Union[str, tzinfo, EllipsisType, None] - - -@dataclass(frozen=True, **SLOTS) -class Unit(BaseMetadata): - """Indicates that the value is a physical quantity with the specified unit. - - It is intended for usage with numeric types, where the value represents the - magnitude of the quantity. For example, ``distance: Annotated[float, Unit('m')]`` - or ``speed: Annotated[float, Unit('m/s')]``. - - Interpretation of the unit string is left to the discretion of the consumer. - It is suggested to follow conventions established by python libraries that work - with physical quantities, such as - - - ``pint`` : - - ``astropy.units``: - - For indicating a quantity with a certain dimensionality but without a specific unit - it is recommended to use square brackets, e.g. `Annotated[float, Unit('[time]')]`. - Note, however, ``annotated_types`` itself makes no use of the unit string. - """ - - unit: str - - -@dataclass(frozen=True, **SLOTS) -class Predicate(BaseMetadata): - """``Predicate(func: Callable)`` implies `func(value)` is truthy for valid values. - - Users should prefer statically inspectable metadata, but if you need the full - power and flexibility of arbitrary runtime predicates... here it is. - - We provide a few predefined predicates for common string constraints: - ``IsLower = Predicate(str.islower)``, ``IsUpper = Predicate(str.isupper)``, and - ``IsDigits = Predicate(str.isdigit)``. Users are encouraged to use methods which - can be given special handling, and avoid indirection like ``lambda s: s.lower()``. - - Some libraries might have special logic to handle certain predicates, e.g. by - checking for `str.isdigit` and using its presence to both call custom logic to - enforce digit-only strings, and customise some generated external schema. - - We do not specify what behaviour should be expected for predicates that raise - an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently - skip invalid constraints, or statically raise an error; or it might try calling it - and then propagate or discard the resulting exception. - """ - - func: Callable[[Any], bool] - - def __repr__(self) -> str: - if getattr(self.func, "__name__", "") == "": - return f"{self.__class__.__name__}({self.func!r})" - if isinstance(self.func, (types.MethodType, types.BuiltinMethodType)) and ( - namespace := getattr(self.func.__self__, "__name__", None) - ): - return f"{self.__class__.__name__}({namespace}.{self.func.__name__})" - if isinstance(self.func, type(str.isascii)): # method descriptor - return f"{self.__class__.__name__}({self.func.__qualname__})" - return f"{self.__class__.__name__}({self.func.__name__})" - - -@dataclass -class Not: - func: Callable[[Any], bool] - - def __call__(self, __v: Any) -> bool: - return not self.func(__v) - - -_StrType = TypeVar("_StrType", bound=str) - -LowerCase = Annotated[_StrType, Predicate(str.islower)] -""" -Return True if the string is a lowercase string, False otherwise. - -A string is lowercase if all cased characters in the string are lowercase and there is at least one cased character in the string. -""" # noqa: E501 -UpperCase = Annotated[_StrType, Predicate(str.isupper)] -""" -Return True if the string is an uppercase string, False otherwise. - -A string is uppercase if all cased characters in the string are uppercase and there is at least one cased character in the string. -""" # noqa: E501 -IsDigit = Annotated[_StrType, Predicate(str.isdigit)] -IsDigits = IsDigit # type: ignore # plural for backwards compatibility, see #63 -""" -Return True if the string is a digit string, False otherwise. - -A string is a digit string if all characters in the string are digits and there is at least one character in the string. -""" # noqa: E501 -IsAscii = Annotated[_StrType, Predicate(str.isascii)] -""" -Return True if all characters in the string are ASCII, False otherwise. - -ASCII characters have code points in the range U+0000-U+007F. Empty string is ASCII too. -""" - -_NumericType = TypeVar('_NumericType', bound=Union[SupportsFloat, SupportsIndex]) -IsFinite = Annotated[_NumericType, Predicate(math.isfinite)] -"""Return True if x is neither an infinity nor a NaN, and False otherwise.""" -IsNotFinite = Annotated[_NumericType, Predicate(Not(math.isfinite))] -"""Return True if x is one of infinity or NaN, and False otherwise""" -IsNan = Annotated[_NumericType, Predicate(math.isnan)] -"""Return True if x is a NaN (not a number), and False otherwise.""" -IsNotNan = Annotated[_NumericType, Predicate(Not(math.isnan))] -"""Return True if x is anything but NaN (not a number), and False otherwise.""" -IsInfinite = Annotated[_NumericType, Predicate(math.isinf)] -"""Return True if x is a positive or negative infinity, and False otherwise.""" -IsNotInfinite = Annotated[_NumericType, Predicate(Not(math.isinf))] -"""Return True if x is neither a positive or negative infinity, and False otherwise.""" - -try: - from typing_extensions import DocInfo, doc # type: ignore [attr-defined] -except ImportError: - - @dataclass(frozen=True, **SLOTS) - class DocInfo: # type: ignore [no-redef] - """ " - The return value of doc(), mainly to be used by tools that want to extract the - Annotated documentation at runtime. - """ - - documentation: str - """The documentation string passed to doc().""" - - def doc( - documentation: str, - ) -> DocInfo: - """ - Add documentation to a type annotation inside of Annotated. - - For example: - - >>> def hi(name: Annotated[int, doc("The name of the user")]) -> None: ... - """ - return DocInfo(documentation) diff --git a/.venv/lib/python3.12/site-packages/annotated_types/py.typed b/.venv/lib/python3.12/site-packages/annotated_types/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/annotated_types/test_cases.py b/.venv/lib/python3.12/site-packages/annotated_types/test_cases.py deleted file mode 100644 index d9164d68..00000000 --- a/.venv/lib/python3.12/site-packages/annotated_types/test_cases.py +++ /dev/null @@ -1,151 +0,0 @@ -import math -import sys -from datetime import date, datetime, timedelta, timezone -from decimal import Decimal -from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Set, Tuple - -if sys.version_info < (3, 9): - from typing_extensions import Annotated -else: - from typing import Annotated - -import annotated_types as at - - -class Case(NamedTuple): - """ - A test case for `annotated_types`. - """ - - annotation: Any - valid_cases: Iterable[Any] - invalid_cases: Iterable[Any] - - -def cases() -> Iterable[Case]: - # Gt, Ge, Lt, Le - yield Case(Annotated[int, at.Gt(4)], (5, 6, 1000), (4, 0, -1)) - yield Case(Annotated[float, at.Gt(0.5)], (0.6, 0.7, 0.8, 0.9), (0.5, 0.0, -0.1)) - yield Case( - Annotated[datetime, at.Gt(datetime(2000, 1, 1))], - [datetime(2000, 1, 2), datetime(2000, 1, 3)], - [datetime(2000, 1, 1), datetime(1999, 12, 31)], - ) - yield Case( - Annotated[datetime, at.Gt(date(2000, 1, 1))], - [date(2000, 1, 2), date(2000, 1, 3)], - [date(2000, 1, 1), date(1999, 12, 31)], - ) - yield Case( - Annotated[datetime, at.Gt(Decimal('1.123'))], - [Decimal('1.1231'), Decimal('123')], - [Decimal('1.123'), Decimal('0')], - ) - - yield Case(Annotated[int, at.Ge(4)], (4, 5, 6, 1000, 4), (0, -1)) - yield Case(Annotated[float, at.Ge(0.5)], (0.5, 0.6, 0.7, 0.8, 0.9), (0.4, 0.0, -0.1)) - yield Case( - Annotated[datetime, at.Ge(datetime(2000, 1, 1))], - [datetime(2000, 1, 2), datetime(2000, 1, 3)], - [datetime(1998, 1, 1), datetime(1999, 12, 31)], - ) - - yield Case(Annotated[int, at.Lt(4)], (0, -1), (4, 5, 6, 1000, 4)) - yield Case(Annotated[float, at.Lt(0.5)], (0.4, 0.0, -0.1), (0.5, 0.6, 0.7, 0.8, 0.9)) - yield Case( - Annotated[datetime, at.Lt(datetime(2000, 1, 1))], - [datetime(1999, 12, 31), datetime(1999, 12, 31)], - [datetime(2000, 1, 2), datetime(2000, 1, 3)], - ) - - yield Case(Annotated[int, at.Le(4)], (4, 0, -1), (5, 6, 1000)) - yield Case(Annotated[float, at.Le(0.5)], (0.5, 0.0, -0.1), (0.6, 0.7, 0.8, 0.9)) - yield Case( - Annotated[datetime, at.Le(datetime(2000, 1, 1))], - [datetime(2000, 1, 1), datetime(1999, 12, 31)], - [datetime(2000, 1, 2), datetime(2000, 1, 3)], - ) - - # Interval - yield Case(Annotated[int, at.Interval(gt=4)], (5, 6, 1000), (4, 0, -1)) - yield Case(Annotated[int, at.Interval(gt=4, lt=10)], (5, 6), (4, 10, 1000, 0, -1)) - yield Case(Annotated[float, at.Interval(ge=0.5, le=1)], (0.5, 0.9, 1), (0.49, 1.1)) - yield Case( - Annotated[datetime, at.Interval(gt=datetime(2000, 1, 1), le=datetime(2000, 1, 3))], - [datetime(2000, 1, 2), datetime(2000, 1, 3)], - [datetime(2000, 1, 1), datetime(2000, 1, 4)], - ) - - yield Case(Annotated[int, at.MultipleOf(multiple_of=3)], (0, 3, 9), (1, 2, 4)) - yield Case(Annotated[float, at.MultipleOf(multiple_of=0.5)], (0, 0.5, 1, 1.5), (0.4, 1.1)) - - # lengths - - yield Case(Annotated[str, at.MinLen(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) - yield Case(Annotated[str, at.Len(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) - yield Case(Annotated[List[int], at.MinLen(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) - yield Case(Annotated[List[int], at.Len(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) - - yield Case(Annotated[str, at.MaxLen(4)], ('', '1234'), ('12345', 'x' * 10)) - yield Case(Annotated[str, at.Len(0, 4)], ('', '1234'), ('12345', 'x' * 10)) - yield Case(Annotated[List[str], at.MaxLen(4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) - yield Case(Annotated[List[str], at.Len(0, 4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) - - yield Case(Annotated[str, at.Len(3, 5)], ('123', '12345'), ('', '1', '12', '123456', 'x' * 10)) - yield Case(Annotated[str, at.Len(3, 3)], ('123',), ('12', '1234')) - - yield Case(Annotated[Dict[int, int], at.Len(2, 3)], [{1: 1, 2: 2}], [{}, {1: 1}, {1: 1, 2: 2, 3: 3, 4: 4}]) - yield Case(Annotated[Set[int], at.Len(2, 3)], ({1, 2}, {1, 2, 3}), (set(), {1}, {1, 2, 3, 4})) - yield Case(Annotated[Tuple[int, ...], at.Len(2, 3)], ((1, 2), (1, 2, 3)), ((), (1,), (1, 2, 3, 4))) - - # Timezone - - yield Case( - Annotated[datetime, at.Timezone(None)], [datetime(2000, 1, 1)], [datetime(2000, 1, 1, tzinfo=timezone.utc)] - ) - yield Case( - Annotated[datetime, at.Timezone(...)], [datetime(2000, 1, 1, tzinfo=timezone.utc)], [datetime(2000, 1, 1)] - ) - yield Case( - Annotated[datetime, at.Timezone(timezone.utc)], - [datetime(2000, 1, 1, tzinfo=timezone.utc)], - [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], - ) - yield Case( - Annotated[datetime, at.Timezone('Europe/London')], - [datetime(2000, 1, 1, tzinfo=timezone(timedelta(0), name='Europe/London'))], - [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], - ) - - # Quantity - - yield Case(Annotated[float, at.Unit(unit='m')], (5, 4.2), ('5m', '4.2m')) - - # predicate types - - yield Case(at.LowerCase[str], ['abc', 'foobar'], ['', 'A', 'Boom']) - yield Case(at.UpperCase[str], ['ABC', 'DEFO'], ['', 'a', 'abc', 'AbC']) - yield Case(at.IsDigit[str], ['123'], ['', 'ab', 'a1b2']) - yield Case(at.IsAscii[str], ['123', 'foo bar'], ['£100', '😊', 'whatever 👀']) - - yield Case(Annotated[int, at.Predicate(lambda x: x % 2 == 0)], [0, 2, 4], [1, 3, 5]) - - yield Case(at.IsFinite[float], [1.23], [math.nan, math.inf, -math.inf]) - yield Case(at.IsNotFinite[float], [math.nan, math.inf], [1.23]) - yield Case(at.IsNan[float], [math.nan], [1.23, math.inf]) - yield Case(at.IsNotNan[float], [1.23, math.inf], [math.nan]) - yield Case(at.IsInfinite[float], [math.inf], [math.nan, 1.23]) - yield Case(at.IsNotInfinite[float], [math.nan, 1.23], [math.inf]) - - # check stacked predicates - yield Case(at.IsInfinite[Annotated[float, at.Predicate(lambda x: x > 0)]], [math.inf], [-math.inf, 1.23, math.nan]) - - # doc - yield Case(Annotated[int, at.doc("A number")], [1, 2], []) - - # custom GroupedMetadata - class MyCustomGroupedMetadata(at.GroupedMetadata): - def __iter__(self) -> Iterator[at.Predicate]: - yield at.Predicate(lambda x: float(x).is_integer()) - - yield Case(Annotated[float, MyCustomGroupedMetadata()], [0, 2.0], [0.01, 1.5]) diff --git a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/METADATA deleted file mode 100644 index 341ffa6c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/METADATA +++ /dev/null @@ -1,97 +0,0 @@ -Metadata-Version: 2.3 -Name: anthropic -Version: 0.97.0 -Summary: The official Python library for the anthropic API -Project-URL: Homepage, https://github.com/anthropics/anthropic-sdk-python -Project-URL: Repository, https://github.com/anthropics/anthropic-sdk-python -Author-email: Anthropic -License: MIT -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: MacOS -Classifier: Operating System :: Microsoft :: Windows -Classifier: Operating System :: OS Independent -Classifier: Operating System :: POSIX -Classifier: Operating System :: POSIX :: Linux -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Typing :: Typed -Requires-Python: >=3.9 -Requires-Dist: anyio<5,>=3.5.0 -Requires-Dist: distro<2,>=1.7.0 -Requires-Dist: docstring-parser<1,>=0.15 -Requires-Dist: httpx<1,>=0.25.0 -Requires-Dist: jiter<1,>=0.4.0 -Requires-Dist: pydantic<3,>=1.9.0 -Requires-Dist: sniffio -Requires-Dist: typing-extensions<5,>=4.14 -Provides-Extra: aiohttp -Requires-Dist: aiohttp; extra == 'aiohttp' -Requires-Dist: httpx-aiohttp>=0.1.9; extra == 'aiohttp' -Provides-Extra: aws -Requires-Dist: boto3>=1.28.57; extra == 'aws' -Requires-Dist: botocore>=1.31.57; extra == 'aws' -Provides-Extra: bedrock -Requires-Dist: boto3>=1.28.57; extra == 'bedrock' -Requires-Dist: botocore>=1.31.57; extra == 'bedrock' -Provides-Extra: mcp -Requires-Dist: mcp>=1.0; (python_version >= '3.10') and extra == 'mcp' -Provides-Extra: vertex -Requires-Dist: google-auth[requests]<3,>=2; extra == 'vertex' -Description-Content-Type: text/markdown - -# Claude SDK for Python - -[![PyPI version](https://img.shields.io/pypi/v/anthropic.svg)](https://pypi.org/project/anthropic/) - -The Claude SDK for Python provides access to the [Claude API](https://docs.anthropic.com/en/api/) from Python applications. - -## Documentation - -Full documentation is available at **[platform.claude.com/docs/en/api/sdks/python](https://platform.claude.com/docs/en/api/sdks/python)**. - -## Installation - -```sh -pip install anthropic -``` - -## Getting started - -```python -import os -from anthropic import Anthropic - -client = Anthropic( - api_key=os.environ.get("ANTHROPIC_API_KEY"), # This is the default and can be omitted -) - -message = client.messages.create( - max_tokens=1024, - messages=[ - { - "role": "user", - "content": "Hello, Claude", - } - ], - model="claude-opus-4-6", -) -print(message.content) -``` - -## Requirements - -Python 3.9+ - -## Contributing - -See [CONTRIBUTING.md](https://github.com/anthropics/anthropic-sdk-python/tree/main/./CONTRIBUTING.md). - -## License - -This project is licensed under the MIT License. See the [LICENSE](https://github.com/anthropics/anthropic-sdk-python/tree/main/LICENSE) file for details. diff --git a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/RECORD deleted file mode 100644 index e1e628fb..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/RECORD +++ /dev/null @@ -1,1575 +0,0 @@ -anthropic-0.97.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -anthropic-0.97.0.dist-info/METADATA,sha256=G_kr_lmvzVR_kIVhKFmkh1B7hzZ7IhkhJi2ynwi0GtM,3106 -anthropic-0.97.0.dist-info/RECORD,, -anthropic-0.97.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -anthropic-0.97.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87 -anthropic-0.97.0.dist-info/licenses/LICENSE,sha256=i_lphP-Lz65-SMrnalKeiiUxe6ngKr9_08xk_flWV6Y,1056 -anthropic/__init__.py,sha256=uhjK6ZnOzbTdLywwopoTHYQyT4grPLmZnyOjuWu7y4k,3257 -anthropic/__pycache__/__init__.cpython-312.pyc,, -anthropic/__pycache__/_base_client.cpython-312.pyc,, -anthropic/__pycache__/_client.cpython-312.pyc,, -anthropic/__pycache__/_compat.cpython-312.pyc,, -anthropic/__pycache__/_constants.cpython-312.pyc,, -anthropic/__pycache__/_exceptions.cpython-312.pyc,, -anthropic/__pycache__/_files.cpython-312.pyc,, -anthropic/__pycache__/_legacy_response.cpython-312.pyc,, -anthropic/__pycache__/_models.cpython-312.pyc,, -anthropic/__pycache__/_qs.cpython-312.pyc,, -anthropic/__pycache__/_resource.cpython-312.pyc,, -anthropic/__pycache__/_response.cpython-312.pyc,, -anthropic/__pycache__/_streaming.cpython-312.pyc,, -anthropic/__pycache__/_types.cpython-312.pyc,, -anthropic/__pycache__/_version.cpython-312.pyc,, -anthropic/__pycache__/pagination.cpython-312.pyc,, -anthropic/_base_client.py,sha256=3LYvmUM_VNliEGqzr1i_2vMHS-7cfCXHSG-eCXeqSlw,79807 -anthropic/_client.py,sha256=Grhc_ib0IVSHor1u3VuK5PGkT2Pg-XRJnuUhoSd6udc,23103 -anthropic/_compat.py,sha256=AE1OHRhWI-0KjpZbrUh-FpLC0v-M-zNnuh0EkAAZJaY,7024 -anthropic/_constants.py,sha256=wADeUqY3lsseF0L6jIen-PexfQ06FOtf2dVESXDM828,885 -anthropic/_decoders/__pycache__/jsonl.cpython-312.pyc,, -anthropic/_decoders/jsonl.py,sha256=KDLw-Frjo7gRup5qDp_BWkXIZ-mFZU5vFDz0WBhEKcs,3510 -anthropic/_exceptions.py,sha256=mnL3Bn_zUJ3WG-dRTzE0gVmEw4k1nqCgitc6qp-1alc,4404 -anthropic/_files.py,sha256=VJZbANaezqUi-tKbjzuDrf_cYNzoB4imk9S4tTvPKIw,5530 -anthropic/_legacy_response.py,sha256=3dDbL9oAd4MexnhvfnIKIT6LCINevjADWSND-pM2lMQ,17372 -anthropic/_models.py,sha256=PA7IEJqfXlAsMdTA__Q5QeUhfpivLGv2JOEDMFo-F88,34026 -anthropic/_qs.py,sha256=ExvESqZz_a5ZALZG1aR2rkzE35o9Rmk0yPMQtKN0DTo,4924 -anthropic/_resource.py,sha256=FYEOzfhB-XWTR2gyTmQuuFoecRiVXxe_SpjZlQQGytU,1080 -anthropic/_response.py,sha256=yOtaSz1IgWAxX6mEyoUXhuaRfB_BGigccY16HOHZdX4,30679 -anthropic/_streaming.py,sha256=-jcNwJIEPE67_7kOfUS4jsRcdVKG7Jw-3AIofcfOfbw,17111 -anthropic/_types.py,sha256=jIMpuLkCfCZ8rLpfnKnGLvHWBEIqo2-Ul992R9Y7p9M,7696 -anthropic/_utils/__init__.py,sha256=JlWEMBeKNRWfLwppztVOHS0X4mygKvxI8sWtVN6F_0M,2371 -anthropic/_utils/__pycache__/__init__.cpython-312.pyc,, -anthropic/_utils/__pycache__/_compat.cpython-312.pyc,, -anthropic/_utils/__pycache__/_datetime_parse.cpython-312.pyc,, -anthropic/_utils/__pycache__/_httpx.cpython-312.pyc,, -anthropic/_utils/__pycache__/_json.cpython-312.pyc,, -anthropic/_utils/__pycache__/_logs.cpython-312.pyc,, -anthropic/_utils/__pycache__/_path.cpython-312.pyc,, -anthropic/_utils/__pycache__/_proxy.cpython-312.pyc,, -anthropic/_utils/__pycache__/_reflection.cpython-312.pyc,, -anthropic/_utils/__pycache__/_resources_proxy.cpython-312.pyc,, -anthropic/_utils/__pycache__/_streams.cpython-312.pyc,, -anthropic/_utils/__pycache__/_sync.cpython-312.pyc,, -anthropic/_utils/__pycache__/_transform.cpython-312.pyc,, -anthropic/_utils/__pycache__/_typing.cpython-312.pyc,, -anthropic/_utils/__pycache__/_utils.cpython-312.pyc,, -anthropic/_utils/_compat.py,sha256=33246eDcl3pwL6kWsEhVuT4Akrd8gZEW9LPTm465ohk,1231 -anthropic/_utils/_datetime_parse.py,sha256=bABTs0Bc6rabdFvnIwXjEhWL15TcRgWZ_6XGTqN8xUk,4204 -anthropic/_utils/_httpx.py,sha256=buTjMcUfp_KBTwIPStAD0mx1PreJIHn10if9y__wBeY,2094 -anthropic/_utils/_json.py,sha256=bl95uuIWwgSfXX-gP1trK_lDAPwJujYfJ05Cxo2SEC4,962 -anthropic/_utils/_logs.py,sha256=R8FqzEnxoLq-BLAzMROQmAHOKJussAkbd4eZL5xBkec,783 -anthropic/_utils/_path.py,sha256=Dk294levuJXP5e4m20YRDDZFPl7zegXvc9Kb3dUCwJI,4701 -anthropic/_utils/_proxy.py,sha256=aglnj2yBTDyGX9Akk2crZHrl10oqRmceUy2Zp008XEs,1975 -anthropic/_utils/_reflection.py,sha256=bNcMUN5iXq5ptScWow9DgNV_n92h__NTZebeKnZz-jI,2769 -anthropic/_utils/_resources_proxy.py,sha256=Y6WaTfDzBlt-GXVlTQLlIjpkSZZ8fRlMzXuRBh64CrA,604 -anthropic/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD-noMLM,289 -anthropic/_utils/_sync.py,sha256=HBnZkkBnzxtwOZe0212C4EyoRvxhTVtTrLFDz2_xVCg,1589 -anthropic/_utils/_transform.py,sha256=OqqM7YSmIEdFwifMIV_kLlH0EiZwebJ3yD7XW4cp4L4,16047 -anthropic/_utils/_typing.py,sha256=PUvUGnj_kpO5j22J8hJZW0-fwu2saCr7lN3r-VVw5s0,4812 -anthropic/_utils/_utils.py,sha256=9dXfN9bD4tR19GWM2SIrua3aBkJ995_5EBdo2NsmMqE,11893 -anthropic/_version.py,sha256=qMcyGZi_qD9beKEIS-vY1JP-pHLlF-rdWRcOL5YsB-s,162 -anthropic/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224 -anthropic/lib/__init__.py,sha256=ed3VXosCln6iXSojwutNZjzjoIVpDLIHfMiiMiSHjlU,99 -anthropic/lib/__pycache__/__init__.cpython-312.pyc,, -anthropic/lib/__pycache__/_files.cpython-312.pyc,, -anthropic/lib/__pycache__/_stainless_helpers.cpython-312.pyc,, -anthropic/lib/__pycache__/foundry.cpython-312.pyc,, -anthropic/lib/_extras/__init__.py,sha256=a9HX69-V9nROM4Em9a4y-xZTgiLE2jdlCyC6ZKtxfyY,53 -anthropic/lib/_extras/__pycache__/__init__.cpython-312.pyc,, -anthropic/lib/_extras/__pycache__/_common.cpython-312.pyc,, -anthropic/lib/_extras/__pycache__/_google_auth.cpython-312.pyc,, -anthropic/lib/_extras/_common.py,sha256=IhHjAsirY2xfLJrzlt9rS_0IPsTJeWqKA2HWUuvDN14,348 -anthropic/lib/_extras/_google_auth.py,sha256=Wukh6VOgcDRYSsFCVT9tx_oXI1ApIsmioSLEMsYvDfw,688 -anthropic/lib/_files.py,sha256=gVtOWR-evsIJV_HCX225tPRU6Dyj73FWXb7OPt5CVcQ,1221 -anthropic/lib/_parse/__pycache__/_response.cpython-312.pyc,, -anthropic/lib/_parse/__pycache__/_transform.cpython-312.pyc,, -anthropic/lib/_parse/_response.py,sha256=_CEbmG-xee43wYNlcgQD5NY4Ov3Uj-4ssNmBUpyqpjA,2460 -anthropic/lib/_parse/_transform.py,sha256=CMRK0yffp8A8Mm4Yldb5Izam21dJq_G7uFUw1pn0ugg,5386 -anthropic/lib/_stainless_helpers.py,sha256=Cu7CMGonVlBkCzHsoJjnSxiLyY9ta3FckOatLRZR2h4,2183 -anthropic/lib/aws/__init__.py,sha256=a4aDwjNQE9a5o_NkmotFl7ngHM1O2JWO5WzawZg9zhY,90 -anthropic/lib/aws/__pycache__/__init__.cpython-312.pyc,, -anthropic/lib/aws/__pycache__/_auth.cpython-312.pyc,, -anthropic/lib/aws/__pycache__/_client.cpython-312.pyc,, -anthropic/lib/aws/__pycache__/_credentials.cpython-312.pyc,, -anthropic/lib/aws/_auth.py,sha256=1Wy0RqFjSe6d30rz6O5G1pxXgJcoT_912wKQnM6X9Kg,1953 -anthropic/lib/aws/_client.py,sha256=jmEUUetXbqpdC2ptJAyaywrg6WGpbEaWHrhHexK4obM,14899 -anthropic/lib/aws/_credentials.py,sha256=5Vje5gQ0btzTAsJkc-9AgnldD-sSwA4VLwiqs1ACbV0,3754 -anthropic/lib/bedrock/__init__.py,sha256=QkxbKfBTHMaVmZywoMQIuljSRug-sFfgCP3fCPgALSM,249 -anthropic/lib/bedrock/__pycache__/__init__.cpython-312.pyc,, -anthropic/lib/bedrock/__pycache__/_auth.cpython-312.pyc,, -anthropic/lib/bedrock/__pycache__/_beta.cpython-312.pyc,, -anthropic/lib/bedrock/__pycache__/_beta_messages.cpython-312.pyc,, -anthropic/lib/bedrock/__pycache__/_client.cpython-312.pyc,, -anthropic/lib/bedrock/__pycache__/_mantle.cpython-312.pyc,, -anthropic/lib/bedrock/__pycache__/_stream.cpython-312.pyc,, -anthropic/lib/bedrock/__pycache__/_stream_decoder.cpython-312.pyc,, -anthropic/lib/bedrock/_auth.py,sha256=6inTIC3Emx86SVFMncfklN_ry486Dd1VPQbmx8pg3zM,1890 -anthropic/lib/bedrock/_beta.py,sha256=8kXsUUIGstf6dZfiZtm6s9OWEueuSgra8dPvkaUacy4,3323 -anthropic/lib/bedrock/_beta_messages.py,sha256=ClPL21UrRbJ9M10G8PcRla_Fu9GoWN_420FUuw91bmY,3197 -anthropic/lib/bedrock/_client.py,sha256=9Tk_ZUTCSNOIn0XFQqdnfPnvvGrxrzTNrda52CT3dlY,17581 -anthropic/lib/bedrock/_mantle.py,sha256=x44WYvzHPcbdM_vOZENg-0lZk7Gb9y17YO9cDlUAfgA,17706 -anthropic/lib/bedrock/_stream.py,sha256=wCS-1otwfIIVbfG3TFFKxTD-antJiTmprW6eAAGTCDA,871 -anthropic/lib/bedrock/_stream_decoder.py,sha256=gTlsTn0s6iVOL4Smp_inhDUBcOZuCgGgJib7fORbQWM,2551 -anthropic/lib/foundry.md,sha256=jFWVnP5a8qARzQjUdtvxhF1p2pkPo2WCRfkumhvdlZw,2745 -anthropic/lib/foundry.py,sha256=q4fZOnbBcBWFE_fVwL9EyJBFt-RFk0l78bzFxyO9Q2k,17537 -anthropic/lib/streaming/__init__.py,sha256=HjQ-FBmB7YiliD_Hu3-Li4c2UG8RHwt5pm2qV3iXAIM,1538 -anthropic/lib/streaming/__pycache__/__init__.cpython-312.pyc,, -anthropic/lib/streaming/__pycache__/_beta_messages.cpython-312.pyc,, -anthropic/lib/streaming/__pycache__/_beta_types.cpython-312.pyc,, -anthropic/lib/streaming/__pycache__/_messages.cpython-312.pyc,, -anthropic/lib/streaming/__pycache__/_types.cpython-312.pyc,, -anthropic/lib/streaming/_beta_messages.py,sha256=vPrzuPcoy81aG42npMQM3IRbiv1VXlA4a7xxnkSBHco,21043 -anthropic/lib/streaming/_beta_types.py,sha256=qeAH9ynqPRf4FstMPwqgpL300iT7BZfCz8jFMPqa9II,2912 -anthropic/lib/streaming/_messages.py,sha256=6HZ-Xx8AdWFLVtbC1GW99Z4_81MTx8yHHdwOjbHOZ-U,18919 -anthropic/lib/streaming/_types.py,sha256=8u3j8YaW2KaMbAmo2HF0t7-AVQ9FMD2RmI-gg3mTBvI,3334 -anthropic/lib/tools/__init__.py,sha256=nRQPh57ASJQDk5sBW_o2UdbuLzrWWwK1c1QR-yW0KTo,843 -anthropic/lib/tools/__pycache__/__init__.cpython-312.pyc,, -anthropic/lib/tools/__pycache__/_beta_builtin_memory_tool.cpython-312.pyc,, -anthropic/lib/tools/__pycache__/_beta_compaction_control.cpython-312.pyc,, -anthropic/lib/tools/__pycache__/_beta_functions.cpython-312.pyc,, -anthropic/lib/tools/__pycache__/_beta_runner.cpython-312.pyc,, -anthropic/lib/tools/__pycache__/mcp.cpython-312.pyc,, -anthropic/lib/tools/_beta_builtin_memory_tool.py,sha256=zlEcgGOBFmpPzjc86vfJq6cziIovZmLxMHTOsY_T5LU,33981 -anthropic/lib/tools/_beta_compaction_control.py,sha256=U3JucTgWhqFBEt0J_UZd3zw66ILweR3LIq74dWYBp-c,2419 -anthropic/lib/tools/_beta_functions.py,sha256=ie9L_bsZZ_HM4C6U6sEnT-rMWhRy2M8bBaEhs8UK2qE,15559 -anthropic/lib/tools/_beta_runner.py,sha256=u4omwF-Rjd_Iv7uzZD3rNGhp2hScIiCgidrKtkPGVDE,26499 -anthropic/lib/tools/mcp.py,sha256=kVa6FExB3eMfdrnZtxObDZV9Mkw6BknxoBpEvPnZ2TQ,15701 -anthropic/lib/vertex/__init__.py,sha256=A8vuK1qVPtmKr1_LQgPuDRVA6I4xm_ye2aPdAa4yGsI,102 -anthropic/lib/vertex/__pycache__/__init__.cpython-312.pyc,, -anthropic/lib/vertex/__pycache__/_auth.cpython-312.pyc,, -anthropic/lib/vertex/__pycache__/_beta.cpython-312.pyc,, -anthropic/lib/vertex/__pycache__/_beta_messages.cpython-312.pyc,, -anthropic/lib/vertex/__pycache__/_client.cpython-312.pyc,, -anthropic/lib/vertex/_auth.py,sha256=tirz9vbyA7dwp4gbdN6SG8301bA3ii2Oy5SDAvVd9xQ,1516 -anthropic/lib/vertex/_beta.py,sha256=8kXsUUIGstf6dZfiZtm6s9OWEueuSgra8dPvkaUacy4,3323 -anthropic/lib/vertex/_beta_messages.py,sha256=4fsV2F6TzB14DuHLo9k8i95vymcbixIPjsplqpsHfac,3399 -anthropic/lib/vertex/_client.py,sha256=ATGOqrptdNvH16FX3NHqFa-jgvrnEQxPq5jfJvt5m-M,16847 -anthropic/pagination.py,sha256=qyUdNS1ZPBQgQOmXBCxHT8U2qBsD4Tk73EACuTVdbHY,4670 -anthropic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -anthropic/resources/__init__.py,sha256=H0t_V-A_u6bIVmbAUpY9ZfgqoNIjIfyNpZz7hAiErIA,1583 -anthropic/resources/__pycache__/__init__.cpython-312.pyc,, -anthropic/resources/__pycache__/completions.cpython-312.pyc,, -anthropic/resources/__pycache__/models.cpython-312.pyc,, -anthropic/resources/beta/__init__.py,sha256=TCvdlG8fwb30q8P6s4WE6ibZdY_ptjcLSPLA506vM80,4245 -anthropic/resources/beta/__pycache__/__init__.cpython-312.pyc,, -anthropic/resources/beta/__pycache__/beta.cpython-312.pyc,, -anthropic/resources/beta/__pycache__/environments.cpython-312.pyc,, -anthropic/resources/beta/__pycache__/files.cpython-312.pyc,, -anthropic/resources/beta/__pycache__/models.cpython-312.pyc,, -anthropic/resources/beta/__pycache__/user_profiles.cpython-312.pyc,, -anthropic/resources/beta/agents/__init__.py,sha256=d2jsvQCxwdYLp6AXK0qw57X5jqlstyPO2Z_ByI1ZadU,836 -anthropic/resources/beta/agents/__pycache__/__init__.cpython-312.pyc,, -anthropic/resources/beta/agents/__pycache__/agents.cpython-312.pyc,, -anthropic/resources/beta/agents/__pycache__/versions.cpython-312.pyc,, -anthropic/resources/beta/agents/agents.py,sha256=9UEq0CykhQYhQnfzHignVBf4OtO96ezujYXOqVik28w,36634 -anthropic/resources/beta/agents/versions.py,sha256=a8cECDCY4lsGG8ZYxTrIgpEi1nTekC-aCwRkaYmihdo,8477 -anthropic/resources/beta/beta.py,sha256=gP-Y9MyPw0eqcucJ5wOTMfl6aawKd2IWyby-KALoocM,12188 -anthropic/resources/beta/environments.py,sha256=VeYRx-SIZgEw0eoWPOmmI3fAMiGw5DRhgaAbx7wAoJs,34235 -anthropic/resources/beta/files.py,sha256=I1Qg6YbkoP5sYVNDjmTX6R_eohOOxfEgBps8Jz-D7us,27746 -anthropic/resources/beta/memory_stores/__init__.py,sha256=WczU3iQf9fjGOO_k9N5IsSbjNrfnADhjhaDTtGae69E,1374 -anthropic/resources/beta/memory_stores/__pycache__/__init__.cpython-312.pyc,, -anthropic/resources/beta/memory_stores/__pycache__/memories.cpython-312.pyc,, -anthropic/resources/beta/memory_stores/__pycache__/memory_stores.cpython-312.pyc,, -anthropic/resources/beta/memory_stores/__pycache__/memory_versions.cpython-312.pyc,, -anthropic/resources/beta/memory_stores/memories.py,sha256=w5GyeH1Nhj01GPBRtOrce1bnx9qRy6XQNLqv443jN7M,33775 -anthropic/resources/beta/memory_stores/memory_stores.py,sha256=lgIRtWXZAgIhiAbnfZ3obH2he9p35q3rRgoLxyHU624,35461 -anthropic/resources/beta/memory_stores/memory_versions.py,sha256=iqYFglbubuwwrA75YgU8urEPxHesfqWqB6orBd28FDA,22345 -anthropic/resources/beta/messages/__init__.py,sha256=7ZO4hB7hPBhXQja7gMzkwLXQVDlyap4JsihpA0UKZjk,849 -anthropic/resources/beta/messages/__pycache__/__init__.cpython-312.pyc,, -anthropic/resources/beta/messages/__pycache__/batches.cpython-312.pyc,, -anthropic/resources/beta/messages/__pycache__/messages.cpython-312.pyc,, -anthropic/resources/beta/messages/batches.py,sha256=Ez0LJBTy7FhjY-HFdca5iDZIEC5we_mqryzrQ6VL0LU,36989 -anthropic/resources/beta/messages/messages.py,sha256=fWU0bhvvJos0ENjkJxFXsOMexVsJzL92EhMjYvpmHME,177191 -anthropic/resources/beta/models.py,sha256=e_hGMmuo1smUnduh6eRaPVcgADc-0vGxJ8c12B4luWs,12597 -anthropic/resources/beta/sessions/__init__.py,sha256=K7K9_0wk3K-XnCRZJJCGoX6M0xmCNOR9qSezDuULHr8,1229 -anthropic/resources/beta/sessions/__pycache__/__init__.cpython-312.pyc,, -anthropic/resources/beta/sessions/__pycache__/events.cpython-312.pyc,, -anthropic/resources/beta/sessions/__pycache__/resources.cpython-312.pyc,, -anthropic/resources/beta/sessions/__pycache__/sessions.cpython-312.pyc,, -anthropic/resources/beta/sessions/events.py,sha256=-FUN6aIrCPc71zJ9C9n6RSvbN5zUM9mTcnbv44gkYpA,19032 -anthropic/resources/beta/sessions/resources.py,sha256=Eev-4n5gY6SNt7W8-0FpZksXL6ledTILoTR-9k_S22U,31024 -anthropic/resources/beta/sessions/sessions.py,sha256=9KNt_7NLEGw0CmSsUBzvW_rRhi1pMeLBr6C76xjpqrY,38726 -anthropic/resources/beta/skills/__init__.py,sha256=QMC_HEzfI-k0jhfKJThUUjf9wf7Vs8HTxSXYNnvVx2o,836 -anthropic/resources/beta/skills/__pycache__/__init__.cpython-312.pyc,, -anthropic/resources/beta/skills/__pycache__/skills.cpython-312.pyc,, -anthropic/resources/beta/skills/__pycache__/versions.cpython-312.pyc,, -anthropic/resources/beta/skills/skills.py,sha256=qo_gh7DTobew6_YBRtYBUrpvfS2LCQAC7ST9DcMOnuU,25259 -anthropic/resources/beta/skills/versions.py,sha256=Vb4AC-0WQjCiokSTlV4O8xuDFURZg49gV-H98OEdsz0,25609 -anthropic/resources/beta/user_profiles.py,sha256=5PmiwsjgmP8_wupI10QU3g5mjOvrOUgIlSJrkQKp_aQ,28717 -anthropic/resources/beta/vaults/__init__.py,sha256=mgR3-LNZcA4S4ArLGTfgkJBemEq1IByylRAWItP9kXA,875 -anthropic/resources/beta/vaults/__pycache__/__init__.cpython-312.pyc,, -anthropic/resources/beta/vaults/__pycache__/credentials.cpython-312.pyc,, -anthropic/resources/beta/vaults/__pycache__/vaults.cpython-312.pyc,, -anthropic/resources/beta/vaults/credentials.py,sha256=-tXq5qx_VTXtpeYdT5AAIR3Lz-S39NhioWxkjW5tgDo,36017 -anthropic/resources/beta/vaults/vaults.py,sha256=7yAg4b0vH32joCEFXO01ikk87gZ_02MU9qIE2gqMBWg,32634 -anthropic/resources/completions.py,sha256=J7MfkE6z3AOginTtFKDqKfUQPnc_kCxDfbsH7RQDpPA,35434 -anthropic/resources/messages/__init__.py,sha256=iOSBh4D7NTXqe7RNhw9HZCiFmJvDfIgVFnjaF7r27YU,897 -anthropic/resources/messages/__pycache__/__init__.cpython-312.pyc,, -anthropic/resources/messages/__pycache__/batches.cpython-312.pyc,, -anthropic/resources/messages/__pycache__/messages.cpython-312.pyc,, -anthropic/resources/messages/batches.py,sha256=_117hVE6_REhMn1kL4gD022TGaSgrrsfCAPMQwlw-mc,29233 -anthropic/resources/messages/messages.py,sha256=uDHMn8yd-irCw_2iYgg-uUhncbwjQv6-M5vy_amT53Q,134340 -anthropic/resources/models.py,sha256=AjkzAPLgycytSvtt3ONWThJ-b6OaTHghpR_rvis2LKk,12483 -anthropic/tools/__init__.py,sha256=sp16QafOAaCtw3WXFGuT8LiNJcHUymMYZ6JCo5mLkUM,22 -anthropic/tools/__pycache__/__init__.cpython-312.pyc,, -anthropic/tools/__pycache__/memory.cpython-312.pyc,, -anthropic/tools/memory.py,sha256=1Ifj-j5xlSq5Esn2htLWC-4nlSx5uqALH7ja-rMFWNk,349 -anthropic/types/__init__.py,sha256=_ICqnalk-0flARzlOrNH3smjOK-KPHTwG2s3iqde2ts,18785 -anthropic/types/__pycache__/__init__.cpython-312.pyc,, -anthropic/types/__pycache__/anthropic_beta_param.cpython-312.pyc,, -anthropic/types/__pycache__/base64_image_source_param.cpython-312.pyc,, -anthropic/types/__pycache__/base64_pdf_source.cpython-312.pyc,, -anthropic/types/__pycache__/base64_pdf_source_param.cpython-312.pyc,, -anthropic/types/__pycache__/bash_code_execution_output_block.cpython-312.pyc,, -anthropic/types/__pycache__/bash_code_execution_output_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/bash_code_execution_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/bash_code_execution_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/bash_code_execution_tool_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/bash_code_execution_tool_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/bash_code_execution_tool_result_error.cpython-312.pyc,, -anthropic/types/__pycache__/bash_code_execution_tool_result_error_code.cpython-312.pyc,, -anthropic/types/__pycache__/bash_code_execution_tool_result_error_param.cpython-312.pyc,, -anthropic/types/__pycache__/beta_api_error.cpython-312.pyc,, -anthropic/types/__pycache__/beta_authentication_error.cpython-312.pyc,, -anthropic/types/__pycache__/beta_billing_error.cpython-312.pyc,, -anthropic/types/__pycache__/beta_error.cpython-312.pyc,, -anthropic/types/__pycache__/beta_error_response.cpython-312.pyc,, -anthropic/types/__pycache__/beta_gateway_timeout_error.cpython-312.pyc,, -anthropic/types/__pycache__/beta_invalid_request_error.cpython-312.pyc,, -anthropic/types/__pycache__/beta_not_found_error.cpython-312.pyc,, -anthropic/types/__pycache__/beta_overloaded_error.cpython-312.pyc,, -anthropic/types/__pycache__/beta_permission_error.cpython-312.pyc,, -anthropic/types/__pycache__/beta_rate_limit_error.cpython-312.pyc,, -anthropic/types/__pycache__/cache_control_ephemeral_param.cpython-312.pyc,, -anthropic/types/__pycache__/cache_creation.cpython-312.pyc,, -anthropic/types/__pycache__/capability_support.cpython-312.pyc,, -anthropic/types/__pycache__/citation_char_location.cpython-312.pyc,, -anthropic/types/__pycache__/citation_char_location_param.cpython-312.pyc,, -anthropic/types/__pycache__/citation_content_block_location.cpython-312.pyc,, -anthropic/types/__pycache__/citation_content_block_location_param.cpython-312.pyc,, -anthropic/types/__pycache__/citation_page_location.cpython-312.pyc,, -anthropic/types/__pycache__/citation_page_location_param.cpython-312.pyc,, -anthropic/types/__pycache__/citation_search_result_location_param.cpython-312.pyc,, -anthropic/types/__pycache__/citation_web_search_result_location_param.cpython-312.pyc,, -anthropic/types/__pycache__/citations_config.cpython-312.pyc,, -anthropic/types/__pycache__/citations_config_param.cpython-312.pyc,, -anthropic/types/__pycache__/citations_delta.cpython-312.pyc,, -anthropic/types/__pycache__/citations_search_result_location.cpython-312.pyc,, -anthropic/types/__pycache__/citations_web_search_result_location.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_output_block.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_output_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_tool_20250522_param.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_tool_20250825_param.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_tool_20260120_param.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_tool_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_tool_result_block_content.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_tool_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_tool_result_block_param_content_param.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_tool_result_error.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_tool_result_error_code.cpython-312.pyc,, -anthropic/types/__pycache__/code_execution_tool_result_error_param.cpython-312.pyc,, -anthropic/types/__pycache__/completion.cpython-312.pyc,, -anthropic/types/__pycache__/completion_create_params.cpython-312.pyc,, -anthropic/types/__pycache__/container.cpython-312.pyc,, -anthropic/types/__pycache__/container_upload_block.cpython-312.pyc,, -anthropic/types/__pycache__/container_upload_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/content_block.cpython-312.pyc,, -anthropic/types/__pycache__/content_block_delta_event.cpython-312.pyc,, -anthropic/types/__pycache__/content_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/content_block_source_content_param.cpython-312.pyc,, -anthropic/types/__pycache__/content_block_source_param.cpython-312.pyc,, -anthropic/types/__pycache__/content_block_start_event.cpython-312.pyc,, -anthropic/types/__pycache__/content_block_stop_event.cpython-312.pyc,, -anthropic/types/__pycache__/context_management_capability.cpython-312.pyc,, -anthropic/types/__pycache__/direct_caller.cpython-312.pyc,, -anthropic/types/__pycache__/direct_caller_param.cpython-312.pyc,, -anthropic/types/__pycache__/document_block.cpython-312.pyc,, -anthropic/types/__pycache__/document_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/effort_capability.cpython-312.pyc,, -anthropic/types/__pycache__/encrypted_code_execution_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/encrypted_code_execution_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/image_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/input_json_delta.cpython-312.pyc,, -anthropic/types/__pycache__/json_output_format_param.cpython-312.pyc,, -anthropic/types/__pycache__/memory_tool_20250818_param.cpython-312.pyc,, -anthropic/types/__pycache__/message.cpython-312.pyc,, -anthropic/types/__pycache__/message_count_tokens_params.cpython-312.pyc,, -anthropic/types/__pycache__/message_count_tokens_tool_param.cpython-312.pyc,, -anthropic/types/__pycache__/message_create_params.cpython-312.pyc,, -anthropic/types/__pycache__/message_delta_event.cpython-312.pyc,, -anthropic/types/__pycache__/message_delta_usage.cpython-312.pyc,, -anthropic/types/__pycache__/message_param.cpython-312.pyc,, -anthropic/types/__pycache__/message_start_event.cpython-312.pyc,, -anthropic/types/__pycache__/message_stop_event.cpython-312.pyc,, -anthropic/types/__pycache__/message_stream_event.cpython-312.pyc,, -anthropic/types/__pycache__/message_tokens_count.cpython-312.pyc,, -anthropic/types/__pycache__/metadata_param.cpython-312.pyc,, -anthropic/types/__pycache__/model.cpython-312.pyc,, -anthropic/types/__pycache__/model_capabilities.cpython-312.pyc,, -anthropic/types/__pycache__/model_info.cpython-312.pyc,, -anthropic/types/__pycache__/model_list_params.cpython-312.pyc,, -anthropic/types/__pycache__/model_param.cpython-312.pyc,, -anthropic/types/__pycache__/output_config_param.cpython-312.pyc,, -anthropic/types/__pycache__/parsed_message.cpython-312.pyc,, -anthropic/types/__pycache__/plain_text_source.cpython-312.pyc,, -anthropic/types/__pycache__/plain_text_source_param.cpython-312.pyc,, -anthropic/types/__pycache__/raw_content_block_delta.cpython-312.pyc,, -anthropic/types/__pycache__/raw_content_block_delta_event.cpython-312.pyc,, -anthropic/types/__pycache__/raw_content_block_start_event.cpython-312.pyc,, -anthropic/types/__pycache__/raw_content_block_stop_event.cpython-312.pyc,, -anthropic/types/__pycache__/raw_message_delta_event.cpython-312.pyc,, -anthropic/types/__pycache__/raw_message_start_event.cpython-312.pyc,, -anthropic/types/__pycache__/raw_message_stop_event.cpython-312.pyc,, -anthropic/types/__pycache__/raw_message_stream_event.cpython-312.pyc,, -anthropic/types/__pycache__/redacted_thinking_block.cpython-312.pyc,, -anthropic/types/__pycache__/redacted_thinking_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/refusal_stop_details.cpython-312.pyc,, -anthropic/types/__pycache__/search_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/server_tool_caller.cpython-312.pyc,, -anthropic/types/__pycache__/server_tool_caller_20260120.cpython-312.pyc,, -anthropic/types/__pycache__/server_tool_caller_20260120_param.cpython-312.pyc,, -anthropic/types/__pycache__/server_tool_caller_param.cpython-312.pyc,, -anthropic/types/__pycache__/server_tool_usage.cpython-312.pyc,, -anthropic/types/__pycache__/server_tool_use_block.cpython-312.pyc,, -anthropic/types/__pycache__/server_tool_use_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/signature_delta.cpython-312.pyc,, -anthropic/types/__pycache__/stop_reason.cpython-312.pyc,, -anthropic/types/__pycache__/text_block.cpython-312.pyc,, -anthropic/types/__pycache__/text_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/text_citation.cpython-312.pyc,, -anthropic/types/__pycache__/text_citation_param.cpython-312.pyc,, -anthropic/types/__pycache__/text_delta.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_create_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_create_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_str_replace_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_str_replace_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_tool_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_tool_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_tool_result_error.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_tool_result_error_code.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_tool_result_error_param.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_view_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/text_editor_code_execution_view_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/thinking_block.cpython-312.pyc,, -anthropic/types/__pycache__/thinking_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/thinking_capability.cpython-312.pyc,, -anthropic/types/__pycache__/thinking_config_adaptive_param.cpython-312.pyc,, -anthropic/types/__pycache__/thinking_config_disabled_param.cpython-312.pyc,, -anthropic/types/__pycache__/thinking_config_enabled_param.cpython-312.pyc,, -anthropic/types/__pycache__/thinking_config_param.cpython-312.pyc,, -anthropic/types/__pycache__/thinking_delta.cpython-312.pyc,, -anthropic/types/__pycache__/thinking_types.cpython-312.pyc,, -anthropic/types/__pycache__/tool_bash_20250124_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_choice_any_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_choice_auto_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_choice_none_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_choice_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_choice_tool_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_reference_block.cpython-312.pyc,, -anthropic/types/__pycache__/tool_reference_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_search_tool_bm25_20251119_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_search_tool_regex_20251119_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_search_tool_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/tool_search_tool_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_search_tool_result_error.cpython-312.pyc,, -anthropic/types/__pycache__/tool_search_tool_result_error_code.cpython-312.pyc,, -anthropic/types/__pycache__/tool_search_tool_result_error_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_search_tool_search_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/tool_search_tool_search_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_text_editor_20250124_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_text_editor_20250429_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_text_editor_20250728_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_union_param.cpython-312.pyc,, -anthropic/types/__pycache__/tool_use_block.cpython-312.pyc,, -anthropic/types/__pycache__/tool_use_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/url_image_source_param.cpython-312.pyc,, -anthropic/types/__pycache__/url_pdf_source_param.cpython-312.pyc,, -anthropic/types/__pycache__/usage.cpython-312.pyc,, -anthropic/types/__pycache__/user_location_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_fetch_block.cpython-312.pyc,, -anthropic/types/__pycache__/web_fetch_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_fetch_tool_20250910_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_fetch_tool_20260209_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_fetch_tool_20260309_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_fetch_tool_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/web_fetch_tool_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_fetch_tool_result_error_block.cpython-312.pyc,, -anthropic/types/__pycache__/web_fetch_tool_result_error_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_fetch_tool_result_error_code.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_tool_20250305_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_tool_20260209_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_tool_request_error_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_tool_result_block.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_tool_result_block_content.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_tool_result_block_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_tool_result_block_param_content_param.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_tool_result_error.cpython-312.pyc,, -anthropic/types/__pycache__/web_search_tool_result_error_code.cpython-312.pyc,, -anthropic/types/anthropic_beta_param.py,sha256=qXdsMFDT8bYOm4YILmYTMMikQ_juCyp80V_yBOoQXjk,1140 -anthropic/types/base64_image_source_param.py,sha256=4djZ4GfXcL2khwcg8KpUdZILKmmzHro5YFXTdkhSqpw,725 -anthropic/types/base64_pdf_source.py,sha256=9Hvdi_upbA04Z-JLkVlwcKV4SSbsw_G-mGGpxlfVZ5k,312 -anthropic/types/base64_pdf_source_param.py,sha256=N2ALmXljCEVfOh9oUbgFjH8hF3iNFoQLK7y0MfvPl4k,684 -anthropic/types/bash_code_execution_output_block.py,sha256=ZdhmAujCcmVqrFHHkQ2anv-tFn3KjkhemOPzAeXBq5k,317 -anthropic/types/bash_code_execution_output_block_param.py,sha256=JTN8c5BXOh8M1qt9U-tGL_H-fR6YwHwpfUjBeAWdysI,384 -anthropic/types/bash_code_execution_result_block.py,sha256=icBjrjyDx_ATor-0Iywl3JanoGveSlOgOaNoh0Zy968,503 -anthropic/types/bash_code_execution_result_block_param.py,sha256=cbl0eChS-IjYcEe8OUmkdLXnuAG95g1aMYQq8PYUCEU,625 -anthropic/types/bash_code_execution_tool_result_block.py,sha256=6j6tPFkqfq8smf_jVuTSqzghNcwcpGUxNQsCBakxmxQ,654 -anthropic/types/bash_code_execution_tool_result_block_param.py,sha256=jhsMBtXps_-_cuIs4eWug4IhLFHEK1RNBOLBVwczECQ,968 -anthropic/types/bash_code_execution_tool_result_error.py,sha256=Ib4dOoLOQgoevEVp2IL1vtW7c7D9uFhe-KruXTfyuEU,465 -anthropic/types/bash_code_execution_tool_result_error_code.py,sha256=CA1Pc6Gsj_1n3_oCzTJUJqxqkaGErkM4-qmjTwruIdE,363 -anthropic/types/bash_code_execution_tool_result_error_param.py,sha256=zWzKXefAYQITxptQYYmF21qnn0lS4fwZDwHHFOrlUzY,533 -anthropic/types/beta/__init__.py,sha256=ulQorhw2bwQRpWKCdc_YjfT7NmcOj5bBOESUlJZ7Pcc,34505 -anthropic/types/beta/__pycache__/__init__.cpython-312.pyc,, -anthropic/types/beta/__pycache__/agent_create_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/agent_list_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/agent_retrieve_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/agent_update_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_advisor_message_iteration_usage.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_advisor_redacted_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_advisor_redacted_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_advisor_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_advisor_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_advisor_tool_20260301_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_advisor_tool_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_advisor_tool_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_advisor_tool_result_error.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_advisor_tool_result_error_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_all_thinking_turns_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_base64_image_source_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_base64_pdf_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_base64_pdf_source.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_base64_pdf_source_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_bash_code_execution_output_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_bash_code_execution_output_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_bash_code_execution_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_bash_code_execution_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_bash_code_execution_tool_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_bash_code_execution_tool_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_bash_code_execution_tool_result_error.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_bash_code_execution_tool_result_error_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_cache_control_ephemeral_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_cache_creation.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_capability_support.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citation_char_location.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citation_char_location_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citation_config.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citation_content_block_location.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citation_content_block_location_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citation_page_location.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citation_page_location_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citation_search_result_location.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citation_search_result_location_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citation_web_search_result_location_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citations_config_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citations_delta.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_citations_web_search_result_location.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_clear_thinking_20251015_edit_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_clear_thinking_20251015_edit_response.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_clear_tool_uses_20250919_edit_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_clear_tool_uses_20250919_edit_response.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_cloud_config.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_cloud_config_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_output_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_output_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_tool_20250522_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_tool_20250825_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_tool_20260120_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_tool_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_tool_result_block_content.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_tool_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_tool_result_block_param_content_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_tool_result_error.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_tool_result_error_code.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_code_execution_tool_result_error_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_compact_20260112_edit_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_compaction_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_compaction_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_compaction_content_block_delta.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_compaction_iteration_usage.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_container.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_container_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_container_upload_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_container_upload_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_content_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_content_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_content_block_source_content_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_content_block_source_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_context_management_capability.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_context_management_config_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_context_management_response.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_count_tokens_context_management_response.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_direct_caller.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_direct_caller_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_document_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_effort_capability.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_encrypted_code_execution_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_encrypted_code_execution_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_environment.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_environment_delete_response.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_file_document_source_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_file_image_source_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_file_scope.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_image_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_input_json_delta.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_input_tokens_clear_at_least_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_input_tokens_trigger_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_iterations_usage.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_json_output_format_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_limited_network.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_limited_network_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_agent.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_agent_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_agent_tool_config.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_agent_tool_config_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_agent_toolset20260401.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_agent_toolset20260401_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_agent_toolset_default_config.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_agent_toolset_default_config_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_always_allow_policy.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_always_allow_policy_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_always_ask_policy.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_always_ask_policy_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_anthropic_skill.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_anthropic_skill_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_branch_checkout.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_branch_checkout_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_cache_creation_usage.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_commit_checkout.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_commit_checkout_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_custom_skill.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_custom_skill_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_custom_tool.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_custom_tool_input_schema.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_custom_tool_input_schema_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_custom_tool_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_deleted_memory_store.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_deleted_session.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_deleted_vault.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_file_resource_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_github_repository_resource_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_mcp_server_url_definition.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_mcp_tool_config.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_mcp_tool_config_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_mcp_toolset.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_mcp_toolset_default_config.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_mcp_toolset_default_config_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_mcp_toolset_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_memory_store.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_memory_store_resource_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_model.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_model_config.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_model_config_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_model_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_session.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_session_agent.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_session_stats.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_session_usage.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_skill_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_url_mcp_server_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_managed_agents_vault.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_mcp_tool_config_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_mcp_tool_default_config_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_mcp_tool_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_mcp_tool_use_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_mcp_tool_use_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_mcp_toolset_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_memory_tool_20250818_command.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_memory_tool_20250818_create_command.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_memory_tool_20250818_delete_command.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_memory_tool_20250818_insert_command.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_memory_tool_20250818_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_memory_tool_20250818_rename_command.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_memory_tool_20250818_str_replace_command.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_memory_tool_20250818_view_command.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_message.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_message_delta_usage.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_message_iteration_usage.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_message_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_message_tokens_count.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_metadata_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_model_capabilities.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_model_info.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_output_config_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_packages.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_packages_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_plain_text_source.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_plain_text_source_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_raw_content_block_delta.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_raw_content_block_delta_event.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_raw_content_block_start_event.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_raw_content_block_stop_event.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_raw_message_delta_event.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_raw_message_start_event.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_raw_message_stop_event.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_raw_message_stream_event.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_redacted_thinking_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_redacted_thinking_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_refusal_stop_details.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_request_document_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_request_mcp_server_tool_configuration_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_request_mcp_server_url_definition_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_request_mcp_tool_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_search_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_server_tool_caller.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_server_tool_caller_20260120.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_server_tool_caller_20260120_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_server_tool_caller_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_server_tool_usage.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_server_tool_use_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_server_tool_use_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_signature_delta.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_skill.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_skill_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_stop_reason.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_citation.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_citation_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_delta.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_editor_code_execution_create_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_editor_code_execution_create_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_editor_code_execution_str_replace_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_editor_code_execution_str_replace_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_editor_code_execution_tool_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_editor_code_execution_tool_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_editor_code_execution_tool_result_error.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_editor_code_execution_tool_result_error_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_editor_code_execution_view_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_text_editor_code_execution_view_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_thinking_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_thinking_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_thinking_capability.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_thinking_config_adaptive_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_thinking_config_disabled_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_thinking_config_enabled_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_thinking_config_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_thinking_delta.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_thinking_turns_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_thinking_types.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_token_task_budget_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_bash_20241022_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_bash_20250124_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_choice_any_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_choice_auto_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_choice_none_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_choice_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_choice_tool_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_computer_use_20241022_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_computer_use_20250124_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_computer_use_20251124_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_reference_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_reference_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_search_tool_bm25_20251119_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_search_tool_regex_20251119_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_search_tool_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_search_tool_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_search_tool_result_error.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_search_tool_result_error_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_search_tool_search_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_search_tool_search_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_text_editor_20241022_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_text_editor_20250124_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_text_editor_20250429_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_text_editor_20250728_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_union_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_use_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_use_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_uses_keep_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_tool_uses_trigger_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_unrestricted_network.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_unrestricted_network_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_url_image_source_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_url_pdf_source_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_usage.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_user_location_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_user_profile.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_user_profile_enrollment_url.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_user_profile_trust_grant.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_fetch_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_fetch_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_fetch_tool_20250910_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_fetch_tool_20260209_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_fetch_tool_20260309_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_fetch_tool_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_fetch_tool_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_fetch_tool_result_error_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_fetch_tool_result_error_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_fetch_tool_result_error_code.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_tool_20250305_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_tool_20260209_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_tool_request_error_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_tool_result_block.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_tool_result_block_content.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_tool_result_block_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_tool_result_block_param_content_param.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_tool_result_error.cpython-312.pyc,, -anthropic/types/beta/__pycache__/beta_web_search_tool_result_error_code.cpython-312.pyc,, -anthropic/types/beta/__pycache__/deleted_file.cpython-312.pyc,, -anthropic/types/beta/__pycache__/environment_create_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/environment_list_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/environment_update_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/file_list_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/file_metadata.cpython-312.pyc,, -anthropic/types/beta/__pycache__/file_upload_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/memory_store_create_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/memory_store_list_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/memory_store_update_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/message_count_tokens_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/message_create_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/model_list_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/parsed_beta_message.cpython-312.pyc,, -anthropic/types/beta/__pycache__/session_create_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/session_list_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/session_update_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/skill_create_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/skill_create_response.cpython-312.pyc,, -anthropic/types/beta/__pycache__/skill_delete_response.cpython-312.pyc,, -anthropic/types/beta/__pycache__/skill_list_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/skill_list_response.cpython-312.pyc,, -anthropic/types/beta/__pycache__/skill_retrieve_response.cpython-312.pyc,, -anthropic/types/beta/__pycache__/user_profile_create_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/user_profile_list_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/user_profile_update_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/vault_create_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/vault_list_params.cpython-312.pyc,, -anthropic/types/beta/__pycache__/vault_update_params.cpython-312.pyc,, -anthropic/types/beta/agent_create_params.py,sha256=eqyl61q0l5bKOL9r8mTnXox4WMlBjyT9SzdaOy6is9U,2571 -anthropic/types/beta/agent_list_params.py,sha256=TxKU3TLBvIdDjb2fZqbx7TPq17ole6DG3tv5UWQ3XRI,1165 -anthropic/types/beta/agent_retrieve_params.py,sha256=Nx-kb0t-6Lgn9wSX9blQUOV3z3qlYiGsWBKLEUeqvwQ,655 -anthropic/types/beta/agent_update_params.py,sha256=1437qaQ8KN8N26QMj7tvo2qBdDvZ8CKtqmy2hbyaV1k,3278 -anthropic/types/beta/agents/__init__.py,sha256=8D00gPyfqyI1xtAcK5pwkjWf13XkfKP0W-mSLHP8Hgw,195 -anthropic/types/beta/agents/__pycache__/__init__.cpython-312.pyc,, -anthropic/types/beta/agents/__pycache__/version_list_params.cpython-312.pyc,, -anthropic/types/beta/agents/version_list_params.py,sha256=yubpMKlYFt5upDF6PajwUosZCH6TNC1LixuoL2Z7hkk,661 -anthropic/types/beta/beta_advisor_message_iteration_usage.py,sha256=7sVIfoaeLKu53t3I14dfu--E-pNlfo3onRPWSpJPZuk,1167 -anthropic/types/beta/beta_advisor_redacted_result_block.py,sha256=XqwfZFqjAxwR0n1o1LTU41NE_PBFCc4DVE7ZW9Szeus,441 -anthropic/types/beta/beta_advisor_redacted_result_block_param.py,sha256=wF4JyBHVYW1hUjaXoFFaRWBsO69Me7eYeEPoMn8PGBI,479 -anthropic/types/beta/beta_advisor_result_block.py,sha256=tnN8hRcrX78v-ZS4XGqjz2O1025FolSdZNQF3c3LsCA,291 -anthropic/types/beta/beta_advisor_result_block_param.py,sha256=L-UNwrm5x6Zrm0A_doE5pkJdg1SGZEPPqCxaXSYw_o8,357 -anthropic/types/beta/beta_advisor_tool_20260301_param.py,sha256=aNp21UezrEQeOV4eUDwpDaXskAYc6Bsk5iIERTgrDPc,1745 -anthropic/types/beta/beta_advisor_tool_result_block.py,sha256=EJTK16mKSsWJcr004wvd3cX_VcU_egxsTgCtDXFjF60,704 -anthropic/types/beta/beta_advisor_tool_result_block_param.py,sha256=ILYAI3pZx4Nxd49Lo-QuzLRN4I2-KkSodqztMZVPjFc,1052 -anthropic/types/beta/beta_advisor_tool_result_error.py,sha256=NswyiI27v93pD0uVnOUFsDeBnwQhFN12hTskQ3xE3Ao,492 -anthropic/types/beta/beta_advisor_tool_result_error_param.py,sha256=J5L1jAzYDg319SOy6PMGIB1qqmZ9-4D2DFPKz6FFRzU,600 -anthropic/types/beta/beta_all_thinking_turns_param.py,sha256=tC2sF_TI22gg4pa6BN4EYHEdudq0M5DIiAQczqiWyAo,317 -anthropic/types/beta/beta_base64_image_source_param.py,sha256=njrnNCJcJyLt9JJQcidX3wuG9kpY_F5xWjb3DRO3tJQ,740 -anthropic/types/beta/beta_base64_pdf_block_param.py,sha256=aYzXqHuaoyXgNNIRnVo0YdyVT3l0rdpT9UoN4CmAYlI,257 -anthropic/types/beta/beta_base64_pdf_source.py,sha256=RbkrF6vfc4tMgntlk3U7jmrdpa876HxO8iDa28szsKA,321 -anthropic/types/beta/beta_base64_pdf_source_param.py,sha256=EeDrTSoJ0TtH2YfimFHtvwMURQ0rbStvrAEVevCnkSs,699 -anthropic/types/beta/beta_bash_code_execution_output_block.py,sha256=3HnSSH_XDKQKbp1Twg_szNHXVxoeVX6EwbLJ_V2nqEg,326 -anthropic/types/beta/beta_bash_code_execution_output_block_param.py,sha256=MvZUE4MZdIccAf5_QmLKsvHnocVr-o8iqdrXvQ3idU8,392 -anthropic/types/beta/beta_bash_code_execution_result_block.py,sha256=PSoGRNzfr045Q9jpEk7PxzrbQCD1x6WHDdfI6QO8rn4,525 -anthropic/types/beta/beta_bash_code_execution_result_block_param.py,sha256=Ot2MyAQplQR5FGemJEWVSoVG_86g4KDu-nodzod7R4k,646 -anthropic/types/beta/beta_bash_code_execution_tool_result_block.py,sha256=eem9jC9gHmHfG-pu27kLZAoMEhdOYcdZ0F8GJbUyuDY,689 -anthropic/types/beta/beta_bash_code_execution_tool_result_block_param.py,sha256=f7Mesx4pldLNeGeSkD2Sm-3YdBCdtNVZDwNDIsEwx1o,1015 -anthropic/types/beta/beta_bash_code_execution_tool_result_error.py,sha256=opjX2RTHSrF51gMTnn_0zUhoNWmNxvy7g10jc22p8ZI,476 -anthropic/types/beta/beta_bash_code_execution_tool_result_error_param.py,sha256=FApBEL4XyZNtOgpVpahyolzeLrQy2RMKKJBFrmotTvU,564 -anthropic/types/beta/beta_cache_control_ephemeral_param.py,sha256=l_knz_Mf0KnXkhO47kRp7AW_5WwJZV8kIjE-8JSRPDc,537 -anthropic/types/beta/beta_cache_creation.py,sha256=zqVV6J8sxETdrrOLmPQVMAsxnLptcz-ESpHeJcXrzpo,416 -anthropic/types/beta/beta_capability_support.py,sha256=XyHz5EKHITnkbcKz5yWE13hx-dTYKq3wOXP-z-X1YQY,336 -anthropic/types/beta/beta_citation_char_location.py,sha256=GoAYqL-EFKVJyGSpBR6AmziTRB320dUM-1lR3j17iwQ,482 -anthropic/types/beta/beta_citation_char_location_param.py,sha256=5Q9mepqDKAnm5BM0bMrcqJP44Pwfqw3ABDIOXW2iTCk,546 -anthropic/types/beta/beta_citation_config.py,sha256=S59joae15tW789z4lmQ_KsxzUYnuWDX1VArKvNWNGi0,211 -anthropic/types/beta/beta_citation_content_block_location.py,sha256=ZZWGGR0zKA05fzuouWhxNG9RFzq3BCLU5zwbTMQtjyw,509 -anthropic/types/beta/beta_citation_content_block_location_param.py,sha256=egBVOEPTGHmlACdjQC2msxlrxUyEDE5a8tuDVORQ-Po,573 -anthropic/types/beta/beta_citation_page_location.py,sha256=YPlI6R0OfVek8wT88_DX-2_OtpXE7dRoZ3TimQ9P3Jk,484 -anthropic/types/beta/beta_citation_page_location_param.py,sha256=Vdku-ReIo-VsVlaSdIVMyoLxUd-c7g3IdRLlcC2J-Yk,548 -anthropic/types/beta/beta_citation_search_result_location.py,sha256=PQGJvBAk5foB2nzPd1-9hlIjE6XB7swvrrh1A0SYjU4,487 -anthropic/types/beta/beta_citation_search_result_location_param.py,sha256=9xoAly_Z7SYf6uhb4Bu4PA33VyPuhlnDcbWwhLIaCYQ,596 -anthropic/types/beta/beta_citation_web_search_result_location_param.py,sha256=4RkUH9rG9bIU7zgpWE2JwfmZmVtryKu6gQ1hAMTbjx0,525 -anthropic/types/beta/beta_citations_config_param.py,sha256=3mv2HzC7BII1OYox10dhjtgxiRmucT5eNYRLxLoYm7E,279 -anthropic/types/beta/beta_citations_delta.py,sha256=Fjk3Sv5fVuZ90q4tPANkELaiWjLrTxhu2xb8ipitiH4,1069 -anthropic/types/beta/beta_citations_web_search_result_location.py,sha256=m03Z39Tc2_6Kcx-qg0_odmWgMZbdNcUsMGFOPrYrOIQ,438 -anthropic/types/beta/beta_clear_thinking_20251015_edit_param.py,sha256=hQwdR1Zli632QTSIReaZMie9kFadB1lSIHfz8eXhGh0,779 -anthropic/types/beta/beta_clear_thinking_20251015_edit_response.py,sha256=gYTDMSREW1-gQ-J6jCecZEaJGq7ofxIyqfIUHCL-tcM,543 -anthropic/types/beta/beta_clear_tool_uses_20250919_edit_param.py,sha256=_8AVMNiDDw1J0Ojkzk3gi1Feyu6Z-zqGrR_0mluV2lE,1485 -anthropic/types/beta/beta_clear_tool_uses_20250919_edit_response.py,sha256=mbHO2KfaTVYREJBkxhPj4vEFtZyFtwlezKObsv7Fe6E,534 -anthropic/types/beta/beta_cloud_config.py,sha256=tfEFGCDvU5GQFn2Nd0g0BAkFooT3zOVwRLAv_WKwXD0,844 -anthropic/types/beta/beta_cloud_config_params.py,sha256=7XD7k5pQ7b9lPtB9ZXTCJi4SJxgK5YXrR2bz8hbPWZ4,1327 -anthropic/types/beta/beta_code_execution_output_block.py,sha256=OpNDX-uckWDLBg70X1gKYNk2LAj6Re3UCOgOsnxJY1I,313 -anthropic/types/beta/beta_code_execution_output_block_param.py,sha256=EOtPBBkd-AJSbmHg_RDUY01rQLH90Q1_NZjX5CHFAeo,379 -anthropic/types/beta/beta_code_execution_result_block.py,sha256=9xSRmN5jLtLU7i8OykIZ2avIyaQYN-AaruG6iH2-H80,499 -anthropic/types/beta/beta_code_execution_result_block_param.py,sha256=ntPk_c1f0xjvW-8EKinOJAyWhKsfwyPrwcxMbK8-0t8,620 -anthropic/types/beta/beta_code_execution_tool_20250522_param.py,sha256=nQZMQVxGO84me8ZglSuZYsXGLLhvQWwEv1sr0emPdZc,1119 -anthropic/types/beta/beta_code_execution_tool_20250825_param.py,sha256=zmQ0YeTBL7WyhLAeMerkK36YG7OwdRiMonTP71xXpJM,1119 -anthropic/types/beta/beta_code_execution_tool_20260120_param.py,sha256=vU1RJik1Wg7kJE2AGZWaswomchbIH-eDQXUlsjXDiOM,1223 -anthropic/types/beta/beta_code_execution_tool_result_block.py,sha256=4LJZ9VAhvZ2bnyoUdm2BcBlaeb2XOgx3wpa53pcb4P0,567 -anthropic/types/beta/beta_code_execution_tool_result_block_content.py,sha256=pz7sNtRUlssyJg3NdBgCRsDKcR3rlXwyoAaq7Z8K6cM,630 -anthropic/types/beta/beta_code_execution_tool_result_block_param.py,sha256=K8jdof-h28TH6s6pxgkD-noPADyLaItVNxEfVAjaMag,911 -anthropic/types/beta/beta_code_execution_tool_result_block_param_content_param.py,sha256=CQOcbTWBBgMNAeUA1Ww72RwrUXbcfxgxlyoR_tUd4QM,734 -anthropic/types/beta/beta_code_execution_tool_result_error.py,sha256=SuLEG42APBGhO0jVc2xu8eXLS1_mM9x5trxtwrXS5ok,461 -anthropic/types/beta/beta_code_execution_tool_result_error_code.py,sha256=BqoqrsTwo3PuXYz4mPiZr4z-q1kx2hs5iLoL7_otmyU,338 -anthropic/types/beta/beta_code_execution_tool_result_error_param.py,sha256=rTdT_buEPIfaeGUnF9_pdfexZhjh_zdQju3LcERNVis,528 -anthropic/types/beta/beta_compact_20260112_edit_param.py,sha256=vtooQBtPXTRPAADENcqm50qOY-uSamGDGeMIqUyKkkY,875 -anthropic/types/beta/beta_compaction_block.py,sha256=w8u0MkAoGTpeXfFzFPzj3NkUDNiCCM_HHELnKq8YXt8,828 -anthropic/types/beta/beta_compaction_block_param.py,sha256=aJT44haCQnWBaS-Bd2LmGrfNt-FmC7lY-OV6DNUXCQg,1146 -anthropic/types/beta/beta_compaction_content_block_delta.py,sha256=Fvpr8fSR7XXPT0Dqh2-DpTGBLpt-_iVfP-s6GXBpBxo,482 -anthropic/types/beta/beta_compaction_iteration_usage.py,sha256=I7vqMAcJigQBCZG8bzXhFlPRAhfHEfvpTSFSY5ZjHnU,912 -anthropic/types/beta/beta_container.py,sha256=TdNMRmTVJlJuvN0KEjeCYmSTXeKf0Wm8FuNmKsLyFEA,625 -anthropic/types/beta/beta_container_params.py,sha256=JI1gltJKsz-6RWH7Iyho8_Y9Im659b92Un_sKQOYP9g,539 -anthropic/types/beta/beta_container_upload_block.py,sha256=ebNuqOpWiBh41Q2qIrVZr1JYWqMxxoQQ1-3MylywG8k,364 -anthropic/types/beta/beta_container_upload_block_param.py,sha256=49MzD6ylPreuDNSS22iTT-ByyMwjlwmRnO9yxS5ZAGI,782 -anthropic/types/beta/beta_content_block.py,sha256=iiBE_dnVHtfJXI6IKDjLe-TEwraYdu-Kdf5CSNJucro,1988 -anthropic/types/beta/beta_content_block_param.py,sha256=M9VyVtZQwE6shBSDom3ULoJ7eTmj-cQBQra3R9pnZuw,2604 -anthropic/types/beta/beta_content_block_source_content_param.py,sha256=IxeRBqzUPEC35VXHr4xHkQdpMw_A5hqSnBwyixn9v7E,445 -anthropic/types/beta/beta_content_block_source_param.py,sha256=baurrUKAlsFMqHnhtEN_1dGYC7b1vakKpdLiX87pFhU,530 -anthropic/types/beta/beta_context_management_capability.py,sha256=R5r2q04hGLI_ek105eXb7Mu2m87mW07QaThllUuoYUo,804 -anthropic/types/beta/beta_context_management_config_param.py,sha256=SByL1rXGhyAi_IT8khzsXeuf8GyJrK2g9_o81OV1P4E,795 -anthropic/types/beta/beta_context_management_response.py,sha256=qwkhE6vtToG7m34R8cxPyOfzej_x7Nfeis5tuEWk8mI,804 -anthropic/types/beta/beta_count_tokens_context_management_response.py,sha256=efL0nsrOlA7KTIQ-M5IiXRmbmb6q-dakLp3oNnEh5G8,341 -anthropic/types/beta/beta_direct_caller.py,sha256=TM5hhPsVBlEOb7G15636eOHuOgIjwM3mH_Y6iKtJl14,308 -anthropic/types/beta/beta_direct_caller_param.py,sha256=UaGWnnuhgWJY69aZ9RE79gNPUiJvALLc2lkLi-1Nsv8,364 -anthropic/types/beta/beta_document_block.py,sha256=lehaAYYdGHJay8F_J-GfMLOYWAe0G8gVWfeixA5XH2s,834 -anthropic/types/beta/beta_effort_capability.py,sha256=Gs5AXYnM2viC7DUxXTFgjXcPamgMY7QUeNDkZLNbRbw,885 -anthropic/types/beta/beta_encrypted_code_execution_result_block.py,sha256=4HL1lwVh5a-Pd0BQ9EbHFlgBmgHvnENgyjfF3Hd1nyI,622 -anthropic/types/beta/beta_encrypted_code_execution_result_block_param.py,sha256=BddxlcdjX3lXs6tdF30KYEXFtT9oPGeR_KR4goDBMvA,743 -anthropic/types/beta/beta_environment.py,sha256=WUqsB57Dg_U4XLpQEJ3wANx3QCrU91OHvOF1wdNNpeU,1119 -anthropic/types/beta/beta_environment_delete_response.py,sha256=Ldze7vwHdYMSsGncEbvzHONtUNJ5BnIpwHWfq40oujk,423 -anthropic/types/beta/beta_file_document_source_param.py,sha256=a5_eicJChOrOoBr7MIVj5hA-MZFs1syo5Oi8W_Jv1_4,350 -anthropic/types/beta/beta_file_image_source_param.py,sha256=5ogaJ3H_NNz2M1Qa5XWyB2uUf-0HHHLkwYXJuA3kOwQ,344 -anthropic/types/beta/beta_file_scope.py,sha256=GwtyEN8IFRLcagvBxorK5WXb5IgG8B6VWrEFxFhyioM,378 -anthropic/types/beta/beta_image_block_param.py,sha256=CkS_-Ft9RuiIEdsUNXUFMSphVYD2RCxJGSU_2C4ZGyk,910 -anthropic/types/beta/beta_input_json_delta.py,sha256=MPlt9LmfuwmpWryQagjkkVHHZRfZzIJZq3a6JWi7auE,293 -anthropic/types/beta/beta_input_tokens_clear_at_least_param.py,sha256=9VMW4rN_ZeSQp5ianz-815vc_h23XjC-FI6ZICsC7d8,366 -anthropic/types/beta/beta_input_tokens_trigger_param.py,sha256=_7MSRq8ZykSOZxxr2upnPqpSZEQ42_m53wHhcqiQ2rE,356 -anthropic/types/beta/beta_iterations_usage.py,sha256=iCF-BF8ex-h-fKiQlvxU5-AzFckYs9OT3b_RaVu3GSA,749 -anthropic/types/beta/beta_json_output_format_param.py,sha256=DYONF-cbP6gSVz0z_5C4H_Nyg-NBrdx59PwTrXiyIbA,430 -anthropic/types/beta/beta_limited_network.py,sha256=evbfebTI2kz7t4cokb8Ln_ov3NkLdBP7f6XSxrn4rCE,788 -anthropic/types/beta/beta_limited_network_params.py,sha256=dwrHM_kSJvD_5hIyIKRDW__FHpsklyTD4858bvTToto,1071 -anthropic/types/beta/beta_managed_agents_agent.py,sha256=rFnN72evvFTUjT4NM_rxZFOa1LP7ML1CxoR5Zl9KI_g,2017 -anthropic/types/beta/beta_managed_agents_agent_params.py,sha256=WsBQ5t-bHjCmhzPtLgFxvp3_QAY8tVURnuUb654tDjM,661 -anthropic/types/beta/beta_managed_agents_agent_tool_config.py,sha256=Zfb9nH9yrfB3WN7LSUg9JWgFlcPH9k2OwRilP5pesLA,990 -anthropic/types/beta/beta_managed_agents_agent_tool_config_params.py,sha256=gQow7CI_4G6OuyDhZbbmBZGddcqLI51yEQGVX4YQbpk,1144 -anthropic/types/beta/beta_managed_agents_agent_toolset20260401.py,sha256=l5j76syp6iOVRTnx4wYBuuuHv6J-jRtAe5nP4whzW8M,702 -anthropic/types/beta/beta_managed_agents_agent_toolset20260401_params.py,sha256=JqqFkWUrgluM9xfzWk73uhTCyx7URQXa529uiCwYI0c,1004 -anthropic/types/beta/beta_managed_agents_agent_toolset_default_config.py,sha256=UtY5Y48HfGNcMg6NGnRHZdhhPBWeCdshzdD92RAm_eQ,871 -anthropic/types/beta/beta_managed_agents_agent_toolset_default_config_params.py,sha256=dfuAnDJyn377fEb_hWIrheviJxMXF-bSKFn9pXFp6ZE,992 -anthropic/types/beta/beta_managed_agents_always_allow_policy.py,sha256=7PWVNmYYh7fxsznzK98vDv1FznmzIquSyjmYmmaGZV0,374 -anthropic/types/beta/beta_managed_agents_always_allow_policy_param.py,sha256=R-pJXkaMcIZxc6QBc7dWR3PIyAHfSXj_clyUWqBEVKI,430 -anthropic/types/beta/beta_managed_agents_always_ask_policy.py,sha256=X2037QPCHT0L96St_VP0YcFzNFuW2z9qEbcOhoVGNZ4,358 -anthropic/types/beta/beta_managed_agents_always_ask_policy_param.py,sha256=IC76fRhpfPVG2hC-wNE3t0OB6K-cZY8_110EzvMP-4Q,414 -anthropic/types/beta/beta_managed_agents_anthropic_skill.py,sha256=A2avg7h9o-VjMJKqQ3fhYWVFrDTyKaEhOXF5aarMUbE,373 -anthropic/types/beta/beta_managed_agents_anthropic_skill_params.py,sha256=UUeLIN4BKm9u0unLwEy0oQJJ96RBfqlOph6zUOVyrO8,588 -anthropic/types/beta/beta_managed_agents_branch_checkout.py,sha256=ZMwgRdmo7CTKZyQmX0SLfXB4LtVDhgYpkEfsYucBMNk,337 -anthropic/types/beta/beta_managed_agents_branch_checkout_param.py,sha256=o_ZLwzZZmiG1gpZHBXWppSUcvhbUJsV3U3uqdrxUnkA,403 -anthropic/types/beta/beta_managed_agents_cache_creation_usage.py,sha256=iJxIRqhPEdoS1M11qn--UM5fj5IUDGdNCMRAuQLG2wM,567 -anthropic/types/beta/beta_managed_agents_commit_checkout.py,sha256=yRFYegRTS-qi4zyUutUgC63MBZny8O4tyyq9gmplZhE,340 -anthropic/types/beta/beta_managed_agents_commit_checkout_param.py,sha256=eBiGqSdp_4vmQoG4OIXNq0jfAoQ0_MGL-RDi1rBOe7M,406 -anthropic/types/beta/beta_managed_agents_custom_skill.py,sha256=SAOPwdJAsbCfrW4g9rSFb_EhX1hbDDksLG0dV-cI-rM,366 -anthropic/types/beta/beta_managed_agents_custom_skill_params.py,sha256=xK7dBFchjHcZCFsqF0ZcVSOQdc-DcjfDD_XLmDULs2Q,586 -anthropic/types/beta/beta_managed_agents_custom_tool.py,sha256=pESTxqf8-vsx82pbqCO-0FSTpxbE-IhYDjHBwxYLWE4,581 -anthropic/types/beta/beta_managed_agents_custom_tool_input_schema.py,sha256=vqeZUmKZKSzdwNSyLeHee4sQv7YgauTfHZr0kZKeSMM,673 -anthropic/types/beta/beta_managed_agents_custom_tool_input_schema_param.py,sha256=tHblWFAajx-qLdmCXZgPub3uVL-gWDim52pYKZ27GNc,710 -anthropic/types/beta/beta_managed_agents_custom_tool_params.py,sha256=o_9s8aGK091fCao_JMS0umWfPn_YwBc2pTEFmtNfAGw,1155 -anthropic/types/beta/beta_managed_agents_deleted_memory_store.py,sha256=rONXaQlSrznvHKL5BDkzGby_fq11NpSd6tzYZ92QVPg,321 -anthropic/types/beta/beta_managed_agents_deleted_session.py,sha256=zsX9cUp9H6FJKSpyn763xeyOHJ_yw4x_xH9zyTkiSYA,379 -anthropic/types/beta/beta_managed_agents_deleted_vault.py,sha256=WWnBarqO5otp-QrUFsOAKAE5nD8GmnDPNySt4yMCStU,396 -anthropic/types/beta/beta_managed_agents_file_resource_params.py,sha256=7d99NZoWgRmzKr914ZUn_5IIxdjjC4QaWmSyn_h--qs,623 -anthropic/types/beta/beta_managed_agents_github_repository_resource_params.py,sha256=dcNf8NPnKFED1SUs-OAq-KmsnJjaYhv2AM-s4EpwxRo,1188 -anthropic/types/beta/beta_managed_agents_mcp_server_url_definition.py,sha256=pywZ4tbPsb6AMtPNRhKDtV4ep73pbXMhXR3x5JI2nvA,401 -anthropic/types/beta/beta_managed_agents_mcp_tool_config.py,sha256=HGQ7fNyThrbjjPKxYpxoOnPR2z3VkCcAF-AL0ZVmcQo,862 -anthropic/types/beta/beta_managed_agents_mcp_tool_config_params.py,sha256=q6_wYAONhAXlljASgYMZzoGMMctJ1XH8AJSpf8W-smI,1027 -anthropic/types/beta/beta_managed_agents_mcp_toolset.py,sha256=Z7YR47RdkSWIvkEyV6Ju2HQYK26zuP3WaKXe0zF4mqE,702 -anthropic/types/beta/beta_managed_agents_mcp_toolset_default_config.py,sha256=WhmvmjdrDn6SlpyUqYiD5-SMl45Bakj1TtYmNQtWROY,884 -anthropic/types/beta/beta_managed_agents_mcp_toolset_default_config_params.py,sha256=Q7HS-Jr2CnImH5sRQIk_UrFZj4md0vc2XSShRCi2Aj8,960 -anthropic/types/beta/beta_managed_agents_mcp_toolset_params.py,sha256=7OKvyIcfeGMLthonexyc84weZe9Fn_PifDg3S-AnQhg,1067 -anthropic/types/beta/beta_managed_agents_memory_store.py,sha256=YYGraIMLU1rY0XVp49qN0WaYoGaIZd7y8z9DJVWG214,683 -anthropic/types/beta/beta_managed_agents_memory_store_resource_param.py,sha256=ytW4xDLBChJli9kzj-PUHdKtz-e6IkskSqNtPg_ruuA,907 -anthropic/types/beta/beta_managed_agents_model.py,sha256=4OYSuqUD58MD7seB0cwKvlhWFB6CdaWIJzOlF6GMzw0,552 -anthropic/types/beta/beta_managed_agents_model_config.py,sha256=w8wOfL7cPX7AzY5L8vsNWiRBXtcCgIWkKPUHeHYmW04,855 -anthropic/types/beta/beta_managed_agents_model_config_params.py,sha256=8JykQenzaJcVpnEFpHF9FEooknELhC-aoiqgwr__pbw,958 -anthropic/types/beta/beta_managed_agents_model_param.py,sha256=S7XYggr_sNaNiF3aSxndJ1u6DMfBwzBfR_s9x2aqpFA,598 -anthropic/types/beta/beta_managed_agents_session.py,sha256=uXsY8_XckD9P4O1zsV_KLq1VbesHHQyCS668h4a-31Y,1659 -anthropic/types/beta/beta_managed_agents_session_agent.py,sha256=ZI1bgR0NqdL0wN2FeWrNKZdGphZ68CkdkZodoeWsagY,1723 -anthropic/types/beta/beta_managed_agents_session_stats.py,sha256=YJXNVJn-RrYUpIgBlJZNggi8t7OPMoxEJUJiRl7pQn4,602 -anthropic/types/beta/beta_managed_agents_session_usage.py,sha256=y8T9mMxfUrd7ELp28k9aRTMpsOQklT-BL1-GPc0oLc0,841 -anthropic/types/beta/beta_managed_agents_skill_params.py,sha256=LNBzArFn92epQqXcThLlTWqtStO7hOz_fUGxLa4sA74,545 -anthropic/types/beta/beta_managed_agents_url_mcp_server_params.py,sha256=ao88BAX-uTNFyw7KsNuhnL9MNIr3Z_-22c00z5xtrbU,582 -anthropic/types/beta/beta_managed_agents_vault.py,sha256=8cNzwe9KsREW-Wn_fB7a6F30cr4Uwa1t0Q-7FbWpx0o,844 -anthropic/types/beta/beta_mcp_tool_config_param.py,sha256=UBXvtQkihooZFlCW_DazHKGHnIs5ZT_mJNv79m_IVok,364 -anthropic/types/beta/beta_mcp_tool_default_config_param.py,sha256=xYBOPL0TPdn83bgx4FufVNGdqMR1Ghq7d1bJ7YZYx4w,376 -anthropic/types/beta/beta_mcp_tool_result_block.py,sha256=mqx1WHh13wYoGpf5PnG8dgGsihq3qd9Pg6t9nolIwGI,439 -anthropic/types/beta/beta_mcp_tool_use_block.py,sha256=KRvDIWyDfq5i2zKGtlY3ZDxHsYxtfmqHa0knEJ5UZnU,444 -anthropic/types/beta/beta_mcp_tool_use_block_param.py,sha256=sE-16rLzREIri44iPGbQgAuRMw-Tsj5vTLUonOqW5K0,723 -anthropic/types/beta/beta_mcp_toolset_param.py,sha256=lNEiIL5rfFDz-tfac2XGWkkUE2e0ddFXOYI1jcBj_I0,1240 -anthropic/types/beta/beta_memory_tool_20250818_command.py,sha256=It-xNhxO4M7DSqpczVfZq7mD2FPDZniHGUxCq9wSGGs,1179 -anthropic/types/beta/beta_memory_tool_20250818_create_command.py,sha256=jmrc8aWVghMz5PRW7vo5LPp3GaUDZkl7Ir8rmqNVsHw,453 -anthropic/types/beta/beta_memory_tool_20250818_delete_command.py,sha256=dRjSRkChmc6P_vIwIWlknVEXcXikM6EZjJxZgqfT-TA,396 -anthropic/types/beta/beta_memory_tool_20250818_insert_command.py,sha256=AkchM3mjzYmLC9Os12l1g4NNIvO1Caf9FNk_1w-jIIc,546 -anthropic/types/beta/beta_memory_tool_20250818_param.py,sha256=ZNdj-ICrBXYGooCFnhPaOWhjCMmpW0UdhM1KTvkJ3RA,1154 -anthropic/types/beta/beta_memory_tool_20250818_rename_command.py,sha256=39AhTdurJEXwEdK54_Z-RjzAOMSvo8AeNo2KHD8vlgA,462 -anthropic/types/beta/beta_memory_tool_20250818_str_replace_command.py,sha256=2FBRtAlMbGio876ixv3NnoDkdIGHfoKwQswUWo6qTfs,524 -anthropic/types/beta/beta_memory_tool_20250818_view_command.py,sha256=NGvvJd_GEaQRfOXH0-YmGYpyyCtj7WkX9RQZ1iVc_OE,519 -anthropic/types/beta/beta_message.py,sha256=keJPQeCk0z7Kx6AwCkeOBTAhcACFirc1oLZIhCpOaXs,4226 -anthropic/types/beta/beta_message_delta_usage.py,sha256=T3JsPAZdwAo-qrFigRxwcbFVNLm3fuZTu5U6zOzS3to,1357 -anthropic/types/beta/beta_message_iteration_usage.py,sha256=WgwcNlVdp2eO2BEd-QsoQQBwZc7spZjGDW2XZDMvaF0,899 -anthropic/types/beta/beta_message_param.py,sha256=jelI5bL_5DFMW5-aKDpBf1KsK-CvIZkueSrU_Go3gUc,477 -anthropic/types/beta/beta_message_tokens_count.py,sha256=yO_2_42iBaPzX5izF1vTXoGSS1qy9LxzAf1K9GQgr4Y,621 -anthropic/types/beta/beta_metadata_param.py,sha256=julUtAFfgnCXSt0sN8qQ-_GuhJvpXbQyqlPhyzE8jmQ,602 -anthropic/types/beta/beta_model_capabilities.py,sha256=e2gMOiOjEZncoigQ2ZejNfL109gOB54irj707G9N4nU,1432 -anthropic/types/beta/beta_model_info.py,sha256=5tS1jaOGTsOFI-qE18mS5TZPHZd1yKxUpktptNKs7Qs,1070 -anthropic/types/beta/beta_output_config_param.py,sha256=z4HZeNbbX0rFE47AFGOsWBdhQXwRCIcSgaXt9YYrTpc,884 -anthropic/types/beta/beta_packages.py,sha256=vXLAJ69fSrkXzRoWgy2FXJhirc_rAM6BB3MGGERbNQQ,753 -anthropic/types/beta/beta_packages_params.py,sha256=BjuFiiBDs1KpS2ze9IwXMv8YjvsRa3ZTNPSqLg-bVYA,1168 -anthropic/types/beta/beta_plain_text_source.py,sha256=u3XpMPojTxn-_LvFdYYMLc_b8WI2ggIXdoZ4pDK4Q-Y,314 -anthropic/types/beta/beta_plain_text_source_param.py,sha256=5VW_apR2n3-G6KmDq6b58Me7kGTcN2IAHAwsGbPrlVQ,390 -anthropic/types/beta/beta_raw_content_block_delta.py,sha256=yqzrvWsuJMFahDLOIG8cNRosgJoVvFjdwAli-jpnNZE,859 -anthropic/types/beta/beta_raw_content_block_delta_event.py,sha256=-hn4oaYfZHCWJ5mUWeAHDM9h_XiPnLJIROqhztkiDM4,415 -anthropic/types/beta/beta_raw_content_block_start_event.py,sha256=Q0qeQfB9xPT5OK1fNlmZJlRKbZDsjhzqUWq6zZcg9vY,2258 -anthropic/types/beta/beta_raw_content_block_stop_event.py,sha256=JcCrM004eYBjmsbFQ_0J-vAngAPCKlkdv30ylh7fi70,308 -anthropic/types/beta/beta_raw_message_delta_event.py,sha256=i2TFskddPw53jop-SJnt52u15rJ9tYl3GpHm546lEN8,1909 -anthropic/types/beta/beta_raw_message_start_event.py,sha256=v7dcNblqSy9jD65ah1LvvNWD71IRBbYMcIG0L3SyXkA,343 -anthropic/types/beta/beta_raw_message_stop_event.py,sha256=Xyo-UPOLgjOTCYA8kYZoK4cx_C_Jegd5MYVjf0C2-t8,276 -anthropic/types/beta/beta_raw_message_stream_event.py,sha256=8Aq-QAF0Fk6esNiI_L44Mbr9SMaIFqNfi8p2NF6aO80,999 -anthropic/types/beta/beta_redacted_thinking_block.py,sha256=DVNuN59cCWpVBFWTYvE5fVPwBEb1LRF27d-BHVgApJI,300 -anthropic/types/beta/beta_redacted_thinking_block_param.py,sha256=BTpab5mqgUtlSgtXTPap0x8HpqVAyTvLoB3pf6o1TqI,366 -anthropic/types/beta/beta_refusal_stop_details.py,sha256=nZH2AkF6uUs0_cno_rnh4qE7r3gFdf7gdHlPdDeeFQ0,735 -anthropic/types/beta/beta_request_document_block_param.py,sha256=lEIWndNXBXQSANKa6KL9BgC6T7MGHfq43z0ObvDqw6k,1319 -anthropic/types/beta/beta_request_mcp_server_tool_configuration_param.py,sha256=CFIzeyT9ni1lBMCUzIwkyp8D_6ry9MmWLaPhMQByVMA,441 -anthropic/types/beta/beta_request_mcp_server_url_definition_param.py,sha256=j8N0ixvGHFheT2KqqI64HKufHmVST9VcJgShtlkPzUw,644 -anthropic/types/beta/beta_request_mcp_tool_result_block_param.py,sha256=xK9SY8bmetn-LWN4hks8KDbeh2WiF0pttcCXsB99v84,761 -anthropic/types/beta/beta_search_result_block_param.py,sha256=uqzKu_6YVDRe6rIbVSmfvQE7YleyRfa_UncwI2k3cuI,842 -anthropic/types/beta/beta_server_tool_caller.py,sha256=CWaGKr7F39lE-vvUAIz_GsE57NLdSLGwTJSzcp_pE48,359 -anthropic/types/beta/beta_server_tool_caller_20260120.py,sha256=eACqMqjfEb1Wq_amuHUZSzVuolVJMNiDfuK3QS9hVms,315 -anthropic/types/beta/beta_server_tool_caller_20260120_param.py,sha256=Qiq9fQ1YPOf4PZgYqeok41kp0HXImic-gF5Gycabulw,381 -anthropic/types/beta/beta_server_tool_caller_param.py,sha256=gEIDqI2JeKdVEX_5QgmVvN8Aqo2qHViwFtKDsrvstZ4,425 -anthropic/types/beta/beta_server_tool_usage.py,sha256=StokZ2PZBQ5r5X8ri71h-eZsFHqLdT0138Tafqy2az4,352 -anthropic/types/beta/beta_server_tool_use_block.py,sha256=3gNWYr7VAZnWv_xGYMrwppspt-XGEHG0xM7zg2EbnHQ,1098 -anthropic/types/beta/beta_server_tool_use_block_param.py,sha256=mksTDxEMLvH_Em_fs5SfrrZFmhWkSmcW3xh1kOA3D6o,1374 -anthropic/types/beta/beta_signature_delta.py,sha256=LGjB7AM6uCcjn5diCtgzSPGMssf-hfS-JQbvtTmY2-I,289 -anthropic/types/beta/beta_skill.py,sha256=mvnxLGUWAN39GYKdKiJVAK8_-ZxvQinb2ovxwdUqKpU,522 -anthropic/types/beta/beta_skill_params.py,sha256=mdRneR8t9DHPX2YrwQi0NBkQmsF5LVnSYqCAggRFOSE,604 -anthropic/types/beta/beta_stop_reason.py,sha256=cqaxnwy6GUssxJjaIYlh5s3eWiDTN3wCIZL5NKRfHCo,365 -anthropic/types/beta/beta_text_block.py,sha256=irciVXypUcB5drTF5p0btH1QzB3ZlfEXq7XxjF1cs_U,684 -anthropic/types/beta/beta_text_block_param.py,sha256=tRCfSMi2Jitz6KLp9j_7KOuToze3Ctlm-DuQH6Li1Do,693 -anthropic/types/beta/beta_text_citation.py,sha256=Ia_-kJ48QQ4ZN5AeWbCSCzAFwNXjVM4LHf-5-jyBJog,921 -anthropic/types/beta/beta_text_citation_param.py,sha256=QuBFgo1xfMkUl3mwWI5FDTRYA-VJ27SpE7ORpyUoTJs,916 -anthropic/types/beta/beta_text_delta.py,sha256=EUXMXCQ7Mk8BnGQzm-kKqIqo5YbbdGLoAlrNLxUxS-0,269 -anthropic/types/beta/beta_text_editor_code_execution_create_result_block.py,sha256=nrQ5lljIQNk_JE4Dw8o5CnT0dY964K_BReTmQj4E4PA,372 -anthropic/types/beta/beta_text_editor_code_execution_create_result_block_param.py,sha256=Y6WowYJPKPDJDQMTp-7cvwe4gU0GD1Q4ei81YhEFs3w,438 -anthropic/types/beta/beta_text_editor_code_execution_str_replace_result_block.py,sha256=gHQ37oPSQ7c0QmUF7XZ6dcbTxHc2aXCg-2Ae6siPQH0,580 -anthropic/types/beta/beta_text_editor_code_execution_str_replace_result_block_param.py,sha256=9FwvDVXX4XsXEwEyCRDyocwHzXjq09zjfiboffnAv-U,643 -anthropic/types/beta/beta_text_editor_code_execution_tool_result_block.py,sha256=_lizxYlA7Tz_9mBAAn7aWPw7tYPGJLoj8MNNyVBO_2w,1103 -anthropic/types/beta/beta_text_editor_code_execution_tool_result_block_param.py,sha256=JymZQHQESONFiTbUU3FrKMHVx_8OsvwbcVNIBwuEB_g,1470 -anthropic/types/beta/beta_text_editor_code_execution_tool_result_error.py,sha256=McC6A7nU3QViJV7lLbGywNmUTDiriwvMtYJ_0g-AV1s,557 -anthropic/types/beta/beta_text_editor_code_execution_tool_result_error_param.py,sha256=xCA1RzA_5nMGpp4_Cpa9sUgkqHr9Iko7I4Gx1OLbXkc,616 -anthropic/types/beta/beta_text_editor_code_execution_view_result_block.py,sha256=Lh93HQ4NjV-1OUqXcRF-ffG9XU0LnxeUhIpqwQiTDL8,548 -anthropic/types/beta/beta_text_editor_code_execution_view_result_block_param.py,sha256=P9UB7HVx7ordu7YZ6M4EceO3yG-OwtrDyHolPe_EIc0,603 -anthropic/types/beta/beta_thinking_block.py,sha256=R-w0ZLaNZzELS2udP0vtxtDmE2MgNcf5gXz9FyMQVEg,299 -anthropic/types/beta/beta_thinking_block_param.py,sha256=tiOk592SxRHZ77nDIpLuocz35B_1yB3qbr7MTZqhnEA,375 -anthropic/types/beta/beta_thinking_capability.py,sha256=6BrEsk3uU6My14tLHla0foPA6Tzusj20XmE3OkkJOUk,453 -anthropic/types/beta/beta_thinking_config_adaptive_param.py,sha256=nCUh-g4fuABP_ibPDVW89_IyVrm6ydko6Qf2ZqwOMoU,692 -anthropic/types/beta/beta_thinking_config_disabled_param.py,sha256=tiVjV6z1NxDUdyl43EpEz3BRIFhDG2dQCjcBYjRc54o,334 -anthropic/types/beta/beta_thinking_config_enabled_param.py,sha256=b7tnskB3k0M5m4s5Y_8dd7Z8_8o7fZQLyBsB8ZUhLSM,1092 -anthropic/types/beta/beta_thinking_config_param.py,sha256=cESklqOtoQ3Eu-exMPZgq8oOT2_B1Vrus2XGMSbAGWs,617 -anthropic/types/beta/beta_thinking_delta.py,sha256=4O9zQHhcqtvOz1zeqcJOo1YJpvzNN7t0q0dEzePswcc,285 -anthropic/types/beta/beta_thinking_turns_param.py,sha256=4rhTtQqaot1VJOhVAIJbGjQ3Q4SVUs9IN0o-TrRfejo,348 -anthropic/types/beta/beta_thinking_types.py,sha256=5Vicro57hNwQKWxUYGDqbhsQfKLyPPvK2FtSajE6wsw,515 -anthropic/types/beta/beta_token_task_budget_param.py,sha256=OS1qzGTKkUPHbFIixWsnymKvfnapqtHJkDPa5OlgMRE,770 -anthropic/types/beta/beta_tool_bash_20241022_param.py,sha256=kx2icDcWhRdzNk2LlkMWzuofPWSbHxK03HOZzbst_24,1146 -anthropic/types/beta/beta_tool_bash_20250124_param.py,sha256=wYB58PCniOXleBO1UMk4dpEcQAkT6j9X5ed50QKxs1Y,1146 -anthropic/types/beta/beta_tool_choice_any_param.py,sha256=DPROata2t1ORbmeV0g6QQVvvzrG252n9v8xrzwGrKjs,544 -anthropic/types/beta/beta_tool_choice_auto_param.py,sha256=WlSP5gg97WteYMYjKp-81iXNygyDrELApismEsVuqKo,565 -anthropic/types/beta/beta_tool_choice_none_param.py,sha256=NqaCkqk3oKXrQ5DC7y5TYaMwuCR-FG5cgPdf9QYXm1Y,369 -anthropic/types/beta/beta_tool_choice_param.py,sha256=kJnRD1gWzx_NPpyfMShZtoXrUcHX6t6WCvhhNd2SWr8,627 -anthropic/types/beta/beta_tool_choice_tool_param.py,sha256=WQZbn92NIJnIQ_m98WbMzV3JMmQmsa0aZP_weO8ciR0,634 -anthropic/types/beta/beta_tool_computer_use_20241022_param.py,sha256=Pnx2oAQZGA7N2QrGwQjtOK4xQgxuwoJwKjK_1bJWTqY,1433 -anthropic/types/beta/beta_tool_computer_use_20250124_param.py,sha256=RaLmGJexyXTXkFyokf0qR9KCYmOK7Z8QZudh7h6B4CE,1433 -anthropic/types/beta/beta_tool_computer_use_20251124_param.py,sha256=8QBLFH9Qd_BjUAAaEnnYOLbJvyfMh3t9QT6H24LMH98,1540 -anthropic/types/beta/beta_tool_param.py,sha256=94TUlvVMem_hRsTl7OGmQxZ8hmvzaV7VQrUxhTYhf3U,2670 -anthropic/types/beta/beta_tool_reference_block.py,sha256=V9jo_vWMQMArdq5NdckUnCu-IeqZKoTmAGdAQbFY4dI,296 -anthropic/types/beta/beta_tool_reference_block_param.py,sha256=jB3AZvKmpwdRZyXuoI9BVOI_GrldY7rfTlHplTvkY40,675 -anthropic/types/beta/beta_tool_result_block_param.py,sha256=Nq6-m74g7glHVUqW6dgMpq_c1B901r4NhUUEGPB14q0,1205 -anthropic/types/beta/beta_tool_search_tool_bm25_20251119_param.py,sha256=Mqm7nrNU6V8coPfrmkEbhIsmz3LHR8I9Ib4yqWYPsbA,1162 -anthropic/types/beta/beta_tool_search_tool_regex_20251119_param.py,sha256=1x6y14WE72JmXnhC-ljQ14lmK4nbZNeeztb_RMK7WYM,1165 -anthropic/types/beta/beta_tool_search_tool_result_block.py,sha256=kVVsMPkwcBzHgv0EDf8rYabFJi3AMuJvIhplinPw7Oo,655 -anthropic/types/beta/beta_tool_search_tool_result_block_param.py,sha256=dGiX5xyWYOAbzIntGa4UDhaTFGan16LTkKLFjawY3Sk,981 -anthropic/types/beta/beta_tool_search_tool_result_error.py,sha256=tnR-X_uYFQ0hiyluau0u9d-Rx0KU3xK9EFXErQla7FE,484 -anthropic/types/beta/beta_tool_search_tool_result_error_param.py,sha256=qOGYVwK7xaphgDxiePo36IEE3pz3fPUE4KfarKC1qwc,481 -anthropic/types/beta/beta_tool_search_tool_search_result_block.py,sha256=1MD5K-GYRFxoil26nhoIn2ZyGls3N3Tq7WOTA6W_kI8,455 -anthropic/types/beta/beta_tool_search_tool_search_result_block_param.py,sha256=aqAQHce8xvuAPfEYFixLJCOOu__6cInJaTWhmKzMu9A,546 -anthropic/types/beta/beta_tool_text_editor_20241022_param.py,sha256=Ez8mCXVKo8bCssI-PTCSep2vwhfiUS9v3ouanWaU--A,1179 -anthropic/types/beta/beta_tool_text_editor_20250124_param.py,sha256=V1Zn35gkl9nlxBDJ7VCZI7InM4-RsUz64-rKJz7QSsA,1179 -anthropic/types/beta/beta_tool_text_editor_20250429_param.py,sha256=93Z18oJOnRRPSvQPW3jIE4H9yEGVAGkYRBG41SkawzI,1188 -anthropic/types/beta/beta_tool_text_editor_20250728_param.py,sha256=rC-6gYTz5z_QxKHBrVPT7AjTclntwMAPhBCfTbiEY9A,1360 -anthropic/types/beta/beta_tool_union_param.py,sha256=MO1wZpOr-LT0x8PyWTk7zsvf8ONFYPFi4EKmutd2Ozc,2889 -anthropic/types/beta/beta_tool_use_block.py,sha256=XEDZv_AXPSEB2jTk69txMAB5QLU59215sXmzmN6o0kw,844 -anthropic/types/beta/beta_tool_use_block_param.py,sha256=RvAb2dxw7O1vdtwb0u1AnoZkQ9oaSUDLr11jmZjogGc,1070 -anthropic/types/beta/beta_tool_uses_keep_param.py,sha256=R9sHxEwQq33kSQEiIG_ONm92EUk0YFmKI039tkhl4vo,341 -anthropic/types/beta/beta_tool_uses_trigger_param.py,sha256=PbTkerKGtnClYCrACGaodsTkHOSpTz801jp_PzvyBEI,347 -anthropic/types/beta/beta_unrestricted_network.py,sha256=mrGMT6o07wSx-DfaOJrEo1rkCoonmE1gnZ13mM_765E,346 -anthropic/types/beta/beta_unrestricted_network_param.py,sha256=duG5vsFU2RM1MrHC64wXwQuvijm4Ziiz9hzGFxuZ0io,402 -anthropic/types/beta/beta_url_image_source_param.py,sha256=pquhkw8b13TbwhXA6_dMkPP-7vxYfbbXbjV_BVx_0ZY,337 -anthropic/types/beta/beta_url_pdf_source_param.py,sha256=Ox2U0GM60MJgQBec8NKPw49uZz9DgR8mhxLCZT7RIVk,333 -anthropic/types/beta/beta_usage.py,sha256=ssKlmL8ogfkesMX1s00CzBpphnah7XjiD2I0oDHZ5EI,1843 -anthropic/types/beta/beta_user_location_param.py,sha256=U2FjNQQI65-r7_CYUqyJ1_ENjrLh0o36NEPC_bYPeB0,720 -anthropic/types/beta/beta_user_profile.py,sha256=BKseXQT70JQdoOcIzSImHSodbNJ15xTY_HNK2plLlKI,1088 -anthropic/types/beta/beta_user_profile_enrollment_url.py,sha256=trNOT44dw6nHgSEkDHzue61os5z7rwETteBgYsycXGE,523 -anthropic/types/beta/beta_user_profile_trust_grant.py,sha256=MbtCZEnnKxb_Be0pKARuaON63nwd0BgHnz9WihH2Quc,336 -anthropic/types/beta/beta_web_fetch_block.py,sha256=zL3A3YWcuTPndBPCXkS2QnVN8dSA5x93x_qoYfWvYw4,523 -anthropic/types/beta/beta_web_fetch_block_param.py,sha256=6q6BR5Mjbknd-S3fIr5FkDEfXZt8CfnlpXB1XviNhz4,631 -anthropic/types/beta/beta_web_fetch_tool_20250910_param.py,sha256=JEu95WJTiFYGP1jM43EFQ3c7N4UkzZS4JfX5ypc_a4k,1877 -anthropic/types/beta/beta_web_fetch_tool_20260209_param.py,sha256=-vauxCpARTRGaIKg5aNtia41MAHZSJW7y8-t2rokUKY,1877 -anthropic/types/beta/beta_web_fetch_tool_20260309_param.py,sha256=XHSipiD9iHRMCwA6EPDop2nGFSqyXY2h7lwlER-fBWA,2205 -anthropic/types/beta/beta_web_fetch_tool_result_block.py,sha256=jf_ypAUGdXDt9vmA0sGH_XnvgbeebyGusHrgKiltopY,1088 -anthropic/types/beta/beta_web_fetch_tool_result_block_param.py,sha256=Isxg5NP0wSmv3yXCi1HJz_l31YhS6_GU_VbMxT_EBTg,1336 -anthropic/types/beta/beta_web_fetch_tool_result_error_block.py,sha256=JWP7NwNHIvw0K-OJ2TKWsIWBFV0HMAkdeu0CzA-cQXU,441 -anthropic/types/beta/beta_web_fetch_tool_result_error_block_param.py,sha256=X9uEu6D34gbMrBewbIo66cd0FOnGzXNVr0w5sfgShoQ,508 -anthropic/types/beta/beta_web_fetch_tool_result_error_code.py,sha256=-kZjKVIUcmPnv15dDbYbs0Hr1xqj4X2LVW1V22A4oV0,436 -anthropic/types/beta/beta_web_search_result_block.py,sha256=8k1ltqF03HVb440Nvms4fRD1xKZmvbrFG-BHeot-SGU,405 -anthropic/types/beta/beta_web_search_result_block_param.py,sha256=pAKcEO3RC5clujQoGSAJOO2o1gpfsYzaebsZ6aIMOfk,484 -anthropic/types/beta/beta_web_search_tool_20250305_param.py,sha256=D7VYkit8iBJqsVb0fXTYTKZf-F67tU0PLt5pzlQL1l8,1808 -anthropic/types/beta/beta_web_search_tool_20260209_param.py,sha256=ZxQFu0idgHNMciMZPiHh5QCbJmpT-xtIo9PqZZ-pN-M,1808 -anthropic/types/beta/beta_web_search_tool_request_error_param.py,sha256=PdRRrtIHg0P00ARhUekoCnlXXZ2H6K6F5wWmJJvKkNo,506 -anthropic/types/beta/beta_web_search_tool_result_block.py,sha256=zzkagD2KZLsvdsCucpxZN_533ChzkWQSWjsSg-nTH9o,981 -anthropic/types/beta/beta_web_search_tool_result_block_content.py,sha256=qm77CYtUz5Owh934Uj5m0oLyCeJ6AoSZ_z3ZwrEi1qk,471 -anthropic/types/beta/beta_web_search_tool_result_block_param.py,sha256=pHBTe4MhU9mBNwnOAjzt09P2p-XggmjhWRPuyxJ2RvA,1229 -anthropic/types/beta/beta_web_search_tool_result_block_param_content_param.py,sha256=gU46iUwyD-LxpaiiUzQbzc4JlhuPiPYaC11VtvTv-B0,576 -anthropic/types/beta/beta_web_search_tool_result_error.py,sha256=toGXIgam7ot0rRMjlnK6VuhGuyv-HmeaHjbkFHMdcGs,437 -anthropic/types/beta/beta_web_search_tool_result_error_code.py,sha256=_TFAKE0LOq4moCUNXpZJxtlPZlAg76TQeN6BbuWoYbc,363 -anthropic/types/beta/deleted_file.py,sha256=VwcPcmaViwLDirEQ6zIYk570vhCbHmUk4Lj61kT4Ef0,437 -anthropic/types/beta/environment_create_params.py,sha256=GW6jJyUh9epiTEEAE_wRFfr5k_eCw2UPQNSQdXqaV3Q,1063 -anthropic/types/beta/environment_list_params.py,sha256=JLY2YQfhPIHcW0aRXQNPDn4GwL9aKsGbKLXUbmkXI-8,853 -anthropic/types/beta/environment_update_params.py,sha256=XbtdSyJfuFYYFc2VcWQBGnx-1-wC7xbCqx5xnHf7pto,1121 -anthropic/types/beta/file_list_params.py,sha256=llPkdc_NQKE86VtkdaaPSH3coXNLaCOxlvUurCCFOeo,1110 -anthropic/types/beta/file_metadata.py,sha256=_zs55goOIQe-kUIquYkpw1zxx93BPzQXJMFMzb2QqUM,1051 -anthropic/types/beta/file_upload_params.py,sha256=CvW5PpxpP2uyL5iIEWBi0MsNiNyTsrWm4I_5A2Qy__c,631 -anthropic/types/beta/memory_store_create_params.py,sha256=kjJgWE-2PCWhWNNvRTTsTe--bLkCOLPlX4lLm2WdMMo,636 -anthropic/types/beta/memory_store_list_params.py,sha256=U_H_diCc-waHQ0QyX3Ui3ubY2_JO1qO_42JB2EXeugI,1108 -anthropic/types/beta/memory_store_update_params.py,sha256=xNOJ76QDhtGSGWcv0ZvmRSfPR6UZtllqsT2qWjvkkYE,887 -anthropic/types/beta/memory_stores/__init__.py,sha256=7-8kV7T12Z90IkfBA2PXIwSDhdyl78PQZCsM29GAQJw,2021 -anthropic/types/beta/memory_stores/__pycache__/__init__.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_actor.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_api_actor.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_deleted_memory.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_memory.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_memory_list_item.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_memory_prefix.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_memory_version.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_memory_version_operation.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_memory_view.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_precondition_param.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_session_actor.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/beta_managed_agents_user_actor.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/memory_create_params.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/memory_delete_params.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/memory_list_params.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/memory_retrieve_params.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/memory_update_params.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/memory_version_list_params.cpython-312.pyc,, -anthropic/types/beta/memory_stores/__pycache__/memory_version_retrieve_params.cpython-312.pyc,, -anthropic/types/beta/memory_stores/beta_managed_agents_actor.py,sha256=sXBIxEJywSUXuBQSUnOpgWd8ltQOjOARArXi91qafuU,642 -anthropic/types/beta/memory_stores/beta_managed_agents_api_actor.py,sha256=-WQWJm7Dqd4iXeKNUGcjDCsKqotRE0njl1sELTWWHUk,299 -anthropic/types/beta/memory_stores/beta_managed_agents_deleted_memory.py,sha256=CQ0URqJ07hIHIZ7f2tFeyUGVPbsaLpi_F5rLoecgoJo,306 -anthropic/types/beta/memory_stores/beta_managed_agents_memory.py,sha256=mOWeY2Kr-BgJfjC51OxNLR-v9BIHAT5FUNckTHt1w5M,634 -anthropic/types/beta/memory_stores/beta_managed_agents_memory_list_item.py,sha256=buSxbIzEAioaHn2PY6h92B0iYX0wLfDzhEgFR8gRDTk,549 -anthropic/types/beta/memory_stores/beta_managed_agents_memory_prefix.py,sha256=kecRiOt25No20GvTg576GK6jfme8roQgh1pSU8nIrtI,305 -anthropic/types/beta/memory_stores/beta_managed_agents_memory_version.py,sha256=gXcA50JhAzzz-F9q7oqlb0kJu5zBy1VPwB0x4r2lB1k,1086 -anthropic/types/beta/memory_stores/beta_managed_agents_memory_version_operation.py,sha256=n0-hBkXxm6oedSjA96AsK0FrBDkDsm8aYFOVpIuHUP0,287 -anthropic/types/beta/memory_stores/beta_managed_agents_memory_view.py,sha256=zx0_kx8nGtfuNJYM6cD9JG2qOcuucah6rlXb-SCduAk,246 -anthropic/types/beta/memory_stores/beta_managed_agents_precondition_param.py,sha256=NMO-sDsydAWC4zqUxb44lpOck5s8_29bA3fKwyg8708,371 -anthropic/types/beta/memory_stores/beta_managed_agents_session_actor.py,sha256=2KFjQ1WwqgmzewyszxYIHzRH8tjXnlVCDfWGvP_Iybg,311 -anthropic/types/beta/memory_stores/beta_managed_agents_user_actor.py,sha256=_IZjI20lMi358_QPJAuMnBLYwHBiykdU7ztd_hip010,299 -anthropic/types/beta/memory_stores/memory_create_params.py,sha256=ViL-xK0RrJXko7xBLUjgGiPnGkjHtyY63HBOrQO--b8,765 -anthropic/types/beta/memory_stores/memory_delete_params.py,sha256=NITaYOHFGfj_jWIjU7w5YlOerfsJiK2e2poWiAQUmTk,669 -anthropic/types/beta/memory_stores/memory_list_params.py,sha256=3JMbA8_FUP3oOh8EdPSXetp1TdEbsYDC772fRDIjAdE,1227 -anthropic/types/beta/memory_stores/memory_retrieve_params.py,sha256=T2kcpK3ws0nu7CKAxu4CTXDO7jbWLF0ewT7FYDrMvG8,732 -anthropic/types/beta/memory_stores/memory_update_params.py,sha256=pSWNBk1P_fh0A5j161GbEc6Qp2C71DnY7f6Jt4ZlY1Y,932 -anthropic/types/beta/memory_stores/memory_version_list_params.py,sha256=hlMF0bfY6_SeXb4ngMMFsJoLdb4jTtLbT1DIntLcQqA,1568 -anthropic/types/beta/memory_stores/memory_version_retrieve_params.py,sha256=dulufntLcjyAD7sOjHyAhmz-vjVKjmuD853IqwVuyqk,746 -anthropic/types/beta/message_count_tokens_params.py,sha256=G6k8O6ABHCF60GxL26iig9bAW2z5PjSR8K5mUfYG3KA,11268 -anthropic/types/beta/message_create_params.py,sha256=WjyczvHQ6bfV-jJ98plTChW10QMKgRlob345voPv9gw,12904 -anthropic/types/beta/messages/__init__.py,sha256=6yumvCsY9IXU9jZW1yIrXXGAXzXpByx2Rlc8aWHdQKQ,1202 -anthropic/types/beta/messages/__pycache__/__init__.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/batch_create_params.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/batch_list_params.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/beta_deleted_message_batch.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/beta_message_batch.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/beta_message_batch_canceled_result.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/beta_message_batch_errored_result.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/beta_message_batch_expired_result.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/beta_message_batch_individual_response.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/beta_message_batch_request_counts.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/beta_message_batch_result.cpython-312.pyc,, -anthropic/types/beta/messages/__pycache__/beta_message_batch_succeeded_result.cpython-312.pyc,, -anthropic/types/beta/messages/batch_create_params.py,sha256=55Mhk9885QJAu0Uy-DZa5NBfSlpl_TbOClKFSGVvleo,1544 -anthropic/types/beta/messages/batch_list_params.py,sha256=_pVFBKhuHPJ3TqXiA9lWO_5W9bjVG291SRCc5BruLuY,978 -anthropic/types/beta/messages/beta_deleted_message_batch.py,sha256=fxnXySfpTxvxxpB0RPYXPcle6M17Bv4LCeMfDguCFaU,438 -anthropic/types/beta/messages/beta_message_batch.py,sha256=xvKuMyh5ozZWi9ZNQG7MChZ69rd7cWunUU1WhgMsJIo,2437 -anthropic/types/beta/messages/beta_message_batch_canceled_result.py,sha256=ZUHa9QvKPR70pTQ4X-yOgkc0OJnXKBapxeFnmf9ndLo,287 -anthropic/types/beta/messages/beta_message_batch_errored_result.py,sha256=3r02yXJd5eAc3IhJgLBqF1C-GvSx8siHWlJXFb8uOb8,367 -anthropic/types/beta/messages/beta_message_batch_expired_result.py,sha256=GuvILKoUDVK-mrOtzbnAnJft5ley6mrrpa4hpRRnkX4,284 -anthropic/types/beta/messages/beta_message_batch_individual_response.py,sha256=7rqc5Rr24AiUFFqbLxHiX9V3GL_KD3LZqTAm66MsWwQ,949 -anthropic/types/beta/messages/beta_message_batch_request_counts.py,sha256=mVj3pgtfgLdOIaMgbPXF8zeh99QuQyPox89T-8g5wWQ,1003 -anthropic/types/beta/messages/beta_message_batch_result.py,sha256=aq-LfNiuRCBg9ZYloNUXRfQEEFJJE7LivWpXyZGIpyg,819 -anthropic/types/beta/messages/beta_message_batch_succeeded_result.py,sha256=y4apNvDRTbJ_ldkpM4tWikiw1o0gROnrITZ0d7Qozrg,355 -anthropic/types/beta/model_list_params.py,sha256=CqxSV6PeWqZOh9D9D1qsJeC6fsWLFQmvY1Q8G1q4Gzo,976 -anthropic/types/beta/parsed_beta_message.py,sha256=-tUMa2w4GclrdJY3qu0mbpJMfC_CRTX-IuNVRhl1Jns,2548 -anthropic/types/beta/session_create_params.py,sha256=Fsw3_VB1iSsMLfoUBBwP6wTJdYid68qp8_w3qhq2KGw,2065 -anthropic/types/beta/session_list_params.py,sha256=gupWIaYV2VCmHqqWkD_WA0S1NNFmwsJdRrnpO0hblkc,1853 -anthropic/types/beta/session_update_params.py,sha256=-wNPJUbQ031Ao9Rd5vDCfXZIvyHDjiV97SOF8DS-3Pw,1025 -anthropic/types/beta/sessions/__init__.py,sha256=O4r8E3zcCz0JYJBmxyazlztSy9sBi7kh6efEmSBSnfw,10190 -anthropic/types/beta/sessions/__pycache__/__init__.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_agent_custom_tool_use_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_agent_mcp_tool_result_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_agent_mcp_tool_use_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_agent_message_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_agent_thinking_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_agent_thread_context_compacted_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_agent_tool_result_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_agent_tool_use_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_base64_document_source.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_base64_document_source_param.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_base64_image_source.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_base64_image_source_param.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_billing_error.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_delete_session_resource.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_document_block.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_document_block_param.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_event_params.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_file_document_source.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_file_document_source_param.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_file_image_source.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_file_image_source_param.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_file_resource.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_github_repository_resource.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_image_block.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_image_block_param.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_mcp_authentication_failed_error.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_mcp_connection_failed_error.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_memory_store_resource.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_model_overloaded_error.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_model_rate_limited_error.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_model_request_failed_error.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_plain_text_document_source.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_plain_text_document_source_param.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_retry_status_exhausted.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_retry_status_retrying.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_retry_status_terminal.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_send_session_events.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_deleted_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_end_turn.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_error_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_requires_action.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_resource.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_retries_exhausted.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_status_idle_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_status_rescheduled_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_status_running_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_session_status_terminated_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_span_model_request_end_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_span_model_request_start_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_span_model_usage.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_stream_session_events.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_text_block.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_text_block_param.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_unknown_error.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_url_document_source.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_url_document_source_param.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_url_image_source.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_url_image_source_param.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_user_custom_tool_result_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_user_custom_tool_result_event_params.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_user_interrupt_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_user_interrupt_event_params.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_user_message_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_user_message_event_params.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_user_tool_confirmation_event.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/beta_managed_agents_user_tool_confirmation_event_params.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/event_list_params.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/event_send_params.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/resource_add_params.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/resource_list_params.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/resource_retrieve_response.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/resource_update_params.cpython-312.pyc,, -anthropic/types/beta/sessions/__pycache__/resource_update_response.cpython-312.pyc,, -anthropic/types/beta/sessions/beta_managed_agents_agent_custom_tool_use_event.py,sha256=V4scoGS7Qf7E727mRvV8AapY_HoKjD5vsbgQgWr_Kpk,806 -anthropic/types/beta/sessions/beta_managed_agents_agent_mcp_tool_result_event.py,sha256=-m1gKq9RdFCUPl5iGo-2oX-awb3ueslHMg5aYRVNW2s,1348 -anthropic/types/beta/sessions/beta_managed_agents_agent_mcp_tool_use_event.py,sha256=ZRNn6VOF-f9WgAnFoqSpU3978YEYJnaEKAA3zegqoOU,912 -anthropic/types/beta/sessions/beta_managed_agents_agent_message_event.py,sha256=p_HCX1htsvqKVMq-oJUXMfDJAt0FPkDu8UsEqt_zGd8,724 -anthropic/types/beta/sessions/beta_managed_agents_agent_thinking_event.py,sha256=oX5hFPfakb1lYTdeKGlUXr8omFYZmd2eq2FybfVdiSY,592 -anthropic/types/beta/sessions/beta_managed_agents_agent_thread_context_compacted_event.py,sha256=07yep_kSgr4Mid5fr7txzbLjWLfCgDXqe8LbgQa73JM,593 -anthropic/types/beta/sessions/beta_managed_agents_agent_tool_result_event.py,sha256=s7-HV2XFnj6eXaPwE0Ed6hp_LpBV1vHyOwbIeGDV0PU,1332 -anthropic/types/beta/sessions/beta_managed_agents_agent_tool_use_event.py,sha256=Ra6HXO-GoSw-9GjsTcn8uHd6aCx4bhZL8FoI7yZwZ18,814 -anthropic/types/beta/sessions/beta_managed_agents_base64_document_source.py,sha256=ayIEpBTMPrnuHtpp8u7fapm692g8mFIKeUhaECrvUOw,479 -anthropic/types/beta/sessions/beta_managed_agents_base64_document_source_param.py,sha256=1EPt914-5gNDQeRYGgood79MdnI_3RV_NqnBGyqnB1o,554 -anthropic/types/beta/sessions/beta_managed_agents_base64_image_source.py,sha256=W4YQYs_G84B1O0bHcWqXVR7TqZQ25U727RwL1zbTe_M,513 -anthropic/types/beta/sessions/beta_managed_agents_base64_image_source_param.py,sha256=c1TsNry5eSXLpabH0_AwmDyZ2QAw_hE6qbR0xLWNG8M,588 -anthropic/types/beta/sessions/beta_managed_agents_billing_error.py,sha256=dsAmfN7rWJeU3M6TK4p5wfgOYvvjXhe8YAtAMQXXhIc,1289 -anthropic/types/beta/sessions/beta_managed_agents_delete_session_resource.py,sha256=CbgQQeDFyKoaAlJXk37t7PzISc2HlRKHU2CE8nCVX-E,378 -anthropic/types/beta/sessions/beta_managed_agents_document_block.py,sha256=fEbEXtcoMxib544oAFIqvZcenTBpHwuVqiMraWy1SDM,1399 -anthropic/types/beta/sessions/beta_managed_agents_document_block_param.py,sha256=kU4diH00F4AOGpg2qoky59qLGZSmMu7KYGYjUG0Gado,1390 -anthropic/types/beta/sessions/beta_managed_agents_event_params.py,sha256=5tPJb6PCq8Ag5MpJYe4LfGM9cK4wnMJ2FLYf-2o_xjE,924 -anthropic/types/beta/sessions/beta_managed_agents_file_document_source.py,sha256=HAv0N65bUPB-B9fWn8UMMikNlK5DNsR3s8e8LjulUSE,398 -anthropic/types/beta/sessions/beta_managed_agents_file_document_source_param.py,sha256=YjTunbTafqUoY1GDgOkZE-MBaSpQmt6fXCrDKtBsw2M,463 -anthropic/types/beta/sessions/beta_managed_agents_file_image_source.py,sha256=K-30qIt6GBI8KzRGqRyQuzJUJS3rCdXXSsMHR2PseCY,389 -anthropic/types/beta/sessions/beta_managed_agents_file_image_source_param.py,sha256=EaM3wfaNhuwaiOni9nz5rzGbiVgN1AYUzoCLl5iaZcE,454 -anthropic/types/beta/sessions/beta_managed_agents_file_resource.py,sha256=CfzD_9qgSOYDwZinvVbPB4-I2jnfaiJKwBRZftDLZ0s,497 -anthropic/types/beta/sessions/beta_managed_agents_github_repository_resource.py,sha256=DSzlsPVovbP-qTaqTPe-CkdWH5wp_l6NJMyw8FLCQJ4,994 -anthropic/types/beta/sessions/beta_managed_agents_image_block.py,sha256=mMz4kc0HHXSnmL0tyLuoLvcgg3bjwLGrqWXsZmvVBCk,969 -anthropic/types/beta/sessions/beta_managed_agents_image_block_param.py,sha256=h7-BxaQxfbnoZjZY5vh77Pznl27y3_HKAsT1vtkKii4,984 -anthropic/types/beta/sessions/beta_managed_agents_mcp_authentication_failed_error.py,sha256=Bqfo57pr0tmTSPUnm7JFIvFkGjw9nr4oKdDURBesS_Q,1251 -anthropic/types/beta/sessions/beta_managed_agents_mcp_connection_failed_error.py,sha256=l0MXBCBGt5AQDCHTEYaLrK-WW3NfmDYP3g3EfDEbTVY,1231 -anthropic/types/beta/sessions/beta_managed_agents_memory_store_resource.py,sha256=mVdkYUeNZNH0i-NJnO_-CNN4I592Qei2OIWXrlN3qDM,1440 -anthropic/types/beta/sessions/beta_managed_agents_model_overloaded_error.py,sha256=cmNqCxaE-wYQt6B-YVMosEeSKboJGfFnAKySXJ-W2G4,1191 -anthropic/types/beta/sessions/beta_managed_agents_model_rate_limited_error.py,sha256=1E_zhiu3pyR6ISAGafT27hZ_LZfH1dGFovP4d_9U1ys,1139 -anthropic/types/beta/sessions/beta_managed_agents_model_request_failed_error.py,sha256=Wi-ILeCV9MoaDegdW8dfXrJA8OHCDfUjYnFaVSppUDc,1183 -anthropic/types/beta/sessions/beta_managed_agents_plain_text_document_source.py,sha256=zLdAbn_uLB2maJXmj4n1vZivVH3J2Iag0y3JDSPuy3I,494 -anthropic/types/beta/sessions/beta_managed_agents_plain_text_document_source_param.py,sha256=bDEUugyJGXX2V1guFrYZDIZZnHQBKP82dWe5f_EwFfs,569 -anthropic/types/beta/sessions/beta_managed_agents_retry_status_exhausted.py,sha256=PhRyS4yg7rOPAfjT5kQ6T2KlHjdlf_--sXXT5EvlCUo,431 -anthropic/types/beta/sessions/beta_managed_agents_retry_status_retrying.py,sha256=4E7wgSa62G5WWOi0OMrXsT-laYT7kFgZqnzNdGXeG1M,481 -anthropic/types/beta/sessions/beta_managed_agents_retry_status_terminal.py,sha256=UJfqog1nGQ3CCTBB6te1vHYz8HTuJsTywX6ASsuWYxc,404 -anthropic/types/beta/sessions/beta_managed_agents_send_session_events.py,sha256=h6hL-_qVl_YMgGvYzWs80JcWnk8HpmZNE-dhsfbhPIk,1154 -anthropic/types/beta/sessions/beta_managed_agents_session_deleted_event.py,sha256=zv7JPk9b8eBmAZQeUGozfnbL4FCYq1JqHBJgDJ_x7Sc,615 -anthropic/types/beta/sessions/beta_managed_agents_session_end_turn.py,sha256=xN2aYPh0tVNoWKmaTyG_JKMTY39yO0m3O-sqCM2PfLY,379 -anthropic/types/beta/sessions/beta_managed_agents_session_error_event.py,sha256=FTJmNt2rL9X243CWZd2R1pRacVTi3xGi0kln_XTCj5A,1933 -anthropic/types/beta/sessions/beta_managed_agents_session_event.py,sha256=KABD-UTNBBfLf0S8FJWhX7v8CmrzPUQdnCHg4W8DOjI,3333 -anthropic/types/beta/sessions/beta_managed_agents_session_requires_action.py,sha256=X6M6cRTJrfnuJK5IYc8rmGd8cWf5oa-bo8wYTQsH5uU,698 -anthropic/types/beta/sessions/beta_managed_agents_session_resource.py,sha256=DyDpx2osLdyRTMyYiz39lJcIiezbK8hy_--TwyA7xOk,756 -anthropic/types/beta/sessions/beta_managed_agents_session_retries_exhausted.py,sha256=M0lhZRu0CFjKzVYx57Hl4q2WUU8ImCF_DnBruCok9rU,468 -anthropic/types/beta/sessions/beta_managed_agents_session_status_idle_event.py,sha256=2dUW9p00Xn4rzJHdkcnKWgPrZ2NutO0hsSRZPjUirY4,1270 -anthropic/types/beta/sessions/beta_managed_agents_session_status_rescheduled_event.py,sha256=nzUdKnRtQcfVl8t0Y-K0YUi48nVEjakKkpdvwNrbxF4,604 -anthropic/types/beta/sessions/beta_managed_agents_session_status_running_event.py,sha256=oXtpETVDy-jLQ1j8p-6138FWuu-QtB98_itIr21D3_Y,560 -anthropic/types/beta/sessions/beta_managed_agents_session_status_terminated_event.py,sha256=CuGNDx6o3V2Z21AsNVhGJHEXU8XDhkL2T5VLqIJT9Is,577 -anthropic/types/beta/sessions/beta_managed_agents_span_model_request_end_event.py,sha256=-10Gdcpz5m0O7gGCX1kaUI6wbAAIkjrwaGmYb0o_YxY,1123 -anthropic/types/beta/sessions/beta_managed_agents_span_model_request_start_event.py,sha256=69HGWqX_s58-6K8rHjoASz1apwle3R7sHKiB-Agg2l4,552 -anthropic/types/beta/sessions/beta_managed_agents_span_model_usage.py,sha256=FJA71Dp89LCidpbaAun-Moj1tPqQZhkBi6pw85Hewq8,940 -anthropic/types/beta/sessions/beta_managed_agents_stream_session_events.py,sha256=JbQWb_uqzX-BFR5OoHpPU357OOhiFFeIDS4OoKZANPk,3347 -anthropic/types/beta/sessions/beta_managed_agents_text_block.py,sha256=igJPRB2DvgUiPLbYbr3MC3pPwdrEgimiv2zZL0u8I2U,351 -anthropic/types/beta/sessions/beta_managed_agents_text_block_param.py,sha256=DWiR0aRh7UmvCezda4fw1efnA_NRGSNFIPFmpnlYcA4,416 -anthropic/types/beta/sessions/beta_managed_agents_unknown_error.py,sha256=iwvDpuKdjg30LNrylZpGn7UNwKP1_DVBn4p5kjHoG-M,1265 -anthropic/types/beta/sessions/beta_managed_agents_url_document_source.py,sha256=Gj0wqQqRsTJqi7m9uID9gwA05EOiDUwqMsI4eFjbS34,383 -anthropic/types/beta/sessions/beta_managed_agents_url_document_source_param.py,sha256=6ijCHCB5nGpFZ36CzDnDiS0HGddyYt6uC7rvbMiV7OM,448 -anthropic/types/beta/sessions/beta_managed_agents_url_image_source.py,sha256=dYhdSMueXCByhUCDT0dP189vlLIl6HMFI-by5utpkG0,371 -anthropic/types/beta/sessions/beta_managed_agents_url_image_source_param.py,sha256=oo0QFfoXxoTMJrGqXMQ4Rv5JA7ZnkvAOLyHL_ZVeKTc,436 -anthropic/types/beta/sessions/beta_managed_agents_user_custom_tool_result_event.py,sha256=rr99IYAhJvyFLa9UA9velbM0yE6Tff0HwX-ccBCqEVc,1632 -anthropic/types/beta/sessions/beta_managed_agents_user_custom_tool_result_event_params.py,sha256=ICnvc8knwTvhopBzY0jNLN0SWXkJ9dX8bTiGSF-zMxQ,1450 -anthropic/types/beta/sessions/beta_managed_agents_user_interrupt_event.py,sha256=GAA9tchleZ2TTY_smianDIXXZQNr9x-Yv22rFiVdKCw,595 -anthropic/types/beta/sessions/beta_managed_agents_user_interrupt_event_params.py,sha256=aRNQCqBkPH35sHp1VkN1Dx2TJlKmkORDUGhTxOAaAVU,427 -anthropic/types/beta/sessions/beta_managed_agents_user_message_event.py,sha256=7pF2FmQUVQ3nHCsd-EbqebzlPrdvJ0B8onyidjZIcPI,1130 -anthropic/types/beta/sessions/beta_managed_agents_user_message_event_params.py,sha256=hFO6Ji2CrLgfhTY8GLQGq4md2aM8jPkUluFL9RrRxNE,969 -anthropic/types/beta/sessions/beta_managed_agents_user_tool_confirmation_event.py,sha256=kM300LSu5sc26Xxy6R8X4MgZTRmdOZv1EzFZ_Xn88XY,1205 -anthropic/types/beta/sessions/beta_managed_agents_user_tool_confirmation_event_params.py,sha256=SEOfXRSLdXRk5r0U1uJ68pzYmldE8Z-6PGavvtP5ODw,1089 -anthropic/types/beta/sessions/event_list_params.py,sha256=RPquQ22M4cAwtjzEDLDAltas86G3SK1sBp_1QeViQt8,817 -anthropic/types/beta/sessions/event_send_params.py,sha256=Hf16pIX-dfBh6z-JdB829WQIXL0xwlfVXrAkv1XM8io,729 -anthropic/types/beta/sessions/resource_add_params.py,sha256=w9YXayj4amDgZvRuX6xxj1cHlU4lkYgxWBB-6qbMySA,787 -anthropic/types/beta/sessions/resource_list_params.py,sha256=2ggH20cmGOc_d5uMMVrQzhkdi6iHw2ASALH6B_Tjz2U,748 -anthropic/types/beta/sessions/resource_retrieve_response.py,sha256=di8wnULyrijLQoNuHW0s3bxJB0PFnC4tlfQwJvv80_8,740 -anthropic/types/beta/sessions/resource_update_params.py,sha256=3HANzJ-m5mGlTv4PO9wonPqDwmg4QhZ04VOwLSffHUU,751 -anthropic/types/beta/sessions/resource_update_response.py,sha256=aEX-NYDDuX-GC4ziHsK7iY-pwn3BokOLcrr14K3x42c,736 -anthropic/types/beta/skill_create_params.py,sha256=5oyHKyq_Fll_J4PwvxLnI7Jn-ThqfKISSlVClK3AIgQ,978 -anthropic/types/beta/skill_create_response.py,sha256=d6hnEKxiUUvD8chx0RCe0Mm6G-HHCRDz6RDIljc8J24,1161 -anthropic/types/beta/skill_delete_response.py,sha256=6_8iQ8ufvbzoxrrRo2wDC5QPgcdgzMyLo-EhlArNNZw,413 -anthropic/types/beta/skill_list_params.py,sha256=BEzzX3nWpA-EAKx8UIm0_vjcJzDRxWO6o0bkHXuMCS0,1099 -anthropic/types/beta/skill_list_response.py,sha256=jImI_kPHYQ8LHtlzkwD9qjBJHAHps8oLzEJzsarg7nM,1157 -anthropic/types/beta/skill_retrieve_response.py,sha256=MFbkjMKP3a0H38tpsZ4bWZZYLue6eNstgezFJWROROI,1165 -anthropic/types/beta/skills/__init__.py,sha256=O7NO7FhQ3882R0q8UthBFi8KlB9Owg93jjdA9ntTGgg,609 -anthropic/types/beta/skills/__pycache__/__init__.cpython-312.pyc,, -anthropic/types/beta/skills/__pycache__/version_create_params.cpython-312.pyc,, -anthropic/types/beta/skills/__pycache__/version_create_response.cpython-312.pyc,, -anthropic/types/beta/skills/__pycache__/version_delete_response.cpython-312.pyc,, -anthropic/types/beta/skills/__pycache__/version_list_params.cpython-312.pyc,, -anthropic/types/beta/skills/__pycache__/version_list_response.cpython-312.pyc,, -anthropic/types/beta/skills/__pycache__/version_retrieve_response.cpython-312.pyc,, -anthropic/types/beta/skills/version_create_params.py,sha256=5608lJ5M0r5fuvRYqndL-8Q6cJWaEs2xQUoGeK-msA8,813 -anthropic/types/beta/skills/version_create_response.py,sha256=h_88pBqDHL-SYefLfx-ZA9i0RVWylO3E6uFZjSrWb8I,1187 -anthropic/types/beta/skills/version_delete_response.py,sha256=_sJ892AuqEtAZ6g5w_r7AmA8N07FfhnLYM4v-jFNXtM,465 -anthropic/types/beta/skills/version_list_params.py,sha256=7ybJm6AoOfrRE22qEF3GaBdDvkpTal1DoywiMN0y8QM,773 -anthropic/types/beta/skills/version_list_response.py,sha256=S2ifcSXltFr4yJzhCEtuQAQtQ0sMycTuv1djhRId5n4,1183 -anthropic/types/beta/skills/version_retrieve_response.py,sha256=1cfKkqynLN5Cy2RpUXdI-tlSZT3NK4e-xIUd4jUwEEo,1191 -anthropic/types/beta/user_profile_create_params.py,sha256=KgC0qD4nSgx4anG9sCrdNYBfWNVnhFIohUIycV8m6-o,922 -anthropic/types/beta/user_profile_list_params.py,sha256=dfwjhCpyDW5trYYCsTjbbcSoi_YWotnz1fkEBWn4TaA,721 -anthropic/types/beta/user_profile_update_params.py,sha256=baY4ldtAH4c1Jg-yD6yIYPB9vReelHhm23FEbDCxP3Q,1024 -anthropic/types/beta/vault_create_params.py,sha256=va8ObhChLAqWKMpkecIEixslnoXR3b-kZ7dyfWKJCMc,809 -anthropic/types/beta/vault_list_params.py,sha256=nBqzNuX2Dq6xo-YopR8d-uFsJj-F_oyqOx8DWcY9f-w,805 -anthropic/types/beta/vault_update_params.py,sha256=8wZXSTikP9HAffGly8D1hLiGXQyBLzsxZJQrErsw25U,830 -anthropic/types/beta/vaults/__init__.py,sha256=S8EkGxp1hXOHi7YLkjpxkhjRfpZKzIZPeMskwgdTZr4,3376 -anthropic/types/beta/vaults/__pycache__/__init__.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_credential.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_deleted_credential.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_mcp_oauth_auth_response.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_mcp_oauth_create_params.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_mcp_oauth_refresh_params.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_mcp_oauth_refresh_response.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_mcp_oauth_refresh_update_params.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_mcp_oauth_update_params.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_static_bearer_auth_response.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_static_bearer_create_params.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_static_bearer_update_params.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_token_endpoint_auth_basic_param.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_token_endpoint_auth_basic_response.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_token_endpoint_auth_basic_update_param.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_token_endpoint_auth_none_param.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_token_endpoint_auth_none_response.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_token_endpoint_auth_post_param.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_token_endpoint_auth_post_response.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/beta_managed_agents_token_endpoint_auth_post_update_param.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/credential_create_params.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/credential_list_params.cpython-312.pyc,, -anthropic/types/beta/vaults/__pycache__/credential_update_params.cpython-312.pyc,, -anthropic/types/beta/vaults/beta_managed_agents_credential.py,sha256=2QdAeeTbhUBuX4GZR1ODfHiyzBszhKgfwaxMi8GTk_w,1507 -anthropic/types/beta/vaults/beta_managed_agents_deleted_credential.py,sha256=av9iwnkkME2mTQFG0aOs0mlcCO9w5_-ERn2KX1Gky1o,428 -anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_auth_response.py,sha256=Mhy6Ec82HoWi5splck2k3bMVEYHvNEHbuvRBKox6VaA,846 -anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_create_params.py,sha256=dmuzPOttH2WkEcGIivtXL8ff1zr0KQBM13ccJMMh1FQ,1066 -anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_params.py,sha256=AImka_CytnI0RjiDeTp2cF27fmd4l1pUw0YVG_CDbaE,1443 -anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_response.py,sha256=ApaE0-nLNnVzvjcDJNzhIGfRgrUPUVlHVEDYl-K9IzM,1467 -anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_update_params.py,sha256=K1X6LXBbIbqdyTTeVL6q-tCmYenAIymKPrccNj5b1jg,1115 -anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_update_params.py,sha256=ZfYS9eg-xzNwcc_j5nvxYR9n20CDjCLiWZuZRa5H1YE,1012 -anthropic/types/beta/vaults/beta_managed_agents_static_bearer_auth_response.py,sha256=uZMKZEqiDIB7kzXn3JAnHQsyH0ITODT_nHiohtQDlfo,479 -anthropic/types/beta/vaults/beta_managed_agents_static_bearer_create_params.py,sha256=c_KAH1EhPZTte_HjxNrAY-aJbrLuspQGce-R1NoIT4E,597 -anthropic/types/beta/vaults/beta_managed_agents_static_bearer_update_params.py,sha256=c24mhKU04WUZmuKlexH0GRl0AQtg2Q76UKe2lHAhYXI,572 -anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_param.py,sha256=X7Q3wgyh9RJS044bd4nIXOEi5kOc3C4U6iPqrPQKixE,518 -anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_response.py,sha256=YBCi9L6T9qe1guMx4KJvwFjh9oOIELQaglcEZHFDJas,414 -anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_update_param.py,sha256=2ihewRaNFGO622usCJTUVYAIghbUy6lLehQgMYpEuQU,564 -anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_none_param.py,sha256=ac0Udj-OMBoi8aT5iJ0YH_1Zl63rJ364ela5LioPrDs,415 -anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_none_response.py,sha256=Bnue2DI2hbJowdRdvUvG4VwsfpCCDZwSQNsDCzBfoqw,376 -anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_param.py,sha256=jZ_spYKRCI6pWzSa5OhW1aRW3INF8fxRzsx0OeWkYco,514 -anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_response.py,sha256=Qrvw48WfT7r4XpmugLevVZQLhFr_BMJPTwBUyWZqecs,410 -anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_update_param.py,sha256=8h9133-f6mdsCQ66Z687SiUqc11jxl9ZfpRjq-xvFDE,560 -anthropic/types/beta/vaults/credential_create_params.py,sha256=mbDds4VwbVoxtjz8fhCro5r7n9e1zXSAuHEQAwPtMTQ,1263 -anthropic/types/beta/vaults/credential_list_params.py,sha256=VTwDw8oNcwsCa7v2S3Lgz8jWlcspygD5TQMpzDTNRDM,832 -anthropic/types/beta/vaults/credential_update_params.py,sha256=D1O5TZpGoLiCDMSBiqanwTyfqWl2yrzEsJdYmzem7X8,1293 -anthropic/types/beta_api_error.py,sha256=rr_VBxFp9VqNmVjTUokYzpkYRYvO9MVh_t406BvGi38,268 -anthropic/types/beta_authentication_error.py,sha256=3nxjZjiGWwxXzvbPVlShjk0x7-EMgvryJsZvprVID8A,301 -anthropic/types/beta_billing_error.py,sha256=6lg7924RmfVKxQymCZBjIWswsvMgAbmNpbRxV2I6r3c,280 -anthropic/types/beta_error.py,sha256=u7ppFd0RXvk0Ol7gU4kwKU_NTJXxl8cVY8xHAMozCvM,1075 -anthropic/types/beta_error_response.py,sha256=o5llWy_eOsp38lEQDIpZENvxsOPTMNyYVW1MjtQ-gZI,378 -anthropic/types/beta_gateway_timeout_error.py,sha256=Je01xyEyAT6Ol4GOD9TyOn26oIkILcWs0_xf4AjjqFE,294 -anthropic/types/beta_invalid_request_error.py,sha256=aT_hyszZwfj02rhdnqL9LcnPe1if-RqgwmsqMO8ML2Q,302 -anthropic/types/beta_not_found_error.py,sha256=Oyc2bXxB1n_q1wm9ejJHY-TBCIdNL-Sl8-yilT61b_0,284 -anthropic/types/beta_overloaded_error.py,sha256=TPBl-7AuTOj0i2IcB8l8OAYBsJE-WjxzyKGlKh0eeeI,289 -anthropic/types/beta_permission_error.py,sha256=OU90hnoOaVLxiP_dwYbROdt25QhSZjuhKbVdTNx3uAM,289 -anthropic/types/beta_rate_limit_error.py,sha256=-I0edM31ytNCWnO5ozYqgyzC92U7PfJbFvaACSEP7zs,287 -anthropic/types/cache_control_ephemeral_param.py,sha256=q03wMTU8_TtKBXTlVJH6N36yIPmv4iRblwgvlZ0LLBA,529 -anthropic/types/cache_creation.py,sha256=Br9XkoKHTr-2DpKCOBkDAzf5bPxQiYBCemHZvXRPY9M,407 -anthropic/types/capability_support.py,sha256=NolyrqXOvudZGhwNyV9JHC3X99hoGT9uc185-e9xhio,327 -anthropic/types/citation_char_location.py,sha256=1PmYQ4NkEgmhJPOv6m7XhcXtd0myp-gHvgtyQ0Uws-s,473 -anthropic/types/citation_char_location_param.py,sha256=9tk6PgA-ktMZ21A1PeWgidXQjaW7cIE2ETKFGWc-6tE,538 -anthropic/types/citation_content_block_location.py,sha256=wF2H_nZcZ7XVlc2n6ZzTsdxuh55h6lUIVEI38SXWGgw,500 -anthropic/types/citation_content_block_location_param.py,sha256=OWwJS3K9rPjwVXX3zic9O0SfIpGbi6268oGiZmcghrE,565 -anthropic/types/citation_page_location.py,sha256=ZrdI5X-bkcHUfTVkugX1vaLsGC_N9H6UQNTkUcii7Io,475 -anthropic/types/citation_page_location_param.py,sha256=HaGbc5OyeI0qNk9PYzwx_xGZwuoQpJ_NvwbkRXBGcTo,540 -anthropic/types/citation_search_result_location_param.py,sha256=VwuJbt_Q-O5igKvt9VdldzD5-fvGJwTIDjHt8HNsNEQ,588 -anthropic/types/citation_web_search_result_location_param.py,sha256=L_49nL2-OQ7jv0ihuaZlGpTwlsHl7JFKQj2XyVvun0s,517 -anthropic/types/citations_config.py,sha256=LSRIEg-jbqK5U_Mxle-IBr2L8dcit6qQJUYxRnvhL0I,204 -anthropic/types/citations_config_param.py,sha256=QaqfWOS568Iv0LOlwnswhCUXF8JtS-AjGsz_fGJKmpI,271 -anthropic/types/citations_delta.py,sha256=1lnPGh4nfooE90rqBRNonwGvHPuyzF7-Rs76LHeHbgQ,997 -anthropic/types/citations_search_result_location.py,sha256=9UN4QljowQ9p3NVWHiGn_vuh1BTrqQhNU3ijiZ9Atms,480 -anthropic/types/citations_web_search_result_location.py,sha256=rxbcJmhqPa394V5253XDKWtphNklZq44RsKhs8_d_xg,429 -anthropic/types/code_execution_output_block.py,sha256=TjuwJU8J-TFCw4-D4KNCaOkrDvbMN_bfSysP-jeToj4,304 -anthropic/types/code_execution_output_block_param.py,sha256=BQc8eVsXGN5JzIvJ7Yj2kLm8wD7yx3D4RNzaWRbWSjs,371 -anthropic/types/code_execution_result_block.py,sha256=SoYIrq0wMcHGB5XYKxKLlN2UmVRFjqDKfVDulxD4o98,477 -anthropic/types/code_execution_result_block_param.py,sha256=CJVyaaNAZO-1WVqyZzTpU2ZsjcmVHW29J2q-QGkFevE,599 -anthropic/types/code_execution_tool_20250522_param.py,sha256=a99mmZLSZjJZFYnpaRq6FxhZUTPSBIgicKYgv4MX_2U,1098 -anthropic/types/code_execution_tool_20250825_param.py,sha256=nFydrVmQ95V4Tk5Z5FoK8Oier82C72ckClIZFVtoElA,1098 -anthropic/types/code_execution_tool_20260120_param.py,sha256=5BkuKBRYJOMo1EtB1eaA2I2ICd0webLCWmjDpRsTbik,1202 -anthropic/types/code_execution_tool_result_block.py,sha256=eMZKrR1KpTrKNmvpSH-YIUAD0dyc4btEXaaeadmlqqU,545 -anthropic/types/code_execution_tool_result_block_content.py,sha256=wlzNCZ9ahbSRX4l8gfy2FRBXqQYq8M7yTwNvojS2u-Y,583 -anthropic/types/code_execution_tool_result_block_param.py,sha256=RO2Z59y_SFPW6AWPdcHO7iMeZQgEHKO4QVIBqvFP3OA,877 -anthropic/types/code_execution_tool_result_block_param_content_param.py,sha256=4z-DmAzrScXMJK3c9IUjQZWRAOOHUwiRcgZVma-9BBs,687 -anthropic/types/code_execution_tool_result_error.py,sha256=TI4P_z9kZ4-k8lPjGFxOIHPjNND8uXV1PQuv1FMhIZk,439 -anthropic/types/code_execution_tool_result_error_code.py,sha256=ljXyJD-KKRQ2bmd4NW4sopxHfzcQHDkdKdSRi96jX2M,330 -anthropic/types/code_execution_tool_result_error_param.py,sha256=nExickszW4yj0KvOYgSgQVYyxVi_rrnUbwfLpD1maBY,507 -anthropic/types/completion.py,sha256=rwyZeILWQMjzTaYA7wNOJFYQrTobiGt5gsxIpD7ejdI,1151 -anthropic/types/completion_create_params.py,sha256=lc_SuNi0a6KJ13ZayJJNVIqGIEdEEs7SZLVhmirApys,4675 -anthropic/types/container.py,sha256=fjJJ1bpijdBMH3avW9mkuRVDiF58MK9Jl4Qoj4fqKwM,461 -anthropic/types/container_upload_block.py,sha256=4PH0_VavMDJyvK_kUQsBdHf776TNyMg9r9FV_sUTtFo,355 -anthropic/types/container_upload_block_param.py,sha256=ioiMELnF_yylkUIz_Db5Otz_arfQ2Ky5vKAjBY73GWE,761 -anthropic/types/content_block.py,sha256=ea7fwZA9Fgh8qllCc3GXtES2paRLc9zDdsX17UzKWyY,1451 -anthropic/types/content_block_delta_event.py,sha256=Y1wLLQioHYK25FeFYMHv0ya2MrOw26iFSksZwnK9eHs,310 -anthropic/types/content_block_param.py,sha256=_yTwyeXQdmygXTE3xlyavsMc3-ev1E594SOTguYDUJg,1853 -anthropic/types/content_block_source_content_param.py,sha256=S7jYbHw_FhL4c1pNW-NEdXpIek7tSk1V812OYpaZuUE,411 -anthropic/types/content_block_source_param.py,sha256=Qs-wmu5DTlECLIuyTi-Ih8YrMQtyu43rKAZV1WD6--8,509 -anthropic/types/content_block_start_event.py,sha256=KIKjsrqrkrOzOlZgjbWS24Ceo2_8-5yS8WtUxtDoEbw,310 -anthropic/types/content_block_stop_event.py,sha256=JLfjHeVxDa9m1R1Pp3pjSjTLiaA6MHBi0tvyQFnfgDw,304 -anthropic/types/context_management_capability.py,sha256=50dNpY9HPRrKRkah5lo9wNqnKyyD5IFhMEFj1v88WNc,774 -anthropic/types/direct_caller.py,sha256=0sfIjYRj2L9Ds4yQxpPKDzTKmo1HCGADJWPKOCfpVcE,299 -anthropic/types/direct_caller_param.py,sha256=UZS2IABj1wwKqc_33s10huC9QVMMu9dkhdo5sfDsk6Y,356 -anthropic/types/document_block.py,sha256=_KTLaW4gCoPCE6EHk22uCjAe7QhjpK5ZsbHVnL6AWhQ,788 -anthropic/types/document_block_param.py,sha256=ATGjDsb0s4A3ExJaljj5kCQh9utjygv1IKy6grGwUcM,1094 -anthropic/types/effort_capability.py,sha256=7LwpzSV1pHBTfDb0rzE7objSAYoXfIPwLBbqnqEK7Iw,847 -anthropic/types/encrypted_code_execution_result_block.py,sha256=BkmGPkQL7-SsHMcR5_ZAx4iyOfeN92d9eeZQ8tGK95Y,600 -anthropic/types/encrypted_code_execution_result_block_param.py,sha256=7vp1HND3KEGMTBtq9ym3SdW7Ky_03X6Q45TezYkeqO4,722 -anthropic/types/image_block_param.py,sha256=qIh7kE3IyA4wrd4KNhmFmpv2fpOeJr1Dp-WNJLjQVx0,770 -anthropic/types/input_json_delta.py,sha256=s-DsbG4jVex1nYxAXNOeraCqGpbRidCbRqBR_Th2YYI,336 -anthropic/types/json_output_format_param.py,sha256=OXADdQgxn9OzpiE16hm3neXUXTPMmPbw0uxwDO9aY2E,422 -anthropic/types/memory_tool_20250818_param.py,sha256=aCsOeAPSyNE7P4-IO6w2Az5wB-ta2HcIVAuxeTDd3-U,1133 -anthropic/types/message.py,sha256=k9cYUJezppPPlIvMhzW2wFqdVE6vjyAo6o5yopfqZ40,3870 -anthropic/types/message_count_tokens_params.py,sha256=I4HAibJq7L4fRAPh1IbERmLMUY605c9YOefzFVhLLSw,7197 -anthropic/types/message_count_tokens_tool_param.py,sha256=-rIG5uNjnaQxdwtw_T00HcPhqDbRnrsNF8Zf0O9XIcc,1924 -anthropic/types/message_create_params.py,sha256=fukGVKlT05Vh-aMrK3CPZZGVgnwvdvjFv_DSh-H3CYI,11587 -anthropic/types/message_delta_event.py,sha256=YXDoFicieByN-ur1L0kLMlBoLJEhQwYjD-wRUgbTiXM,279 -anthropic/types/message_delta_usage.py,sha256=xckWsOsyF2QXRuJTfMKrlkPLohMsOc0lyMFFpmD8Sws,816 -anthropic/types/message_param.py,sha256=yZw1y6Jv_TCtAaO1Qm0ZDc2tlSYjEtWIfXYEChltBdk,2411 -anthropic/types/message_start_event.py,sha256=ZTGWYmtAKcXWgYovM09IutHGiF8__Ol9x2XMkivzVaM,279 -anthropic/types/message_stop_event.py,sha256=rtYh1F-b9xilu8s_RdaHijP7kf3om6FvK9cXP-MJo68,273 -anthropic/types/message_stream_event.py,sha256=OspCo1IFpItyJDr4Ta16o8DQmTsgVWSmeNg4BhfMM0M,285 -anthropic/types/message_tokens_count.py,sha256=JmkcWw9nZAUgr2WY5G4Mwqs2jcnMuZXh920MlUkvY70,329 -anthropic/types/messages/__init__.py,sha256=rL0U5ew9nqZzJRMked2CdI-UVIauM0cAx8O9a2RF5qo,1076 -anthropic/types/messages/__pycache__/__init__.cpython-312.pyc,, -anthropic/types/messages/__pycache__/batch_create_params.cpython-312.pyc,, -anthropic/types/messages/__pycache__/batch_list_params.cpython-312.pyc,, -anthropic/types/messages/__pycache__/deleted_message_batch.cpython-312.pyc,, -anthropic/types/messages/__pycache__/message_batch.cpython-312.pyc,, -anthropic/types/messages/__pycache__/message_batch_canceled_result.cpython-312.pyc,, -anthropic/types/messages/__pycache__/message_batch_errored_result.cpython-312.pyc,, -anthropic/types/messages/__pycache__/message_batch_expired_result.cpython-312.pyc,, -anthropic/types/messages/__pycache__/message_batch_individual_response.cpython-312.pyc,, -anthropic/types/messages/__pycache__/message_batch_request_counts.cpython-312.pyc,, -anthropic/types/messages/__pycache__/message_batch_result.cpython-312.pyc,, -anthropic/types/messages/__pycache__/message_batch_succeeded_result.cpython-312.pyc,, -anthropic/types/messages/batch_create_params.py,sha256=4kLtO6FnAY_VvibgcCHLBSKmW2uZjDyLAANL1MbIlYw,1092 -anthropic/types/messages/batch_list_params.py,sha256=uuyRsq3a2qb89vESjKuvz7l6bkVewfQSJsVzWp8lKrI,691 -anthropic/types/messages/deleted_message_batch.py,sha256=f5CDJzj4UEsRAy9SkYivpMuz-E5lpfoLHTl8mLeThAg,429 -anthropic/types/messages/message_batch.py,sha256=2Oxp1wiOkp22w_UvIkBL4cgwH-4IkZcAx7MpN-ycYGg,2415 -anthropic/types/messages/message_batch_canceled_result.py,sha256=u2VevMap02v0B1fgXs8bhiBoc8obE2AWbKV7qd0vto0,278 -anthropic/types/messages/message_batch_errored_result.py,sha256=VnxtXDxONJTxZbCvl_8DefG-yR1pNLSIikZAfPac30A,351 -anthropic/types/messages/message_batch_expired_result.py,sha256=zntExk51haoLk2gGldTCCuhWJw8j-xv5DxOnx6GDyn4,275 -anthropic/types/messages/message_batch_individual_response.py,sha256=CVCqRkKP4_RJhAnaQ4yJtmt5SzIroDzy-Cn4vMPflF0,927 -anthropic/types/messages/message_batch_request_counts.py,sha256=KL64Dp8ISD5KwxryYGzDR9xg4m6Ovm-6okaXHWRPcNA,994 -anthropic/types/messages/message_batch_result.py,sha256=VdNDHse9-8i5ogM2Si4Yp3cc73rMGlfDLJzNYdWbEAU,733 -anthropic/types/messages/message_batch_succeeded_result.py,sha256=k1ruBaFzaT6dnUxuelLpuFSOC__1EZ6Nni1sPHHeUUU,333 -anthropic/types/metadata_param.py,sha256=p6j8bWh3FfI3PB-vJjU4JhRukP2NZdrcE2gQixw5zgw,594 -anthropic/types/model.py,sha256=hYl8n9t0eCTl5x932x2B6NT9Ll9-UxZc5nYyshiE1iA,775 -anthropic/types/model_capabilities.py,sha256=ZyJFM1jFTzLomxCQh_8IsS-5b8CDjteDh3m04zXcJRE,1351 -anthropic/types/model_info.py,sha256=bl0dOSbj_aX-E_psxY2O5UHRSmaN2IVYLEEUV5YwqlQ,1048 -anthropic/types/model_list_params.py,sha256=O2GJOAHr6pB7yGAJhLjcwsDJ8ACtE1GrOrI2JDkj0w8,974 -anthropic/types/model_param.py,sha256=rpKZxZpA8rmKgKitsaoAf1K34tCJCwBIfUBuA5we8jE,821 -anthropic/types/output_config_param.py,sha256=_gwHqt_Kkd4fnOPxQkKMG_X8u6uoUXT6TqWi3XtVdlc,679 -anthropic/types/parsed_message.py,sha256=oMP1kUgumiHEOXAy8bQzyHppOEDn4coJuCMSGOFcOiE,1632 -anthropic/types/plain_text_source.py,sha256=8lnx3wad3F8rWlw_zVJ7SA3h5Sx01yfYiIZXlC7APxo,305 -anthropic/types/plain_text_source_param.py,sha256=zdzLMfSQZH2_9Z8ssVc5hLG1w_AuFZ2Z3E17lEntAzg,382 -anthropic/types/raw_content_block_delta.py,sha256=T1i1gSGq9u9obYbxgXYAwux-WIRqSRWJW9tBjBDXoP8,611 -anthropic/types/raw_content_block_delta_event.py,sha256=XKpY_cCljZ6NFtVCt5R38imPbnZAbFyQVIB5d4K4ZgY,393 -anthropic/types/raw_content_block_start_event.py,sha256=Lnly2xKEkBzaUBdNswXYXJyOyIHRUYfhmKaD0BbLlSU,1720 -anthropic/types/raw_content_block_stop_event.py,sha256=_W-iWfHT1EBHaSi8VEL86HX61NSKmqDwEDay6DA8BJA,299 -anthropic/types/raw_message_delta_event.py,sha256=FnJefiJ9-Ma5Gzu_0G1iwIIxci4-X3Jc84-P4a4KQ9Q,1615 -anthropic/types/raw_message_start_event.py,sha256=S1NNGKlkhm82tDpCaIIm71p0kOK8Cw8IDh2Aj0WTRFA,321 -anthropic/types/raw_message_stop_event.py,sha256=JyudS9wnL0c2dG913QDDuenIaRGjXEmHocqbyboK5sA,267 -anthropic/types/raw_message_stream_event.py,sha256=fazzMhSf9xLVLXHQu62f7gRHyBiWfTWkeavd0G-CcrU,912 -anthropic/types/redacted_thinking_block.py,sha256=rRoc3AUPGUaYywZ29cLkZ7oGvaAj69vlSIZipr_ZqcQ,291 -anthropic/types/redacted_thinking_block_param.py,sha256=x00GNJXOnAYLPqWMrkRDcHveOJEvrU4iAaTP1rmNqBU,358 -anthropic/types/refusal_stop_details.py,sha256=zbJeZvdkTBIktONOaTBgDNHOUXXyXYaCuMDG8u0Cy3c,726 -anthropic/types/search_result_block_param.py,sha256=89JZzDqAAZcQZFu6Yy1jNqXK0Px64RGg8wyg6mN0Pfs,795 -anthropic/types/server_tool_caller.py,sha256=CGn-jrn0hFnTyAX6tdwS9r7IVdl5xLXg_cecTxWGiBI,350 -anthropic/types/server_tool_caller_20260120.py,sha256=NWhwlN32Fv3Z6Tz2_M9NGZDywCczI9hlgqWeLLM4JOQ,306 -anthropic/types/server_tool_caller_20260120_param.py,sha256=v6hIrB0YsCaxJLPDNpVX9uWvPPZ-GqnZKOqTUoll-H8,373 -anthropic/types/server_tool_caller_param.py,sha256=_tDCa7uVHTx8xKjE8G-G87PP6Z0dZ7gt__yyyC4vlCg,417 -anthropic/types/server_tool_usage.py,sha256=AwJivtH0UQINkBQl5D_GNuEICzpXaYtK4MZ2a_aTO9s,343 -anthropic/types/server_tool_use_block.py,sha256=bU-OrgWwf-ghyAjrGB8pFIRnlouwiZD6U1twPxKDY-s,1030 -anthropic/types/server_tool_use_block_param.py,sha256=jF1nGXWXtJLfLWQHMAUqzm97IOvXY4ZY5CaYvkQ45yc,1291 -anthropic/types/shared/__init__.py,sha256=XZCaTBqb3CHyIeuZ3N6JocXtYCbFRMQyGW6ngvXDmQ0,851 -anthropic/types/shared/__pycache__/__init__.cpython-312.pyc,, -anthropic/types/shared/__pycache__/api_error_object.cpython-312.pyc,, -anthropic/types/shared/__pycache__/authentication_error.cpython-312.pyc,, -anthropic/types/shared/__pycache__/billing_error.cpython-312.pyc,, -anthropic/types/shared/__pycache__/error_object.cpython-312.pyc,, -anthropic/types/shared/__pycache__/error_response.cpython-312.pyc,, -anthropic/types/shared/__pycache__/error_type.cpython-312.pyc,, -anthropic/types/shared/__pycache__/gateway_timeout_error.cpython-312.pyc,, -anthropic/types/shared/__pycache__/invalid_request_error.cpython-312.pyc,, -anthropic/types/shared/__pycache__/not_found_error.cpython-312.pyc,, -anthropic/types/shared/__pycache__/overloaded_error.cpython-312.pyc,, -anthropic/types/shared/__pycache__/permission_error.cpython-312.pyc,, -anthropic/types/shared/__pycache__/rate_limit_error.cpython-312.pyc,, -anthropic/types/shared/api_error_object.py,sha256=7AY_Fus-yBeLhaCFix39VFV0DHSrUpd54BWKevuz5z4,273 -anthropic/types/shared/authentication_error.py,sha256=XcEcXJLosZ4WSOdzTjsE4W6Yfik0BnGJhRKMd8sPGsc,294 -anthropic/types/shared/billing_error.py,sha256=yKzFXPOWicwm9b3VSMiTPe9B__FUJeGcv0e0heam9ug,273 -anthropic/types/shared/error_object.py,sha256=mGgRyJgHP7mtVojlSfxz08s8l9EzXXQ4_67-jY-ssxA,982 -anthropic/types/shared/error_response.py,sha256=v9tmfnrglq2xsyzOIL4CmEavVt7M81jX0AOff58kg9g,377 -anthropic/types/shared/error_type.py,sha256=gW0MoR2T_ILXSNjylEJr6c_EXotr5sRkbzyHAFGYjdA,407 -anthropic/types/shared/gateway_timeout_error.py,sha256=-SPRDz7gzUHtrRLC_E7B0waG9ESbVEx1Jhwyerr8yCo,287 -anthropic/types/shared/invalid_request_error.py,sha256=RsNA8WGtbXBVpOE6OiH0Q_Za_lE9WOFjxWhFkvUiWKg,295 -anthropic/types/shared/not_found_error.py,sha256=R6OsCvAmsf_SB2TwoX6E63o049qZMaA6hLvzzSqIKlQ,277 -anthropic/types/shared/overloaded_error.py,sha256=PlyhHt3wmzcnynSfkWbfP4XkLoWsPa9B39V3CyAdgx8,282 -anthropic/types/shared/permission_error.py,sha256=nuyxtLXOiEkYEbFRXiAWjxU6XtdyjkAaXQ2NgMB3pjw,282 -anthropic/types/shared/rate_limit_error.py,sha256=eYULATjXa6KKdqeBauest7RzuN-bhGsY5BWwH9eYv4c,280 -anthropic/types/signature_delta.py,sha256=1e7MwUUU2j5oOie79x-5QU4-Fi1WXccDqgIMnvxfXTQ,280 -anthropic/types/stop_reason.py,sha256=LZTfwN184HpIH4xNBwgNZ44EskkBDIvUWScEgaJWSd0,275 -anthropic/types/text_block.py,sha256=otDts8sbTaDw9kIsvyqMHAxE-hxJv4F4HK4q7QkCmDo,662 -anthropic/types/text_block_param.py,sha256=oz75dBBWudPw3IBl-Xpu4sLP4OdxQmrz8qbQc6pMoCw,659 -anthropic/types/text_citation.py,sha256=otKNuFral4D_25v98K5NuGD0pDWKAyHTW5uvr90Wp5o,850 -anthropic/types/text_citation_param.py,sha256=nquWfBfKiw_BPawDbsJaQihcd5p46I_dZJK2Vb2AH_0,843 -anthropic/types/text_delta.py,sha256=c9IXT5EENOr9TZTD4F6oHbi0gV3SxtsW_FLScgms3SQ,260 -anthropic/types/text_editor_code_execution_create_result_block.py,sha256=bjQ8EBPWZk98zsrqdHnr94BCEMKWA-CVJpsIj8cJqrE,363 -anthropic/types/text_editor_code_execution_create_result_block_param.py,sha256=mGO9BiPd6bOpL-rxZduytEKotTA3wDFhmSzfQfh-Yr4,430 -anthropic/types/text_editor_code_execution_str_replace_result_block.py,sha256=T_Gaqn8R8F_o4tdwwpaa0bh-tKpJ_3bp0rsrGD4e3l4,571 -anthropic/types/text_editor_code_execution_str_replace_result_block_param.py,sha256=7RZFgIBl3Ze27WAYOtVk8qRzC8JsprL7m_Eg0z4vgNA,634 -anthropic/types/text_editor_code_execution_tool_result_block.py,sha256=91tAj_d5ECCT7HFY_d6MGwmPBIwro6Qs_ZgXWZ9UmB4,1042 -anthropic/types/text_editor_code_execution_tool_result_block_param.py,sha256=CH2AL0xc2jmdhbuY4Izz7yusxT4ryOl8R1a2IuNb2kY,1388 -anthropic/types/text_editor_code_execution_tool_result_error.py,sha256=H0mV5Z8yJTZlNTnNyB86xtqojNsETL9FM-_cm312FNQ,572 -anthropic/types/text_editor_code_execution_tool_result_error_code.py,sha256=YL82XHkkOFjAS0xshWULnq-XlbLJ4pmJosNYqDcm3VM,368 -anthropic/types/text_editor_code_execution_tool_result_error_param.py,sha256=UVIyL1TcaKfYCka_b46pZpjdU0ilNwjkChAzj3K8tds,633 -anthropic/types/text_editor_code_execution_view_result_block.py,sha256=eyulv_8B6BDR_sKhphadpT0920NLm3gdYt_SQfW0JNE,539 -anthropic/types/text_editor_code_execution_view_result_block_param.py,sha256=3gAXlrRCysiAVej4yC-s8cN_YQc5X3a8dppDPY69c28,595 -anthropic/types/thinking_block.py,sha256=2SQDYXwdg0VrYgQVBes6tFY2VU7nFe9UCmqBWL4dun8,290 -anthropic/types/thinking_block_param.py,sha256=fqeY1_iHnCCcH_36_TZjfwP90BdS8ikSp_WYmHsheSk,367 -anthropic/types/thinking_capability.py,sha256=NYAI7wtrkMuzl8daV10O2NaUVf4bAgySY5fyNSgW-gI,431 -anthropic/types/thinking_config_adaptive_param.py,sha256=zE65NYEU2KFmhrkIh08cbHqK2moDwceWS7UyT9TALq0,684 -anthropic/types/thinking_config_disabled_param.py,sha256=13QHVviCeaBGcZ2_xsYQROrC9p4-GFhdoeIVXZ9AXX4,326 -anthropic/types/thinking_config_enabled_param.py,sha256=U7TgDzptze2JfoLbCo_NCzzf7bKUzt_vKRxBabIpFgY,1084 -anthropic/types/thinking_config_param.py,sha256=WnXaVizVvLBN-uX8avLjFpYH3XibVtcLmzH2B8qBgFw,570 -anthropic/types/thinking_delta.py,sha256=OfGsFuv2SEKKIbMQw0fdQBnIPZtwNFQEB2oGjlryCNg,276 -anthropic/types/thinking_types.py,sha256=ReqRcu9Xm0kHZfYMO93DdOWnr8S11aMKQMKAtLUhnu0,489 -anthropic/types/tool_bash_20250124_param.py,sha256=9QfLUtQicB3NjUJpdcx9sXuO7tbH2XOzWAmr8BEMR9k,1125 -anthropic/types/tool_choice_any_param.py,sha256=QKtNQbAvqbB5sh02Kbpihxdw77tCFcDOgPV1mFu3h90,536 -anthropic/types/tool_choice_auto_param.py,sha256=Zk2s9x4QNeQN458nzsJjGZlpVwxSLoekGJUm5SZ1ZL0,557 -anthropic/types/tool_choice_none_param.py,sha256=uAVV99JNVXGC5aFsQRildmzCiw1sWpWK3mzMSDoBuqs,361 -anthropic/types/tool_choice_param.py,sha256=nA7VNo9XKPNTpof8yr7GcgAPKOjWyR3glRpBVZZR2gc,561 -anthropic/types/tool_choice_tool_param.py,sha256=2w_8mPWrZTMuqKxxHg-6TGzRv1QEYGPMFItq0mr09rs,626 -anthropic/types/tool_param.py,sha256=zU8DDm_VLb0GIQIJUrWJf14HFbEMcBsdMf5Mx5ya0d8,2749 -anthropic/types/tool_reference_block.py,sha256=gigTfd9xQF4pd8HllSNkKZHi2BRMxZUz7vOBMygwd9c,287 -anthropic/types/tool_reference_block_param.py,sha256=k4_IAnTeXKJIGPe_O698WeOHiDWCqJ1vTevrIcQzutg,654 -anthropic/types/tool_result_block_param.py,sha256=4dHiVC6viFnVwlgh1czg7LrU77nhk1SufuDlV5w-J8k,1080 -anthropic/types/tool_search_tool_bm25_20251119_param.py,sha256=13TA-TatWOok014m5cRB_4nbqMgSUCdUjHtDkevdiL4,1141 -anthropic/types/tool_search_tool_regex_20251119_param.py,sha256=HF2Rx3ZLn4YBnb5bgzv01mrIeVVZIGPxSUFxgaRvG88,1144 -anthropic/types/tool_search_tool_result_block.py,sha256=44lfRqjYAaHX2W_GbQ2YHiEewOkwRU537yy1I1Lq1KA,620 -anthropic/types/tool_search_tool_result_block_param.py,sha256=P7gPbimgu_BPmS8CKndqzkeVUNnhA5-nPxYvZJ7Tgvk,934 -anthropic/types/tool_search_tool_result_error.py,sha256=kqW7iiuTuM1e2vLpUxiymtFlVEf5faxFqqjn9lXStec,490 -anthropic/types/tool_search_tool_result_error_code.py,sha256=0OKCGdL8IxO1XkoxG0TEfeCRRcUO3GdSEsCkZtxvv_U,324 -anthropic/types/tool_search_tool_result_error_param.py,sha256=iOJaI9Av64GJLnlZtqZfVZWAW1J_aI-lr1m-kkqVzpM,489 -anthropic/types/tool_search_tool_search_result_block.py,sha256=T9WrMwBaM0Zs5oegMZ9vB69YMWAJRYN7LDmGNzU3TrA,433 -anthropic/types/tool_search_tool_search_result_block_param.py,sha256=jmR8UQTvGSQoqT7ZVU6kcP3ymw_0DCs25PQ0sHJIwbM,525 -anthropic/types/tool_text_editor_20250124_param.py,sha256=Rov6MZehMEYfF1rWlzpkdXN9CiLfFPgq3m90GyW4OHQ,1158 -anthropic/types/tool_text_editor_20250429_param.py,sha256=T00nVZ2FdhsklTowioLTFA6t7grGzKQmibuVUBCFgsM,1167 -anthropic/types/tool_text_editor_20250728_param.py,sha256=dbJiW812d8TNhuSE3PgYPNktwVkxaZS38w1iAcK0dXw,1339 -anthropic/types/tool_union_param.py,sha256=lk5gZsFyk7k8n_oRIAFE53UCeuXwdmplF6lxDMg00D4,1898 -anthropic/types/tool_use_block.py,sha256=RYLN_J_MFe2jKPMCcsXMLaYvytu9h_S17bBwZWOciRg,795 -anthropic/types/tool_use_block_param.py,sha256=XmVfJ0C4XqJUufg_i215PkpLlYqvOgN69sI9AG6Vz0Q,1010 -anthropic/types/url_image_source_param.py,sha256=jhgWbgwFgChO8v_XZzuMpuv2u3E0R8zISam8WbVwXyw,329 -anthropic/types/url_pdf_source_param.py,sha256=knFb8DFOWlrFFYwXnZbQx8tqejjWbPQjn3euIWPBMKk,325 -anthropic/types/usage.py,sha256=6y-mFAZOKyOmJcgSdhjinUsdhoRw6IX_0fndfF-Xt9Q,1174 -anthropic/types/user_location_param.py,sha256=G8D8abZaGK9dx0sPL673zFlNCpSAiLqGcdd1Xru2G9Y,712 -anthropic/types/web_fetch_block.py,sha256=dzrMMmL9y7NkXI2BIMSRngBlVn_W6BWGuNghk3GSSRc,501 -anthropic/types/web_fetch_block_param.py,sha256=VQoP5-_hVXXXGtW2dMCLKqsPE5XWt44RWngxDDUsicY,588 -anthropic/types/web_fetch_tool_20250910_param.py,sha256=hPkt5sb_z6t4j8ZOuy_9SvP47uOjqKmQazE9od1s1Z4,1842 -anthropic/types/web_fetch_tool_20260209_param.py,sha256=Lkmu5QrT2QowkMVZAASBqxNTJWlRia6ec9oVkGjEB5w,1842 -anthropic/types/web_fetch_tool_20260309_param.py,sha256=vHQ5QR7maE8EjxhLmlR3PE-sUZsJT3FhQuaqGOawYs4,2170 -anthropic/types/web_fetch_tool_result_block.py,sha256=Rv2rpVWrzEAWYcLtRuRwTwVgG74BFjJmo2b3fjQXakg,1013 -anthropic/types/web_fetch_tool_result_block_param.py,sha256=vnzbkdlYIbHncSpDe_WJdIuOyDeywoEzipKhUMyp3tY,1250 -anthropic/types/web_fetch_tool_result_error_block.py,sha256=7qkrv9wTbKXP8azNe9kNu0l00ZTsURvLIUNXOcgWumE,419 -anthropic/types/web_fetch_tool_result_error_block_param.py,sha256=qsjFQp9iuGCKBA3CuYwFs9KIp2QRNPW3Yw3VLhxn5Nw,487 -anthropic/types/web_fetch_tool_result_error_code.py,sha256=xZvnRJTI32qQsry4ql8unmQh9rVijAp7Rn5VgxuY1UI,428 -anthropic/types/web_search_result_block.py,sha256=Y8-r0n86qKex4eonDCKre4mS1w7SXleuOFMCT6HmpHU,396 -anthropic/types/web_search_result_block_param.py,sha256=AbQnJIyfQYHSOEUYxBCGuZNqP5hmpFZI_1SxyD5QC8A,476 -anthropic/types/web_search_tool_20250305_param.py,sha256=JO2BAPKLH1yKSveO4QGA-VKxaAg_Pe3D-EFEoPNHabA,1849 -anthropic/types/web_search_tool_20260209_param.py,sha256=GVjMhFhhfd78nEKasmsmUF5Dp4Sa34h-PWZS6rvsm4o,1849 -anthropic/types/web_search_tool_request_error_param.py,sha256=SKhG96rxlkT_pMqqZbzGc9LPJ4YLTxUdOfWIxooDNfY,485 -anthropic/types/web_search_tool_result_block.py,sha256=600QbHE1ByZUY_1RXirTfkYUT6quCflQfz3Ik23yi2Q,919 -anthropic/types/web_search_tool_result_block_content.py,sha256=Ev_QL9KMO7emKGcTduZkNgyWFZAiG7kYamPGqwpCffk,437 -anthropic/types/web_search_tool_result_block_param.py,sha256=fO1spOhjsZ-5jKOeCVOznMqwYqjNAN0QXDGJ9osCT7s,1156 -anthropic/types/web_search_tool_result_block_param_content_param.py,sha256=YIBYcDI1GSlrI-4QBugJ_2YLpkofR7Da3vOwVDU44lo,542 -anthropic/types/web_search_tool_result_error.py,sha256=cQxTY2XpxmJb-bnCyZt0mXj0WOXnpX1T2Zo4whErRJI,415 -anthropic/types/web_search_tool_result_error_code.py,sha256=4PnhXqvf5O4hB5hbhNxFmyE34933fMFiAPNI1wWyFLc,355 diff --git a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/REQUESTED deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/WHEEL deleted file mode 100644 index 21aaa729..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.26.3 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/licenses/LICENSE deleted file mode 100644 index ac71a66c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic-0.97.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -Copyright 2023 Anthropic, PBC. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/.venv/lib/python3.12/site-packages/anthropic/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/__init__.py deleted file mode 100644 index 70a9e4f2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/__init__.py +++ /dev/null @@ -1,121 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import typing as _t - -from . import types -from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given -from ._utils import file_from_path -from ._client import ( - Client, - Stream, - Timeout, - Anthropic, - Transport, - AsyncClient, - AsyncStream, - AsyncAnthropic, - RequestOptions, -) -from ._models import BaseModel -from ._version import __title__, __version__ -from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse -from ._constants import ( - AI_PROMPT as AI_PROMPT, - HUMAN_PROMPT as HUMAN_PROMPT, - DEFAULT_TIMEOUT, - DEFAULT_MAX_RETRIES, - DEFAULT_CONNECTION_LIMITS, -) -from ._exceptions import ( - APIError, - ConflictError, - NotFoundError, - AnthropicError, - APIStatusError, - RateLimitError, - APITimeoutError, - BadRequestError, - APIConnectionError, - AuthenticationError, - InternalServerError, - PermissionDeniedError, - UnprocessableEntityError, - APIResponseValidationError, -) -from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient -from ._utils._logs import setup_logging as _setup_logging -from .lib._parse._transform import transform_schema - -__all__ = [ - "types", - "__version__", - "__title__", - "NoneType", - "Transport", - "ProxiesTypes", - "NotGiven", - "NOT_GIVEN", - "not_given", - "Omit", - "omit", - "AnthropicError", - "APIError", - "APIStatusError", - "APITimeoutError", - "APIConnectionError", - "APIResponseValidationError", - "BadRequestError", - "AuthenticationError", - "PermissionDeniedError", - "NotFoundError", - "ConflictError", - "UnprocessableEntityError", - "RateLimitError", - "InternalServerError", - "Timeout", - "RequestOptions", - "Client", - "AsyncClient", - "Stream", - "AsyncStream", - "Anthropic", - "AsyncAnthropic", - "file_from_path", - "BaseModel", - "DEFAULT_TIMEOUT", - "DEFAULT_MAX_RETRIES", - "DEFAULT_CONNECTION_LIMITS", - "DefaultHttpxClient", - "DefaultAsyncHttpxClient", - "DefaultAioHttpClient", - "HUMAN_PROMPT", - "AI_PROMPT", - "beta_tool", - "beta_async_tool", - "transform_schema", -] - -if not _t.TYPE_CHECKING: - from ._utils._resources_proxy import resources as resources - -from .lib.aws import AnthropicAWS as AnthropicAWS, AsyncAnthropicAWS as AsyncAnthropicAWS -from .lib.tools import beta_tool, beta_async_tool -from .lib.vertex import * -from .lib.bedrock import * -from .lib.foundry import AnthropicFoundry as AnthropicFoundry, AsyncAnthropicFoundry as AsyncAnthropicFoundry -from .lib.streaming import * - -_setup_logging() - -# Update the __module__ attribute for exported symbols so that -# error messages point to this module instead of the module -# it was originally defined in, e.g. -# anthropic._exceptions.NotFoundError -> anthropic.NotFoundError -__locals = locals() -for __name in __all__: - if not __name.startswith("__"): - try: - __locals[__name].__module__ = "anthropic" - except (TypeError, AttributeError): - # Some of our exported symbols are builtins which we can't set attributes for. - pass diff --git a/.venv/lib/python3.12/site-packages/anthropic/_base_client.py b/.venv/lib/python3.12/site-packages/anthropic/_base_client.py deleted file mode 100644 index 02122c53..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_base_client.py +++ /dev/null @@ -1,2271 +0,0 @@ -from __future__ import annotations - -import sys -import json -import time -import uuid -import email -import socket -import asyncio -import inspect -import logging -import platform -import warnings -import email.utils -from types import TracebackType -from random import random -from typing import ( - TYPE_CHECKING, - Any, - Dict, - List, - Type, - Tuple, - Union, - Generic, - Mapping, - TypeVar, - Iterable, - Iterator, - Optional, - Generator, - AsyncIterator, - cast, - overload, -) -from typing_extensions import Literal, override, get_origin - -import anyio -import httpx -import distro -import pydantic -from httpx import URL, Proxy, HTTPTransport, AsyncHTTPTransport -from pydantic import PrivateAttr - -from . import _exceptions -from ._qs import Querystring -from ._files import to_httpx_files, async_to_httpx_files -from ._types import ( - Body, - Omit, - Query, - Headers, - Timeout, - NotGiven, - ResponseT, - AnyMapping, - PostParser, - BinaryTypes, - RequestFiles, - HttpxSendArgs, - RequestOptions, - AsyncBinaryTypes, - HttpxRequestFiles, - ModelBuilderProtocol, - not_given, -) -from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping -from ._compat import PYDANTIC_V1, model_copy, model_dump -from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type -from ._response import ( - APIResponse, - BaseAPIResponse, - AsyncAPIResponse, - extract_response_type, -) -from ._constants import ( - DEFAULT_TIMEOUT, - MAX_RETRY_DELAY, - DEFAULT_MAX_RETRIES, - INITIAL_RETRY_DELAY, - RAW_RESPONSE_HEADER, - OVERRIDE_CAST_TO_HEADER, - DEFAULT_CONNECTION_LIMITS, -) -from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder -from ._exceptions import ( - APIStatusError, - APITimeoutError, - APIConnectionError, - APIResponseValidationError, -) -from ._utils._json import openapi_dumps -from ._utils._httpx import get_environment_proxies -from ._legacy_response import LegacyAPIResponse - -log: logging.Logger = logging.getLogger(__name__) - -# TODO: make base page type vars covariant -SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]") -AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]") - - -_T = TypeVar("_T") -_T_co = TypeVar("_T_co", covariant=True) - -_StreamT = TypeVar("_StreamT", bound=Stream[Any]) -_AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) - -if TYPE_CHECKING: - from httpx._config import ( - DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage] - ) - - HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG -else: - try: - from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT - except ImportError: - # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366 - HTTPX_DEFAULT_TIMEOUT = Timeout(5.0) - - -class PageInfo: - """Stores the necessary information to build the request to retrieve the next page. - - Either `url` or `params` must be set. - """ - - url: URL | NotGiven - params: Query | NotGiven - json: Body | NotGiven - - @overload - def __init__( - self, - *, - url: URL, - ) -> None: ... - - @overload - def __init__( - self, - *, - params: Query, - ) -> None: ... - - @overload - def __init__( - self, - *, - json: Body, - ) -> None: ... - - def __init__( - self, - *, - url: URL | NotGiven = not_given, - json: Body | NotGiven = not_given, - params: Query | NotGiven = not_given, - ) -> None: - self.url = url - self.json = json - self.params = params - - @override - def __repr__(self) -> str: - if self.url: - return f"{self.__class__.__name__}(url={self.url})" - if self.json: - return f"{self.__class__.__name__}(json={self.json})" - return f"{self.__class__.__name__}(params={self.params})" - - -class BasePage(GenericModel, Generic[_T]): - """ - Defines the core interface for pagination. - - Type Args: - ModelT: The pydantic model that represents an item in the response. - - Methods: - has_next_page(): Check if there is another page available - next_page_info(): Get the necessary information to make a request for the next page - """ - - _options: FinalRequestOptions = PrivateAttr() - _model: Type[_T] = PrivateAttr() - - def has_next_page(self) -> bool: - items = self._get_page_items() - if not items: - return False - return self.next_page_info() is not None - - def next_page_info(self) -> Optional[PageInfo]: ... - - def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] - ... - - def _params_from_url(self, url: URL) -> httpx.QueryParams: - # TODO: do we have to preprocess params here? - return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params) - - def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: - options = model_copy(self._options) - options._strip_raw_response_header() - - if not isinstance(info.params, NotGiven): - options.params = {**options.params, **info.params} - return options - - if not isinstance(info.url, NotGiven): - params = self._params_from_url(info.url) - url = info.url.copy_with(params=params) - options.params = dict(url.params) - options.url = str(url) - return options - - if not isinstance(info.json, NotGiven): - if not is_mapping(info.json): - raise TypeError("Pagination is only supported with mappings") - - if not options.json_data: - options.json_data = {**info.json} - else: - if not is_mapping(options.json_data): - raise TypeError("Pagination is only supported with mappings") - - options.json_data = {**options.json_data, **info.json} - return options - - raise ValueError("Unexpected PageInfo state") - - -class BaseSyncPage(BasePage[_T], Generic[_T]): - _client: SyncAPIClient = pydantic.PrivateAttr() - - def _set_private_attributes( - self, - client: SyncAPIClient, - model: Type[_T], - options: FinalRequestOptions, - ) -> None: - if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: - self.__pydantic_private__ = {} - - self._model = model - self._client = client - self._options = options - - # Pydantic uses a custom `__iter__` method to support casting BaseModels - # to dictionaries. e.g. dict(model). - # As we want to support `for item in page`, this is inherently incompatible - # with the default pydantic behaviour. It is not possible to support both - # use cases at once. Fortunately, this is not a big deal as all other pydantic - # methods should continue to work as expected as there is an alternative method - # to cast a model to a dictionary, model.dict(), which is used internally - # by pydantic. - def __iter__(self) -> Iterator[_T]: # type: ignore - for page in self.iter_pages(): - for item in page._get_page_items(): - yield item - - def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]: - page = self - while True: - yield page - if page.has_next_page(): - page = page.get_next_page() - else: - return - - def get_next_page(self: SyncPageT) -> SyncPageT: - info = self.next_page_info() - if not info: - raise RuntimeError( - "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." - ) - - options = self._info_to_options(info) - return self._client._request_api_list(self._model, page=self.__class__, options=options) - - -class AsyncPaginator(Generic[_T, AsyncPageT]): - def __init__( - self, - client: AsyncAPIClient, - options: FinalRequestOptions, - page_cls: Type[AsyncPageT], - model: Type[_T], - ) -> None: - self._model = model - self._client = client - self._options = options - self._page_cls = page_cls - - def __await__(self) -> Generator[Any, None, AsyncPageT]: - return self._get_page().__await__() - - async def _get_page(self) -> AsyncPageT: - def _parser(resp: AsyncPageT) -> AsyncPageT: - resp._set_private_attributes( - model=self._model, - options=self._options, - client=self._client, - ) - return resp - - self._options.post_parser = _parser - - return await self._client.request(self._page_cls, self._options) - - async def __aiter__(self) -> AsyncIterator[_T]: - # https://github.com/microsoft/pyright/issues/3464 - page = cast( - AsyncPageT, - await self, # type: ignore - ) - async for item in page: - yield item - - -class BaseAsyncPage(BasePage[_T], Generic[_T]): - _client: AsyncAPIClient = pydantic.PrivateAttr() - - def _set_private_attributes( - self, - model: Type[_T], - client: AsyncAPIClient, - options: FinalRequestOptions, - ) -> None: - if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: - self.__pydantic_private__ = {} - - self._model = model - self._client = client - self._options = options - - async def __aiter__(self) -> AsyncIterator[_T]: - async for page in self.iter_pages(): - for item in page._get_page_items(): - yield item - - async def iter_pages(self: AsyncPageT) -> AsyncIterator[AsyncPageT]: - page = self - while True: - yield page - if page.has_next_page(): - page = await page.get_next_page() - else: - return - - async def get_next_page(self: AsyncPageT) -> AsyncPageT: - info = self.next_page_info() - if not info: - raise RuntimeError( - "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." - ) - - options = self._info_to_options(info) - return await self._client._request_api_list(self._model, page=self.__class__, options=options) - - -_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) -_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) - - -class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): - _client: _HttpxClientT - _version: str - _base_url: URL - max_retries: int - timeout: Union[float, Timeout, None] - _strict_response_validation: bool - _idempotency_header: str | None - _default_stream_cls: type[_DefaultStreamT] | None = None - - def __init__( - self, - *, - version: str, - base_url: str | URL, - _strict_response_validation: bool, - max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None = DEFAULT_TIMEOUT, - custom_headers: Mapping[str, str] | None = None, - custom_query: Mapping[str, object] | None = None, - ) -> None: - self._version = version - self._base_url = self._enforce_trailing_slash(URL(base_url)) - self.max_retries = max_retries - self.timeout = timeout - self._custom_headers = custom_headers or {} - self._custom_query = custom_query or {} - self._strict_response_validation = _strict_response_validation - self._idempotency_header = None - self._platform: Platform | None = None - - if max_retries is None: # pyright: ignore[reportUnnecessaryComparison] - raise TypeError( - "max_retries cannot be None. If you want to disable retries, pass `0`; if you want unlimited retries, pass `math.inf` or a very high number; if you want the default behavior, pass `anthropic.DEFAULT_MAX_RETRIES`" - ) - - def _enforce_trailing_slash(self, url: URL) -> URL: - if url.raw_path.endswith(b"/"): - return url - return url.copy_with(raw_path=url.raw_path + b"/") - - def _make_status_error_from_response( - self, - response: httpx.Response, - ) -> APIStatusError: - if response.is_closed and not response.is_stream_consumed: - # We can't read the response body as it has been closed - # before it was read. This can happen if an event hook - # raises a status error. - body = None - err_msg = f"Error code: {response.status_code}" - else: - err_text = response.text.strip() - body = err_text - - try: - body = json.loads(err_text) - err_msg = f"Error code: {response.status_code} - {body}" - except Exception: - err_msg = err_text or f"Error code: {response.status_code}" - - return self._make_status_error(err_msg, body=body, response=response) - - def _make_status_error( - self, - err_msg: str, - *, - body: object, - response: httpx.Response, - ) -> _exceptions.APIStatusError: - raise NotImplementedError() - - def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers: - custom_headers = options.headers or {} - headers_dict = _merge_mappings( - { - "x-stainless-timeout": str(options.timeout.read) - if isinstance(options.timeout, Timeout) - else str(options.timeout), - **self.default_headers, - }, - custom_headers, - ) - self._validate_headers(headers_dict, custom_headers) - - # headers are case-insensitive while dictionaries are not. - headers = httpx.Headers(headers_dict) - - idempotency_header = self._idempotency_header - if idempotency_header and options.idempotency_key and idempotency_header not in headers: - headers[idempotency_header] = options.idempotency_key - - # Don't set these headers if they were already set or removed by the caller. We check - # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. - lower_custom_headers = [header.lower() for header in custom_headers] - if "x-stainless-retry-count" not in lower_custom_headers: - headers["x-stainless-retry-count"] = str(retries_taken) - if "x-stainless-read-timeout" not in lower_custom_headers: - timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout - if isinstance(timeout, Timeout): - timeout = timeout.read - if timeout is not None: - headers["x-stainless-read-timeout"] = str(timeout) - - return headers - - def _prepare_url(self, url: str) -> URL: - """ - Merge a URL argument together with any 'base_url' on the client, - to create the URL used for the outgoing request. - """ - # Copied from httpx's `_merge_url` method. - merge_url = URL(url) - if merge_url.is_relative_url: - merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") - return self.base_url.copy_with(raw_path=merge_raw_path) - - return merge_url - - def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: - return SSEDecoder() - - def _build_request( - self, - options: FinalRequestOptions, - *, - retries_taken: int = 0, - ) -> httpx.Request: - if log.isEnabledFor(logging.DEBUG): - log.debug( - "Request options: %s", - model_dump( - options, - exclude_unset=True, - # Pydantic v1 can't dump every type we support in content, so we exclude it for now. - exclude={ - "content", - } - if PYDANTIC_V1 - else {}, - ), - ) - kwargs: dict[str, Any] = {} - - json_data = options.json_data - if options.extra_json is not None: - if json_data is None: - json_data = cast(Body, options.extra_json) - elif is_mapping(json_data): - json_data = _merge_mappings(json_data, options.extra_json) - else: - raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") - - headers = self._build_headers(options, retries_taken=retries_taken) - params = _merge_mappings(self.default_query, options.params) - content_type = headers.get("Content-Type") - files = options.files - - # If the given Content-Type header is multipart/form-data then it - # has to be removed so that httpx can generate the header with - # additional information for us as it has to be in this form - # for the server to be able to correctly parse the request: - # multipart/form-data; boundary=---abc-- - if content_type is not None and content_type.startswith("multipart/form-data"): - if "boundary" not in content_type: - # only remove the header if the boundary hasn't been explicitly set - # as the caller doesn't want httpx to come up with their own boundary - headers.pop("Content-Type") - - # As we are now sending multipart/form-data instead of application/json - # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding - if json_data: - if not is_dict(json_data): - raise TypeError( - f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead." - ) - kwargs["data"] = self._serialize_multipartform(json_data) - - # httpx determines whether or not to send a "multipart/form-data" - # request based on the truthiness of the "files" argument. - # This gets around that issue by generating a dict value that - # evaluates to true. - # - # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 - if not files: - files = cast(HttpxRequestFiles, ForceMultipartDict()) - - prepared_url = self._prepare_url(options.url) - # preserve hard-coded query params from the url - if params and prepared_url.query: - params = {**dict(prepared_url.params.items()), **params} - prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) - if "_" in prepared_url.host: - # work around https://github.com/encode/httpx/discussions/2880 - kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} - - is_body_allowed = options.method.lower() != "get" - - if is_body_allowed: - if options.content is not None and json_data is not None: - raise TypeError("Passing both `content` and `json_data` is not supported") - if options.content is not None and files is not None: - raise TypeError("Passing both `content` and `files` is not supported") - if options.content is not None: - kwargs["content"] = options.content - elif isinstance(json_data, bytes): - kwargs["content"] = json_data - elif not files: - # Don't set content when JSON is sent as multipart/form-data, - # since httpx's content param overrides other body arguments - kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None - kwargs["files"] = files - else: - headers.pop("Content-Type", None) - kwargs.pop("data", None) - - # TODO: report this error to httpx - return self._client.build_request( # pyright: ignore[reportUnknownMemberType] - headers=headers, - timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, - method=options.method, - url=prepared_url, - # the `Query` type that we use is incompatible with qs' - # `Params` type as it needs to be typed as `Mapping[str, object]` - # so that passing a `TypedDict` doesn't cause an error. - # https://github.com/microsoft/pyright/issues/3526#event-6715453066 - params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, - **kwargs, - ) - - def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, object]: - items = self.qs.stringify_items( - # TODO: type ignore is required as stringify_items is well typed but we can't be - # well typed without heavy validation. - data, # type: ignore - array_format="brackets", - ) - serialized: dict[str, object] = {} - for key, value in items: - existing = serialized.get(key) - - if not existing: - serialized[key] = value - continue - - # If a value has already been set for this key then that - # means we're sending data like `array[]=[1, 2, 3]` and we - # need to tell httpx that we want to send multiple values with - # the same key which is done by using a list or a tuple. - # - # Note: 2d arrays should never result in the same key at both - # levels so it's safe to assume that if the value is a list, - # it was because we changed it to be a list. - if is_list(existing): - existing.append(value) - else: - serialized[key] = [existing, value] - - return serialized - - def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]: - if not is_given(options.headers): - return cast_to - - # make a copy of the headers so we don't mutate user-input - headers = dict(options.headers) - - # we internally support defining a temporary header to override the - # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` - # see _response.py for implementation details - override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) - if is_given(override_cast_to): - options.headers = headers - return cast(Type[ResponseT], override_cast_to) - - return cast_to - - def _should_stream_response_body(self, request: httpx.Request) -> bool: - return request.headers.get(RAW_RESPONSE_HEADER) == "stream" # type: ignore[no-any-return] - - def _process_response_data( - self, - *, - data: object, - cast_to: type[ResponseT], - response: httpx.Response, - ) -> ResponseT: - if data is None: - return cast(ResponseT, None) - - if cast_to is object: - return cast(ResponseT, data) - - try: - if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol): - return cast(ResponseT, cast_to.build(response=response, data=data)) - - if self._strict_response_validation: - return cast(ResponseT, validate_type(type_=cast_to, value=data)) - - return cast(ResponseT, construct_type(type_=cast_to, value=data)) - except pydantic.ValidationError as err: - raise APIResponseValidationError(response=response, body=data) from err - - @property - def qs(self) -> Querystring: - return Querystring() - - @property - def custom_auth(self) -> httpx.Auth | None: - return None - - @property - def auth_headers(self) -> dict[str, str]: - return {} - - @property - def default_headers(self) -> dict[str, str | Omit]: - return { - "Accept": "application/json", - "Content-Type": "application/json", - "User-Agent": self.user_agent, - **self.platform_headers(), - **self.auth_headers, - **self._custom_headers, - } - - @property - def default_query(self) -> dict[str, object]: - return { - **self._custom_query, - } - - def _validate_headers( - self, - headers: Headers, # noqa: ARG002 - custom_headers: Headers, # noqa: ARG002 - ) -> None: - """Validate the given default headers and custom headers. - - Does nothing by default. - """ - return - - @property - def user_agent(self) -> str: - return f"{self.__class__.__name__}/Python {self._version}" - - @property - def base_url(self) -> URL: - return self._base_url - - @base_url.setter - def base_url(self, url: URL | str) -> None: - self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) - - def platform_headers(self) -> Dict[str, str]: - # the actual implementation is in a separate `lru_cache` decorated - # function because adding `lru_cache` to methods will leak memory - # https://github.com/python/cpython/issues/88476 - return platform_headers(self._version, platform=self._platform) - - def _calculate_nonstreaming_timeout(self, max_tokens: int, max_nonstreaming_tokens: int | None) -> Timeout: - maximum_time = 60 * 60 - default_time = 60 * 10 - - expected_time = maximum_time * max_tokens / 128_000 - if expected_time > default_time or (max_nonstreaming_tokens and max_tokens > max_nonstreaming_tokens): - raise ValueError( - "Streaming is required for operations that may take longer than 10 minutes. " - + "See https://github.com/anthropics/anthropic-sdk-python#long-requests for more details", - ) - return Timeout( - default_time, - connect=5.0, - ) - - def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: - """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. - - About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax - """ - if response_headers is None: - return None - - # First, try the non-standard `retry-after-ms` header for milliseconds, - # which is more precise than integer-seconds `retry-after` - try: - retry_ms_header = response_headers.get("retry-after-ms", None) - return float(retry_ms_header) / 1000 - except (TypeError, ValueError): - pass - - # Next, try parsing `retry-after` header as seconds (allowing nonstandard floats). - retry_header = response_headers.get("retry-after") - try: - # note: the spec indicates that this should only ever be an integer - # but if someone sends a float there's no reason for us to not respect it - return float(retry_header) - except (TypeError, ValueError): - pass - - # Last, try parsing `retry-after` as a date. - retry_date_tuple = email.utils.parsedate_tz(retry_header) - if retry_date_tuple is None: - return None - - retry_date = email.utils.mktime_tz(retry_date_tuple) - return float(retry_date - time.time()) - - def _calculate_retry_timeout( - self, - remaining_retries: int, - options: FinalRequestOptions, - response_headers: Optional[httpx.Headers] = None, - ) -> float: - max_retries = options.get_max_retries(self.max_retries) - - # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. - retry_after = self._parse_retry_after_header(response_headers) - if retry_after is not None and 0 < retry_after <= 60: - return retry_after - - # Also cap retry count to 1000 to avoid any potential overflows with `pow` - nb_retries = min(max_retries - remaining_retries, 1000) - - # Apply exponential backoff, but not more than the max. - sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) - - # Apply some jitter, plus-or-minus half a second. - jitter = 1 - 0.25 * random() - timeout = sleep_seconds * jitter - return timeout if timeout >= 0 else 0 - - def _should_retry(self, response: httpx.Response) -> bool: - # Note: this is not a standard header - should_retry_header = response.headers.get("x-should-retry") - - # If the server explicitly says whether or not to retry, obey. - if should_retry_header == "true": - log.debug("Retrying as header `x-should-retry` is set to `true`") - return True - if should_retry_header == "false": - log.debug("Not retrying as header `x-should-retry` is set to `false`") - return False - - # Retry on request timeouts. - if response.status_code == 408: - log.debug("Retrying due to status code %i", response.status_code) - return True - - # Retry on lock timeouts. - if response.status_code == 409: - log.debug("Retrying due to status code %i", response.status_code) - return True - - # Retry on rate limits. - if response.status_code == 429: - log.debug("Retrying due to status code %i", response.status_code) - return True - - # Retry internal errors. - if response.status_code >= 500: - log.debug("Retrying due to status code %i", response.status_code) - return True - - log.debug("Not retrying") - return False - - def _idempotency_key(self) -> str: - return f"stainless-python-retry-{uuid.uuid4()}" - - -class _DefaultHttpxClient(httpx.Client): - def __init__(self, **kwargs: Any) -> None: - kwargs.setdefault("timeout", DEFAULT_TIMEOUT) - kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) - kwargs.setdefault("follow_redirects", True) - - if "transport" not in kwargs: - socket_options: List[Tuple[int, int, Union[int, bool]]] = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True)] - - TCP_KEEPINTVL = getattr(socket, "TCP_KEEPINTVL", None) - - if TCP_KEEPINTVL is not None: - socket_options.append((socket.IPPROTO_TCP, TCP_KEEPINTVL, 60)) - elif sys.platform == "darwin": - TCP_KEEPALIVE = getattr(socket, "TCP_KEEPALIVE", 0x10) - socket_options.append((socket.IPPROTO_TCP, TCP_KEEPALIVE, 60)) - - TCP_KEEPCNT = getattr(socket, "TCP_KEEPCNT", None) - if TCP_KEEPCNT is not None: - socket_options.append((socket.IPPROTO_TCP, TCP_KEEPCNT, 5)) - - TCP_KEEPIDLE = getattr(socket, "TCP_KEEPIDLE", None) - if TCP_KEEPIDLE is not None: - socket_options.append((socket.IPPROTO_TCP, TCP_KEEPIDLE, 60)) - - proxy_map = {key: None if url is None else Proxy(url=url) for key, url in get_environment_proxies().items()} - - transport_kwargs = { - arg: kwargs[arg] for arg in ("verify", "cert", "trust_env", "http1", "http2", "limits") if arg in kwargs - } - - transport_kwargs["socket_options"] = socket_options - - proxy_mounts = { - key: None if proxy is None else HTTPTransport(proxy=proxy, **transport_kwargs) - for key, proxy in proxy_map.items() - } - default_transport = HTTPTransport(**transport_kwargs) - - # Prioritize the mounts set by the user over the environment variables. - proxy_mounts.update(kwargs.get("mounts", {})) - kwargs["mounts"] = proxy_mounts - - # Sets the default transport so that HTTPX won't automatically configure proxies. - kwargs["transport"] = default_transport - - super().__init__(**kwargs) - - -if TYPE_CHECKING: - DefaultHttpxClient = httpx.Client - """An alias to `httpx.Client` that provides the same defaults that this SDK - uses internally. - - This is useful because overriding the `http_client` with your own instance of - `httpx.Client` will result in httpx's defaults being used, not ours. - """ -else: - DefaultHttpxClient = _DefaultHttpxClient - - -class SyncHttpxClientWrapper(DefaultHttpxClient): - def __del__(self) -> None: - if self.is_closed: - return - - try: - self.close() - except Exception: - pass - - -class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]): - _client: httpx.Client - _default_stream_cls: type[Stream[Any]] | None = None - - def __init__( - self, - *, - version: str, - base_url: str | URL, - max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = not_given, - http_client: httpx.Client | None = None, - custom_headers: Mapping[str, str] | None = None, - custom_query: Mapping[str, object] | None = None, - _strict_response_validation: bool, - ) -> None: - if not is_given(timeout): - # if the user passed in a custom http client with a non-default - # timeout set then we use that timeout. - # - # note: there is an edge case here where the user passes in a client - # where they've explicitly set the timeout to match the default timeout - # as this check is structural, meaning that we'll think they didn't - # pass in a timeout and will ignore it - if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: - timeout = http_client.timeout - else: - timeout = DEFAULT_TIMEOUT - - if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] - raise TypeError( - f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" - ) - - super().__init__( - version=version, - # cast to a valid type because mypy doesn't understand our type narrowing - timeout=cast(Timeout, timeout), - base_url=base_url, - max_retries=max_retries, - custom_query=custom_query, - custom_headers=custom_headers, - _strict_response_validation=_strict_response_validation, - ) - self._client = http_client or SyncHttpxClientWrapper( - base_url=base_url, - # cast to a valid type because mypy doesn't understand our type narrowing - timeout=cast(Timeout, timeout), - ) - - def is_closed(self) -> bool: - return self._client.is_closed - - def close(self) -> None: - """Close the underlying HTTPX client. - - The client will *not* be usable after this. - """ - # If an error is thrown while constructing a client, self._client - # may not be present - if hasattr(self, "_client"): - self._client.close() - - def __enter__(self: _T) -> _T: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.close() - - def _prepare_options( - self, - options: FinalRequestOptions, # noqa: ARG002 - ) -> FinalRequestOptions: - """Hook for mutating the given options""" - return options - - def _prepare_request( - self, - request: httpx.Request, # noqa: ARG002 - ) -> None: - """This method is used as a callback for mutating the `Request` object - after it has been constructed. - This is useful for cases where you want to add certain headers based off of - the request properties, e.g. `url`, `method` etc. - """ - return None - - @overload - def request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: Literal[True], - stream_cls: Type[_StreamT], - ) -> _StreamT: ... - - @overload - def request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: Literal[False] = False, - ) -> ResponseT: ... - - @overload - def request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: bool = False, - stream_cls: Type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: ... - - def request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: bool = False, - stream_cls: type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: - cast_to = self._maybe_override_cast_to(cast_to, options) - - # create a copy of the options we were given so that if the - # options are mutated later & we then retry, the retries are - # given the original options - input_options = model_copy(options) - if input_options.idempotency_key is None and input_options.method.lower() != "get": - # ensure the idempotency key is reused between requests - input_options.idempotency_key = self._idempotency_key() - - response: httpx.Response | None = None - max_retries = input_options.get_max_retries(self.max_retries) - - retries_taken = 0 - for retries_taken in range(max_retries + 1): - options = model_copy(input_options) - options = self._prepare_options(options) - - remaining_retries = max_retries - retries_taken - request = self._build_request(options, retries_taken=retries_taken) - self._prepare_request(request) - - kwargs: HttpxSendArgs = {} - if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth - - if options.follow_redirects is not None: - kwargs["follow_redirects"] = options.follow_redirects - - log.debug("Sending HTTP Request: %s %s", request.method, request.url) - - response = None - try: - response = self._client.send( - request, - stream=stream or self._should_stream_response_body(request=request), - **kwargs, - ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) - - if remaining_retries > 0: - self._sleep_for_retry( - retries_taken=retries_taken, - max_retries=max_retries, - options=input_options, - response=None, - ) - continue - - log.debug("Raising timeout error") - raise APITimeoutError(request=request) from err - except Exception as err: - log.debug("Encountered Exception", exc_info=True) - - if remaining_retries > 0: - self._sleep_for_retry( - retries_taken=retries_taken, - max_retries=max_retries, - options=input_options, - response=None, - ) - continue - - log.debug("Raising connection error") - raise APIConnectionError(request=request) from err - - log.debug( - 'HTTP Response: %s %s "%i %s" %s', - request.method, - request.url, - response.status_code, - response.reason_phrase, - response.headers, - ) - log.debug("request_id: %s", response.headers.get("request-id")) - - try: - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - - if remaining_retries > 0 and self._should_retry(err.response): - err.response.close() - self._sleep_for_retry( - retries_taken=retries_taken, - max_retries=max_retries, - options=input_options, - response=response, - ) - continue - - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - err.response.read() - - log.debug("Re-raising status error") - raise self._make_status_error_from_response(err.response) from None - - break - - assert response is not None, "could not resolve response (should never happen)" - return self._process_response( - cast_to=cast_to, - options=options, - response=response, - stream=stream, - stream_cls=stream_cls, - retries_taken=retries_taken, - ) - - def _sleep_for_retry( - self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None - ) -> None: - remaining_retries = max_retries - retries_taken - if remaining_retries == 1: - log.debug("1 retry left") - else: - log.debug("%i retries left", remaining_retries) - - timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) - log.info("Retrying request to %s in %f seconds", options.url, timeout) - - time.sleep(timeout) - - def _process_response( - self, - *, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - response: httpx.Response, - stream: bool, - stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, - retries_taken: int = 0, - ) -> ResponseT: - if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": - return cast( - ResponseT, - LegacyAPIResponse( - raw=response, - client=self, - cast_to=cast_to, - stream=stream, - stream_cls=stream_cls, - options=options, - retries_taken=retries_taken, - ), - ) - - origin = get_origin(cast_to) or cast_to - - if ( - inspect.isclass(origin) - and issubclass(origin, BaseAPIResponse) - # we only want to actually return the custom BaseAPIResponse class if we're - # returning the raw response, or if we're not streaming SSE, as if we're streaming - # SSE then `cast_to` doesn't actively reflect the type we need to parse into - and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) - ): - if not issubclass(origin, APIResponse): - raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") - - response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) - return cast( - ResponseT, - response_cls( - raw=response, - client=self, - cast_to=extract_response_type(response_cls), - stream=stream, - stream_cls=stream_cls, - options=options, - retries_taken=retries_taken, - ), - ) - - if cast_to == httpx.Response: - return cast(ResponseT, response) - - api_response = APIResponse( - raw=response, - client=self, - cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] - stream=stream, - stream_cls=stream_cls, - options=options, - retries_taken=retries_taken, - ) - if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): - return cast(ResponseT, api_response) - - return api_response.parse() - - def _request_api_list( - self, - model: Type[object], - page: Type[SyncPageT], - options: FinalRequestOptions, - ) -> SyncPageT: - def _parser(resp: SyncPageT) -> SyncPageT: - resp._set_private_attributes( - client=self, - model=model, - options=options, - ) - return resp - - options.post_parser = _parser - - return self.request(page, options, stream=False) - - @overload - def get( - self, - path: str, - *, - cast_to: Type[ResponseT], - options: RequestOptions = {}, - stream: Literal[False] = False, - ) -> ResponseT: ... - - @overload - def get( - self, - path: str, - *, - cast_to: Type[ResponseT], - options: RequestOptions = {}, - stream: Literal[True], - stream_cls: type[_StreamT], - ) -> _StreamT: ... - - @overload - def get( - self, - path: str, - *, - cast_to: Type[ResponseT], - options: RequestOptions = {}, - stream: bool, - stream_cls: type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: ... - - def get( - self, - path: str, - *, - cast_to: Type[ResponseT], - options: RequestOptions = {}, - stream: bool = False, - stream_cls: type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: - opts = FinalRequestOptions.construct(method="get", url=path, **options) - # cast is required because mypy complains about returning Any even though - # it understands the type variables - return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) - - @overload - def post( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: BinaryTypes | None = None, - options: RequestOptions = {}, - files: RequestFiles | None = None, - stream: Literal[False] = False, - ) -> ResponseT: ... - - @overload - def post( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: BinaryTypes | None = None, - options: RequestOptions = {}, - files: RequestFiles | None = None, - stream: Literal[True], - stream_cls: type[_StreamT], - ) -> _StreamT: ... - - @overload - def post( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: BinaryTypes | None = None, - options: RequestOptions = {}, - files: RequestFiles | None = None, - stream: bool, - stream_cls: type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: ... - - def post( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: BinaryTypes | None = None, - options: RequestOptions = {}, - files: RequestFiles | None = None, - stream: bool = False, - stream_cls: type[_StreamT] | None = None, - ) -> ResponseT | _StreamT: - if body is not None and content is not None: - raise TypeError("Passing both `body` and `content` is not supported") - if files is not None and content is not None: - raise TypeError("Passing both `files` and `content` is not supported") - if isinstance(body, bytes): - warnings.warn( - "Passing raw bytes as `body` is deprecated and will be removed in a future version. " - "Please pass raw bytes via the `content` parameter instead.", - DeprecationWarning, - stacklevel=2, - ) - opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options - ) - return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) - - def patch( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: BinaryTypes | None = None, - files: RequestFiles | None = None, - options: RequestOptions = {}, - ) -> ResponseT: - if body is not None and content is not None: - raise TypeError("Passing both `body` and `content` is not supported") - if files is not None and content is not None: - raise TypeError("Passing both `files` and `content` is not supported") - if isinstance(body, bytes): - warnings.warn( - "Passing raw bytes as `body` is deprecated and will be removed in a future version. " - "Please pass raw bytes via the `content` parameter instead.", - DeprecationWarning, - stacklevel=2, - ) - opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, content=content, files=to_httpx_files(files), **options - ) - return self.request(cast_to, opts) - - def put( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: BinaryTypes | None = None, - files: RequestFiles | None = None, - options: RequestOptions = {}, - ) -> ResponseT: - if body is not None and content is not None: - raise TypeError("Passing both `body` and `content` is not supported") - if files is not None and content is not None: - raise TypeError("Passing both `files` and `content` is not supported") - if isinstance(body, bytes): - warnings.warn( - "Passing raw bytes as `body` is deprecated and will be removed in a future version. " - "Please pass raw bytes via the `content` parameter instead.", - DeprecationWarning, - stacklevel=2, - ) - opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, content=content, files=to_httpx_files(files), **options - ) - return self.request(cast_to, opts) - - def delete( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: BinaryTypes | None = None, - options: RequestOptions = {}, - ) -> ResponseT: - if body is not None and content is not None: - raise TypeError("Passing both `body` and `content` is not supported") - if isinstance(body, bytes): - warnings.warn( - "Passing raw bytes as `body` is deprecated and will be removed in a future version. " - "Please pass raw bytes via the `content` parameter instead.", - DeprecationWarning, - stacklevel=2, - ) - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) - return self.request(cast_to, opts) - - def get_api_list( - self, - path: str, - *, - model: Type[object], - page: Type[SyncPageT], - body: Body | None = None, - options: RequestOptions = {}, - method: str = "get", - ) -> SyncPageT: - opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) - return self._request_api_list(model, page, opts) - - -class _DefaultAsyncHttpxClient(httpx.AsyncClient): - def __init__(self, **kwargs: Any) -> None: - kwargs.setdefault("timeout", DEFAULT_TIMEOUT) - kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) - kwargs.setdefault("follow_redirects", True) - - if "transport" not in kwargs: - socket_options: List[Tuple[int, int, Union[int, bool]]] = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True)] - - TCP_KEEPINTVL = getattr(socket, "TCP_KEEPINTVL", None) - - if TCP_KEEPINTVL is not None: - socket_options.append((socket.IPPROTO_TCP, TCP_KEEPINTVL, 60)) - elif sys.platform == "darwin": - TCP_KEEPALIVE = getattr(socket, "TCP_KEEPALIVE", 0x10) - socket_options.append((socket.IPPROTO_TCP, TCP_KEEPALIVE, 60)) - - TCP_KEEPCNT = getattr(socket, "TCP_KEEPCNT", None) - if TCP_KEEPCNT is not None: - socket_options.append((socket.IPPROTO_TCP, TCP_KEEPCNT, 5)) - - TCP_KEEPIDLE = getattr(socket, "TCP_KEEPIDLE", None) - if TCP_KEEPIDLE is not None: - socket_options.append((socket.IPPROTO_TCP, TCP_KEEPIDLE, 60)) - - proxy_map = {key: None if url is None else Proxy(url=url) for key, url in get_environment_proxies().items()} - - transport_kwargs = { - arg: kwargs[arg] for arg in ("verify", "cert", "trust_env", "http1", "http2", "limits") if arg in kwargs - } - - transport_kwargs["socket_options"] = socket_options - - proxy_mounts = { - key: None if proxy is None else AsyncHTTPTransport(proxy=proxy, **transport_kwargs) - for key, proxy in proxy_map.items() - } - default_transport = AsyncHTTPTransport(**transport_kwargs) - - # Prioritize the mounts set by the user over the environment variables. - proxy_mounts.update(kwargs.get("mounts", {})) - kwargs["mounts"] = proxy_mounts - - # Sets the default transport so that HTTPX won't automatically configure proxies. - kwargs["transport"] = default_transport - - super().__init__(**kwargs) - - -try: - import httpx_aiohttp -except ImportError: - - class _DefaultAioHttpClient(httpx.AsyncClient): - def __init__(self, **_kwargs: Any) -> None: - raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") -else: - - class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore - def __init__(self, **kwargs: Any) -> None: - kwargs.setdefault("timeout", DEFAULT_TIMEOUT) - kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) - kwargs.setdefault("follow_redirects", True) - - super().__init__(**kwargs) - - -if TYPE_CHECKING: - DefaultAsyncHttpxClient = httpx.AsyncClient - """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK - uses internally. - - This is useful because overriding the `http_client` with your own instance of - `httpx.AsyncClient` will result in httpx's defaults being used, not ours. - """ - - DefaultAioHttpClient = httpx.AsyncClient - """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" -else: - DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient - DefaultAioHttpClient = _DefaultAioHttpClient - - -class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): - def __del__(self) -> None: - if self.is_closed: - return - - try: - # TODO(someday): support non asyncio runtimes here - asyncio.get_running_loop().create_task(self.aclose()) - except Exception: - pass - - -class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]): - _client: httpx.AsyncClient - _default_stream_cls: type[AsyncStream[Any]] | None = None - - def __init__( - self, - *, - version: str, - base_url: str | URL, - _strict_response_validation: bool, - max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = not_given, - http_client: httpx.AsyncClient | None = None, - custom_headers: Mapping[str, str] | None = None, - custom_query: Mapping[str, object] | None = None, - ) -> None: - if not is_given(timeout): - # if the user passed in a custom http client with a non-default - # timeout set then we use that timeout. - # - # note: there is an edge case here where the user passes in a client - # where they've explicitly set the timeout to match the default timeout - # as this check is structural, meaning that we'll think they didn't - # pass in a timeout and will ignore it - if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: - timeout = http_client.timeout - else: - timeout = DEFAULT_TIMEOUT - - if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] - raise TypeError( - f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" - ) - - super().__init__( - version=version, - base_url=base_url, - # cast to a valid type because mypy doesn't understand our type narrowing - timeout=cast(Timeout, timeout), - max_retries=max_retries, - custom_query=custom_query, - custom_headers=custom_headers, - _strict_response_validation=_strict_response_validation, - ) - self._client = http_client or AsyncHttpxClientWrapper( - base_url=base_url, - # cast to a valid type because mypy doesn't understand our type narrowing - timeout=cast(Timeout, timeout), - ) - - def is_closed(self) -> bool: - return self._client.is_closed - - async def close(self) -> None: - """Close the underlying HTTPX client. - - The client will *not* be usable after this. - """ - await self._client.aclose() - - async def __aenter__(self: _T) -> _T: - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - await self.close() - - async def _prepare_options( - self, - options: FinalRequestOptions, # noqa: ARG002 - ) -> FinalRequestOptions: - """Hook for mutating the given options""" - return options - - async def _prepare_request( - self, - request: httpx.Request, # noqa: ARG002 - ) -> None: - """This method is used as a callback for mutating the `Request` object - after it has been constructed. - This is useful for cases where you want to add certain headers based off of - the request properties, e.g. `url`, `method` etc. - """ - return None - - @overload - async def request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: Literal[False] = False, - ) -> ResponseT: ... - - @overload - async def request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: Literal[True], - stream_cls: type[_AsyncStreamT], - ) -> _AsyncStreamT: ... - - @overload - async def request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: bool, - stream_cls: type[_AsyncStreamT] | None = None, - ) -> ResponseT | _AsyncStreamT: ... - - async def request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: bool = False, - stream_cls: type[_AsyncStreamT] | None = None, - ) -> ResponseT | _AsyncStreamT: - if self._platform is None: - # `get_platform` can make blocking IO calls so we - # execute it earlier while we are in an async context - self._platform = await asyncify(get_platform)() - - cast_to = self._maybe_override_cast_to(cast_to, options) - - # create a copy of the options we were given so that if the - # options are mutated later & we then retry, the retries are - # given the original options - input_options = model_copy(options) - if input_options.idempotency_key is None and input_options.method.lower() != "get": - # ensure the idempotency key is reused between requests - input_options.idempotency_key = self._idempotency_key() - - response: httpx.Response | None = None - max_retries = input_options.get_max_retries(self.max_retries) - - retries_taken = 0 - for retries_taken in range(max_retries + 1): - options = model_copy(input_options) - options = await self._prepare_options(options) - - remaining_retries = max_retries - retries_taken - request = self._build_request(options, retries_taken=retries_taken) - await self._prepare_request(request) - - kwargs: HttpxSendArgs = {} - if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth - - if options.follow_redirects is not None: - kwargs["follow_redirects"] = options.follow_redirects - - log.debug("Sending HTTP Request: %s %s", request.method, request.url) - - response = None - try: - response = await self._client.send( - request, - stream=stream or self._should_stream_response_body(request=request), - **kwargs, - ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) - - if remaining_retries > 0: - await self._sleep_for_retry( - retries_taken=retries_taken, - max_retries=max_retries, - options=input_options, - response=None, - ) - continue - - log.debug("Raising timeout error") - raise APITimeoutError(request=request) from err - except Exception as err: - log.debug("Encountered Exception", exc_info=True) - - if remaining_retries > 0: - await self._sleep_for_retry( - retries_taken=retries_taken, - max_retries=max_retries, - options=input_options, - response=None, - ) - continue - - log.debug("Raising connection error") - raise APIConnectionError(request=request) from err - - log.debug( - 'HTTP Response: %s %s "%i %s" %s', - request.method, - request.url, - response.status_code, - response.reason_phrase, - response.headers, - ) - log.debug("request_id: %s", response.headers.get("request-id")) - - try: - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - - if remaining_retries > 0 and self._should_retry(err.response): - await err.response.aclose() - await self._sleep_for_retry( - retries_taken=retries_taken, - max_retries=max_retries, - options=input_options, - response=response, - ) - continue - - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - await err.response.aread() - - log.debug("Re-raising status error") - raise self._make_status_error_from_response(err.response) from None - - break - - assert response is not None, "could not resolve response (should never happen)" - return await self._process_response( - cast_to=cast_to, - options=options, - response=response, - stream=stream, - stream_cls=stream_cls, - retries_taken=retries_taken, - ) - - async def _sleep_for_retry( - self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None - ) -> None: - remaining_retries = max_retries - retries_taken - if remaining_retries == 1: - log.debug("1 retry left") - else: - log.debug("%i retries left", remaining_retries) - - timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) - log.info("Retrying request to %s in %f seconds", options.url, timeout) - - await anyio.sleep(timeout) - - async def _process_response( - self, - *, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - response: httpx.Response, - stream: bool, - stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, - retries_taken: int = 0, - ) -> ResponseT: - if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": - return cast( - ResponseT, - LegacyAPIResponse( - raw=response, - client=self, - cast_to=cast_to, - stream=stream, - stream_cls=stream_cls, - options=options, - retries_taken=retries_taken, - ), - ) - - origin = get_origin(cast_to) or cast_to - - if ( - inspect.isclass(origin) - and issubclass(origin, BaseAPIResponse) - # we only want to actually return the custom BaseAPIResponse class if we're - # returning the raw response, or if we're not streaming SSE, as if we're streaming - # SSE then `cast_to` doesn't actively reflect the type we need to parse into - and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) - ): - if not issubclass(origin, AsyncAPIResponse): - raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") - - response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) - return cast( - "ResponseT", - response_cls( - raw=response, - client=self, - cast_to=extract_response_type(response_cls), - stream=stream, - stream_cls=stream_cls, - options=options, - retries_taken=retries_taken, - ), - ) - - if cast_to == httpx.Response: - return cast(ResponseT, response) - - api_response = AsyncAPIResponse( - raw=response, - client=self, - cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] - stream=stream, - stream_cls=stream_cls, - options=options, - retries_taken=retries_taken, - ) - if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): - return cast(ResponseT, api_response) - - return await api_response.parse() - - def _request_api_list( - self, - model: Type[_T], - page: Type[AsyncPageT], - options: FinalRequestOptions, - ) -> AsyncPaginator[_T, AsyncPageT]: - return AsyncPaginator(client=self, options=options, page_cls=page, model=model) - - @overload - async def get( - self, - path: str, - *, - cast_to: Type[ResponseT], - options: RequestOptions = {}, - stream: Literal[False] = False, - ) -> ResponseT: ... - - @overload - async def get( - self, - path: str, - *, - cast_to: Type[ResponseT], - options: RequestOptions = {}, - stream: Literal[True], - stream_cls: type[_AsyncStreamT], - ) -> _AsyncStreamT: ... - - @overload - async def get( - self, - path: str, - *, - cast_to: Type[ResponseT], - options: RequestOptions = {}, - stream: bool, - stream_cls: type[_AsyncStreamT] | None = None, - ) -> ResponseT | _AsyncStreamT: ... - - async def get( - self, - path: str, - *, - cast_to: Type[ResponseT], - options: RequestOptions = {}, - stream: bool = False, - stream_cls: type[_AsyncStreamT] | None = None, - ) -> ResponseT | _AsyncStreamT: - opts = FinalRequestOptions.construct(method="get", url=path, **options) - return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) - - @overload - async def post( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: AsyncBinaryTypes | None = None, - files: RequestFiles | None = None, - options: RequestOptions = {}, - stream: Literal[False] = False, - ) -> ResponseT: ... - - @overload - async def post( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: AsyncBinaryTypes | None = None, - files: RequestFiles | None = None, - options: RequestOptions = {}, - stream: Literal[True], - stream_cls: type[_AsyncStreamT], - ) -> _AsyncStreamT: ... - - @overload - async def post( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: AsyncBinaryTypes | None = None, - files: RequestFiles | None = None, - options: RequestOptions = {}, - stream: bool, - stream_cls: type[_AsyncStreamT] | None = None, - ) -> ResponseT | _AsyncStreamT: ... - - async def post( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: AsyncBinaryTypes | None = None, - files: RequestFiles | None = None, - options: RequestOptions = {}, - stream: bool = False, - stream_cls: type[_AsyncStreamT] | None = None, - ) -> ResponseT | _AsyncStreamT: - if body is not None and content is not None: - raise TypeError("Passing both `body` and `content` is not supported") - if files is not None and content is not None: - raise TypeError("Passing both `files` and `content` is not supported") - if isinstance(body, bytes): - warnings.warn( - "Passing raw bytes as `body` is deprecated and will be removed in a future version. " - "Please pass raw bytes via the `content` parameter instead.", - DeprecationWarning, - stacklevel=2, - ) - opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options - ) - return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) - - async def patch( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: AsyncBinaryTypes | None = None, - files: RequestFiles | None = None, - options: RequestOptions = {}, - ) -> ResponseT: - if body is not None and content is not None: - raise TypeError("Passing both `body` and `content` is not supported") - if files is not None and content is not None: - raise TypeError("Passing both `files` and `content` is not supported") - if isinstance(body, bytes): - warnings.warn( - "Passing raw bytes as `body` is deprecated and will be removed in a future version. " - "Please pass raw bytes via the `content` parameter instead.", - DeprecationWarning, - stacklevel=2, - ) - opts = FinalRequestOptions.construct( - method="patch", - url=path, - json_data=body, - content=content, - files=await async_to_httpx_files(files), - **options, - ) - return await self.request(cast_to, opts) - - async def put( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: AsyncBinaryTypes | None = None, - files: RequestFiles | None = None, - options: RequestOptions = {}, - ) -> ResponseT: - if body is not None and content is not None: - raise TypeError("Passing both `body` and `content` is not supported") - if files is not None and content is not None: - raise TypeError("Passing both `files` and `content` is not supported") - if isinstance(body, bytes): - warnings.warn( - "Passing raw bytes as `body` is deprecated and will be removed in a future version. " - "Please pass raw bytes via the `content` parameter instead.", - DeprecationWarning, - stacklevel=2, - ) - opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options - ) - return await self.request(cast_to, opts) - - async def delete( - self, - path: str, - *, - cast_to: Type[ResponseT], - body: Body | None = None, - content: AsyncBinaryTypes | None = None, - options: RequestOptions = {}, - ) -> ResponseT: - if body is not None and content is not None: - raise TypeError("Passing both `body` and `content` is not supported") - if isinstance(body, bytes): - warnings.warn( - "Passing raw bytes as `body` is deprecated and will be removed in a future version. " - "Please pass raw bytes via the `content` parameter instead.", - DeprecationWarning, - stacklevel=2, - ) - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) - return await self.request(cast_to, opts) - - def get_api_list( - self, - path: str, - *, - model: Type[_T], - page: Type[AsyncPageT], - body: Body | None = None, - options: RequestOptions = {}, - method: str = "get", - ) -> AsyncPaginator[_T, AsyncPageT]: - opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) - return self._request_api_list(model, page, opts) - - -def make_request_options( - *, - query: Query | None = None, - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - idempotency_key: str | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - post_parser: PostParser | NotGiven = not_given, -) -> RequestOptions: - """Create a dict of type RequestOptions without keys of NotGiven values.""" - options: RequestOptions = {} - if extra_headers is not None: - options["headers"] = extra_headers - - if extra_body is not None: - options["extra_json"] = cast(AnyMapping, extra_body) - - if query is not None: - options["params"] = query - - if extra_query is not None: - options["params"] = {**options.get("params", {}), **extra_query} - - if not isinstance(timeout, NotGiven): - options["timeout"] = timeout - - if idempotency_key is not None: - options["idempotency_key"] = idempotency_key - - if is_given(post_parser): - # internal - options["post_parser"] = post_parser # type: ignore - - return options - - -class ForceMultipartDict(Dict[str, None]): - def __bool__(self) -> bool: - return True - - -class OtherPlatform: - def __init__(self, name: str) -> None: - self.name = name - - @override - def __str__(self) -> str: - return f"Other:{self.name}" - - -Platform = Union[ - OtherPlatform, - Literal[ - "MacOS", - "Linux", - "Windows", - "FreeBSD", - "OpenBSD", - "iOS", - "Android", - "Unknown", - ], -] - - -def get_platform() -> Platform: - try: - system = platform.system().lower() - platform_name = platform.platform().lower() - except Exception: - return "Unknown" - - if "iphone" in platform_name or "ipad" in platform_name: - # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 - # system is Darwin and platform_name is a string like: - # - Darwin-21.6.0-iPhone12,1-64bit - # - Darwin-21.6.0-iPad7,11-64bit - return "iOS" - - if system == "darwin": - return "MacOS" - - if system == "windows": - return "Windows" - - if "android" in platform_name: - # Tested using Pydroid 3 - # system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc' - return "Android" - - if system == "linux": - # https://distro.readthedocs.io/en/latest/#distro.id - distro_id = distro.id() - if distro_id == "freebsd": - return "FreeBSD" - - if distro_id == "openbsd": - return "OpenBSD" - - return "Linux" - - if platform_name: - return OtherPlatform(platform_name) - - return "Unknown" - - -@lru_cache(maxsize=None) -def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: - return { - "X-Stainless-Lang": "python", - "X-Stainless-Package-Version": version, - "X-Stainless-OS": str(platform or get_platform()), - "X-Stainless-Arch": str(get_architecture()), - "X-Stainless-Runtime": get_python_runtime(), - "X-Stainless-Runtime-Version": get_python_version(), - } - - -class OtherArch: - def __init__(self, name: str) -> None: - self.name = name - - @override - def __str__(self) -> str: - return f"other:{self.name}" - - -Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] - - -def get_python_runtime() -> str: - try: - return platform.python_implementation() - except Exception: - return "unknown" - - -def get_python_version() -> str: - try: - return platform.python_version() - except Exception: - return "unknown" - - -def get_architecture() -> Arch: - try: - machine = platform.machine().lower() - except Exception: - return "unknown" - - if machine in ("arm64", "aarch64"): - return "arm64" - - # TODO: untested - if machine == "arm": - return "arm" - - if machine == "x86_64": - return "x64" - - # TODO: untested - if sys.maxsize <= 2**32: - return "x32" - - if machine: - return OtherArch(machine) - - return "unknown" - - -def _merge_mappings( - obj1: Mapping[_T_co, Union[_T, Omit]], - obj2: Mapping[_T_co, Union[_T, Omit]], -) -> Dict[_T_co, _T]: - """Merge two mappings of the same type, removing any values that are instances of `Omit`. - - In cases with duplicate keys the second mapping takes precedence. - """ - merged = {**obj1, **obj2} - return {key: value for key, value in merged.items() if not isinstance(value, Omit)} diff --git a/.venv/lib/python3.12/site-packages/anthropic/_client.py b/.venv/lib/python3.12/site-packages/anthropic/_client.py deleted file mode 100644 index accf652d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_client.py +++ /dev/null @@ -1,659 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import TYPE_CHECKING, Any, Mapping -from typing_extensions import Self, override - -import httpx - -from . import _constants, _exceptions -from ._qs import Querystring -from ._types import ( - Omit, - Headers, - Timeout, - NotGiven, - Transport, - ProxiesTypes, - RequestOptions, - not_given, -) -from ._utils import is_given, get_async_library -from ._compat import cached_property -from ._version import __version__ -from ._streaming import Stream as Stream, AsyncStream as AsyncStream -from ._exceptions import APIStatusError -from ._base_client import ( - DEFAULT_MAX_RETRIES, - SyncAPIClient, - AsyncAPIClient, -) - -if TYPE_CHECKING: - from .resources import beta, models, messages, completions - from .resources.models import Models, AsyncModels - from .resources.beta.beta import Beta, AsyncBeta - from .resources.completions import Completions, AsyncCompletions - from .resources.messages.messages import Messages, AsyncMessages - -__all__ = [ - "Timeout", - "Transport", - "ProxiesTypes", - "RequestOptions", - "Anthropic", - "AsyncAnthropic", - "Client", - "AsyncClient", -] - - -class Anthropic(SyncAPIClient): - # client options - api_key: str | None - auth_token: str | None - - # constants - HUMAN_PROMPT = _constants.HUMAN_PROMPT - AI_PROMPT = _constants.AI_PROMPT - - def __init__( - self, - *, - api_key: str | None = None, - auth_token: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = not_given, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. - # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. - # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. - http_client: httpx.Client | None = None, - # Enable or disable schema validation for data returned by the API. - # When enabled an error APIResponseValidationError is raised - # if the API responds with invalid data for the expected schema. - # - # This parameter may be removed or changed in the future. - # If you rely on this feature, please open a GitHub issue - # outlining your use-case to help us decide if it should be - # part of our public interface in the future. - _strict_response_validation: bool = False, - ) -> None: - """Construct a new synchronous Anthropic client instance. - - This automatically infers the following arguments from their corresponding environment variables if they are not provided: - - `api_key` from `ANTHROPIC_API_KEY` - - `auth_token` from `ANTHROPIC_AUTH_TOKEN` - """ - if api_key is None: - api_key = os.environ.get("ANTHROPIC_API_KEY") - self.api_key = api_key - - if auth_token is None: - auth_token = os.environ.get("ANTHROPIC_AUTH_TOKEN") - self.auth_token = auth_token - - if base_url is None: - base_url = os.environ.get("ANTHROPIC_BASE_URL") - if base_url is None: - base_url = f"https://api.anthropic.com" - - super().__init__( - version=__version__, - base_url=base_url, - max_retries=max_retries, - timeout=timeout, - http_client=http_client, - custom_headers=default_headers, - custom_query=default_query, - _strict_response_validation=_strict_response_validation, - ) - - self._default_stream_cls = Stream - - @cached_property - def completions(self) -> Completions: - from .resources.completions import Completions - - return Completions(self) - - @cached_property - def messages(self) -> Messages: - from .resources.messages import Messages - - return Messages(self) - - @cached_property - def models(self) -> Models: - from .resources.models import Models - - return Models(self) - - @cached_property - def beta(self) -> Beta: - from .resources.beta import Beta - - return Beta(self) - - @cached_property - def with_raw_response(self) -> AnthropicWithRawResponse: - return AnthropicWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AnthropicWithStreamedResponse: - return AnthropicWithStreamedResponse(self) - - @property - @override - def qs(self) -> Querystring: - return Querystring(array_format="comma") - - @property - @override - def auth_headers(self) -> dict[str, str]: - return {**self._api_key_auth, **self._bearer_auth} - - @property - def _api_key_auth(self) -> dict[str, str]: - api_key = self.api_key - if api_key is None: - return {} - return {"X-Api-Key": api_key} - - @property - def _bearer_auth(self) -> dict[str, str]: - auth_token = self.auth_token - if auth_token is None: - return {} - return {"Authorization": f"Bearer {auth_token}"} - - @property - @override - def default_headers(self) -> dict[str, str | Omit]: - return { - **super().default_headers, - "X-Stainless-Async": "false", - "anthropic-version": "2023-06-01", - **self._custom_headers, - } - - @override - def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: - if headers.get("Authorization") or headers.get("X-Api-Key"): - # valid - return - - if headers.get("X-Api-Key") or isinstance(custom_headers.get("X-Api-Key"), Omit): - return - - if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): - return - - raise TypeError( - '"Could not resolve authentication method. Expected either api_key or auth_token to be set. Or for one of the `X-Api-Key` or `Authorization` headers to be explicitly omitted"' - ) - - def copy( - self, - *, - api_key: str | None = None, - auth_token: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = not_given, - http_client: httpx.Client | None = None, - max_retries: int | NotGiven = not_given, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - """ - Create a new client instance re-using the same options given to the current client with optional overriding. - """ - if default_headers is not None and set_default_headers is not None: - raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") - - if default_query is not None and set_default_query is not None: - raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - - headers = self._custom_headers - if default_headers is not None: - headers = {**headers, **default_headers} - elif set_default_headers is not None: - headers = set_default_headers - - params = self._custom_query - if default_query is not None: - params = {**params, **default_query} - elif set_default_query is not None: - params = set_default_query - - http_client = http_client or self._client - return self.__class__( - api_key=api_key or self.api_key, - auth_token=auth_token or self.auth_token, - base_url=base_url or self.base_url, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - **_extra_kwargs, - ) - - # Alias for `copy` for nicer inline usage, e.g. - # client.with_options(timeout=10).foo.create(...) - with_options = copy - - @override - def _make_status_error( - self, - err_msg: str, - *, - body: object, - response: httpx.Response, - ) -> APIStatusError: - if response.status_code == 400: - return _exceptions.BadRequestError(err_msg, response=response, body=body) - - if response.status_code == 401: - return _exceptions.AuthenticationError(err_msg, response=response, body=body) - - if response.status_code == 403: - return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) - - if response.status_code == 404: - return _exceptions.NotFoundError(err_msg, response=response, body=body) - - if response.status_code == 409: - return _exceptions.ConflictError(err_msg, response=response, body=body) - - if response.status_code == 413: - return _exceptions.RequestTooLargeError(err_msg, response=response, body=body) - - if response.status_code == 422: - return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) - - if response.status_code == 429: - return _exceptions.RateLimitError(err_msg, response=response, body=body) - - if response.status_code == 529: - return _exceptions.OverloadedError(err_msg, response=response, body=body) - - if response.status_code >= 500: - return _exceptions.InternalServerError(err_msg, response=response, body=body) - return APIStatusError(err_msg, response=response, body=body) - - -class AsyncAnthropic(AsyncAPIClient): - # client options - api_key: str | None - auth_token: str | None - - # constants - HUMAN_PROMPT = _constants.HUMAN_PROMPT - AI_PROMPT = _constants.AI_PROMPT - - def __init__( - self, - *, - api_key: str | None = None, - auth_token: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = not_given, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. - # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. - # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. - http_client: httpx.AsyncClient | None = None, - # Enable or disable schema validation for data returned by the API. - # When enabled an error APIResponseValidationError is raised - # if the API responds with invalid data for the expected schema. - # - # This parameter may be removed or changed in the future. - # If you rely on this feature, please open a GitHub issue - # outlining your use-case to help us decide if it should be - # part of our public interface in the future. - _strict_response_validation: bool = False, - ) -> None: - """Construct a new async AsyncAnthropic client instance. - - This automatically infers the following arguments from their corresponding environment variables if they are not provided: - - `api_key` from `ANTHROPIC_API_KEY` - - `auth_token` from `ANTHROPIC_AUTH_TOKEN` - """ - if api_key is None: - api_key = os.environ.get("ANTHROPIC_API_KEY") - self.api_key = api_key - - if auth_token is None: - auth_token = os.environ.get("ANTHROPIC_AUTH_TOKEN") - self.auth_token = auth_token - - if base_url is None: - base_url = os.environ.get("ANTHROPIC_BASE_URL") - if base_url is None: - base_url = f"https://api.anthropic.com" - - super().__init__( - version=__version__, - base_url=base_url, - max_retries=max_retries, - timeout=timeout, - http_client=http_client, - custom_headers=default_headers, - custom_query=default_query, - _strict_response_validation=_strict_response_validation, - ) - - self._default_stream_cls = AsyncStream - - @cached_property - def completions(self) -> AsyncCompletions: - from .resources.completions import AsyncCompletions - - return AsyncCompletions(self) - - @cached_property - def messages(self) -> AsyncMessages: - from .resources.messages import AsyncMessages - - return AsyncMessages(self) - - @cached_property - def models(self) -> AsyncModels: - from .resources.models import AsyncModels - - return AsyncModels(self) - - @cached_property - def beta(self) -> AsyncBeta: - from .resources.beta import AsyncBeta - - return AsyncBeta(self) - - @cached_property - def with_raw_response(self) -> AsyncAnthropicWithRawResponse: - return AsyncAnthropicWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncAnthropicWithStreamedResponse: - return AsyncAnthropicWithStreamedResponse(self) - - @property - @override - def qs(self) -> Querystring: - return Querystring(array_format="comma") - - @property - @override - def auth_headers(self) -> dict[str, str]: - return {**self._api_key_auth, **self._bearer_auth} - - @property - def _api_key_auth(self) -> dict[str, str]: - api_key = self.api_key - if api_key is None: - return {} - return {"X-Api-Key": api_key} - - @property - def _bearer_auth(self) -> dict[str, str]: - auth_token = self.auth_token - if auth_token is None: - return {} - return {"Authorization": f"Bearer {auth_token}"} - - @property - @override - def default_headers(self) -> dict[str, str | Omit]: - return { - **super().default_headers, - "X-Stainless-Async": f"async:{get_async_library()}", - "anthropic-version": "2023-06-01", - **self._custom_headers, - } - - @override - def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: - if headers.get("Authorization") or headers.get("X-Api-Key"): - # valid - return - - if headers.get("X-Api-Key") or isinstance(custom_headers.get("X-Api-Key"), Omit): - return - - if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): - return - - raise TypeError( - '"Could not resolve authentication method. Expected either api_key or auth_token to be set. Or for one of the `X-Api-Key` or `Authorization` headers to be explicitly omitted"' - ) - - def copy( - self, - *, - api_key: str | None = None, - auth_token: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = not_given, - http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = not_given, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - """ - Create a new client instance re-using the same options given to the current client with optional overriding. - """ - if default_headers is not None and set_default_headers is not None: - raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") - - if default_query is not None and set_default_query is not None: - raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - - headers = self._custom_headers - if default_headers is not None: - headers = {**headers, **default_headers} - elif set_default_headers is not None: - headers = set_default_headers - - params = self._custom_query - if default_query is not None: - params = {**params, **default_query} - elif set_default_query is not None: - params = set_default_query - - http_client = http_client or self._client - return self.__class__( - api_key=api_key or self.api_key, - auth_token=auth_token or self.auth_token, - base_url=base_url or self.base_url, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - **_extra_kwargs, - ) - - # Alias for `copy` for nicer inline usage, e.g. - # client.with_options(timeout=10).foo.create(...) - with_options = copy - - @override - def _make_status_error( - self, - err_msg: str, - *, - body: object, - response: httpx.Response, - ) -> APIStatusError: - if response.status_code == 400: - return _exceptions.BadRequestError(err_msg, response=response, body=body) - - if response.status_code == 401: - return _exceptions.AuthenticationError(err_msg, response=response, body=body) - - if response.status_code == 403: - return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) - - if response.status_code == 404: - return _exceptions.NotFoundError(err_msg, response=response, body=body) - - if response.status_code == 409: - return _exceptions.ConflictError(err_msg, response=response, body=body) - - if response.status_code == 413: - return _exceptions.RequestTooLargeError(err_msg, response=response, body=body) - - if response.status_code == 422: - return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) - - if response.status_code == 429: - return _exceptions.RateLimitError(err_msg, response=response, body=body) - - if response.status_code == 529: - return _exceptions.OverloadedError(err_msg, response=response, body=body) - - if response.status_code >= 500: - return _exceptions.InternalServerError(err_msg, response=response, body=body) - return APIStatusError(err_msg, response=response, body=body) - - -class AnthropicWithRawResponse: - _client: Anthropic - - def __init__(self, client: Anthropic) -> None: - self._client = client - - @cached_property - def completions(self) -> completions.CompletionsWithRawResponse: - from .resources.completions import CompletionsWithRawResponse - - return CompletionsWithRawResponse(self._client.completions) - - @cached_property - def messages(self) -> messages.MessagesWithRawResponse: - from .resources.messages import MessagesWithRawResponse - - return MessagesWithRawResponse(self._client.messages) - - @cached_property - def models(self) -> models.ModelsWithRawResponse: - from .resources.models import ModelsWithRawResponse - - return ModelsWithRawResponse(self._client.models) - - @cached_property - def beta(self) -> beta.BetaWithRawResponse: - from .resources.beta import BetaWithRawResponse - - return BetaWithRawResponse(self._client.beta) - - -class AsyncAnthropicWithRawResponse: - _client: AsyncAnthropic - - def __init__(self, client: AsyncAnthropic) -> None: - self._client = client - - @cached_property - def completions(self) -> completions.AsyncCompletionsWithRawResponse: - from .resources.completions import AsyncCompletionsWithRawResponse - - return AsyncCompletionsWithRawResponse(self._client.completions) - - @cached_property - def messages(self) -> messages.AsyncMessagesWithRawResponse: - from .resources.messages import AsyncMessagesWithRawResponse - - return AsyncMessagesWithRawResponse(self._client.messages) - - @cached_property - def models(self) -> models.AsyncModelsWithRawResponse: - from .resources.models import AsyncModelsWithRawResponse - - return AsyncModelsWithRawResponse(self._client.models) - - @cached_property - def beta(self) -> beta.AsyncBetaWithRawResponse: - from .resources.beta import AsyncBetaWithRawResponse - - return AsyncBetaWithRawResponse(self._client.beta) - - -class AnthropicWithStreamedResponse: - _client: Anthropic - - def __init__(self, client: Anthropic) -> None: - self._client = client - - @cached_property - def completions(self) -> completions.CompletionsWithStreamingResponse: - from .resources.completions import CompletionsWithStreamingResponse - - return CompletionsWithStreamingResponse(self._client.completions) - - @cached_property - def messages(self) -> messages.MessagesWithStreamingResponse: - from .resources.messages import MessagesWithStreamingResponse - - return MessagesWithStreamingResponse(self._client.messages) - - @cached_property - def models(self) -> models.ModelsWithStreamingResponse: - from .resources.models import ModelsWithStreamingResponse - - return ModelsWithStreamingResponse(self._client.models) - - @cached_property - def beta(self) -> beta.BetaWithStreamingResponse: - from .resources.beta import BetaWithStreamingResponse - - return BetaWithStreamingResponse(self._client.beta) - - -class AsyncAnthropicWithStreamedResponse: - _client: AsyncAnthropic - - def __init__(self, client: AsyncAnthropic) -> None: - self._client = client - - @cached_property - def completions(self) -> completions.AsyncCompletionsWithStreamingResponse: - from .resources.completions import AsyncCompletionsWithStreamingResponse - - return AsyncCompletionsWithStreamingResponse(self._client.completions) - - @cached_property - def messages(self) -> messages.AsyncMessagesWithStreamingResponse: - from .resources.messages import AsyncMessagesWithStreamingResponse - - return AsyncMessagesWithStreamingResponse(self._client.messages) - - @cached_property - def models(self) -> models.AsyncModelsWithStreamingResponse: - from .resources.models import AsyncModelsWithStreamingResponse - - return AsyncModelsWithStreamingResponse(self._client.models) - - @cached_property - def beta(self) -> beta.AsyncBetaWithStreamingResponse: - from .resources.beta import AsyncBetaWithStreamingResponse - - return AsyncBetaWithStreamingResponse(self._client.beta) - - -Client = Anthropic - -AsyncClient = AsyncAnthropic diff --git a/.venv/lib/python3.12/site-packages/anthropic/_compat.py b/.venv/lib/python3.12/site-packages/anthropic/_compat.py deleted file mode 100644 index 4d7e4b03..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_compat.py +++ /dev/null @@ -1,232 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload -from datetime import date, datetime -from typing_extensions import Self, Literal, TypedDict - -import pydantic -from pydantic.fields import FieldInfo - -from ._types import IncEx, StrBytesIntFloat - -_T = TypeVar("_T") -_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) - -# --------------- Pydantic v2, v3 compatibility --------------- - -# Pyright incorrectly reports some of our functions as overriding a method when they don't -# pyright: reportIncompatibleMethodOverride=false - -PYDANTIC_V1 = pydantic.VERSION.startswith("1.") - -if TYPE_CHECKING: - - def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 - ... - - def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: # noqa: ARG001 - ... - - def get_args(t: type[Any]) -> tuple[Any, ...]: # noqa: ARG001 - ... - - def is_union(tp: type[Any] | None) -> bool: # noqa: ARG001 - ... - - def get_origin(t: type[Any]) -> type[Any] | None: # noqa: ARG001 - ... - - def is_literal_type(type_: type[Any]) -> bool: # noqa: ARG001 - ... - - def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 - ... - -else: - # v1 re-exports - if PYDANTIC_V1: - from pydantic.typing import ( - get_args as get_args, - is_union as is_union, - get_origin as get_origin, - is_typeddict as is_typeddict, - is_literal_type as is_literal_type, - ) - from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime - else: - from ._utils import ( - get_args as get_args, - is_union as is_union, - get_origin as get_origin, - parse_date as parse_date, - is_typeddict as is_typeddict, - parse_datetime as parse_datetime, - is_literal_type as is_literal_type, - ) - - -# refactored config -if TYPE_CHECKING: - from pydantic import ConfigDict as ConfigDict -else: - if PYDANTIC_V1: - # TODO: provide an error message here? - ConfigDict = None - else: - from pydantic import ConfigDict as ConfigDict - - -# renamed methods / properties -def parse_obj(model: type[_ModelT], value: object) -> _ModelT: - if PYDANTIC_V1: - return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - else: - return model.model_validate(value) - - -def field_is_required(field: FieldInfo) -> bool: - if PYDANTIC_V1: - return field.required # type: ignore - return field.is_required() - - -def field_get_default(field: FieldInfo) -> Any: - value = field.get_default() - if PYDANTIC_V1: - return value - from pydantic_core import PydanticUndefined - - if value == PydanticUndefined: - return None - return value - - -def field_outer_type(field: FieldInfo) -> Any: - if PYDANTIC_V1: - return field.outer_type_ # type: ignore - return field.annotation - - -def get_model_config(model: type[pydantic.BaseModel]) -> Any: - if PYDANTIC_V1: - return model.__config__ # type: ignore - return model.model_config - - -def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: - if PYDANTIC_V1: - return model.__fields__ # type: ignore - return model.model_fields - - -def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: - if PYDANTIC_V1: - return model.copy(deep=deep) # type: ignore - return model.model_copy(deep=deep) - - -def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: - if PYDANTIC_V1: - return model.json(indent=indent) # type: ignore - return model.model_dump_json(indent=indent) - - -def model_parse_json(model: type[_ModelT], data: str | bytes) -> _ModelT: - if PYDANTIC_V1: - return model.parse_raw(data) # pyright: ignore[reportDeprecated] - return model.model_validate_json(data) - - -class _ModelDumpKwargs(TypedDict, total=False): - by_alias: bool - - -def model_dump( - model: pydantic.BaseModel, - *, - exclude: IncEx | None = None, - exclude_unset: bool = False, - exclude_defaults: bool = False, - warnings: bool = True, - mode: Literal["json", "python"] = "python", - by_alias: bool | None = None, -) -> dict[str, Any]: - if (not PYDANTIC_V1) or hasattr(model, "model_dump"): - kwargs: _ModelDumpKwargs = {} - if by_alias is not None: - kwargs["by_alias"] = by_alias - return model.model_dump( - mode=mode, - exclude=exclude, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - # warnings are not supported in Pydantic v1 - warnings=True if PYDANTIC_V1 else warnings, - **kwargs, - ) - return cast( - "dict[str, Any]", - model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias) - ), - ) - - -def model_parse(model: type[_ModelT], data: Any) -> _ModelT: - if PYDANTIC_V1: - return model.parse_obj(data) # pyright: ignore[reportDeprecated] - return model.model_validate(data) - - -# generic models -if TYPE_CHECKING: - - class GenericModel(pydantic.BaseModel): ... - -else: - if PYDANTIC_V1: - import pydantic.generics - - class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... - else: - # there no longer needs to be a distinction in v2 but - # we still have to create our own subclass to avoid - # inconsistent MRO ordering errors - class GenericModel(pydantic.BaseModel): ... - - -# cached properties -if TYPE_CHECKING: - cached_property = property - - # we define a separate type (copied from typeshed) - # that represents that `cached_property` is `set`able - # at runtime, which differs from `@property`. - # - # this is a separate type as editors likely special case - # `@property` and we don't want to cause issues just to have - # more helpful internal types. - - class typed_cached_property(Generic[_T]): - func: Callable[[Any], _T] - attrname: str | None - - def __init__(self, func: Callable[[Any], _T]) -> None: ... - - @overload - def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... - - @overload - def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... - - def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: - raise NotImplementedError() - - def __set_name__(self, owner: type[Any], name: str) -> None: ... - - # __set__ is not defined at runtime, but @cached_property is designed to be settable - def __set__(self, instance: object, value: _T) -> None: ... -else: - from functools import cached_property as cached_property - - typed_cached_property = cached_property diff --git a/.venv/lib/python3.12/site-packages/anthropic/_constants.py b/.venv/lib/python3.12/site-packages/anthropic/_constants.py deleted file mode 100644 index 58929cb8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_constants.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import httpx - -RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" -OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" - -# default timeout is 10 minutes -DEFAULT_TIMEOUT = httpx.Timeout(timeout=10 * 60, connect=5.0) -DEFAULT_MAX_RETRIES = 2 -DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=1000, max_keepalive_connections=100) - -INITIAL_RETRY_DELAY = 0.5 -MAX_RETRY_DELAY = 8.0 - -HUMAN_PROMPT = "\n\nHuman:" - -AI_PROMPT = "\n\nAssistant:" - -MODEL_NONSTREAMING_TOKENS = { - "claude-opus-4-20250514": 8_192, - "claude-opus-4-0": 8_192, - "claude-4-opus-20250514": 8_192, - "anthropic.claude-opus-4-20250514-v1:0": 8_192, - "claude-opus-4@20250514": 8_192, - "claude-opus-4-1-20250805": 8192, - "anthropic.claude-opus-4-1-20250805-v1:0": 8192, - "claude-opus-4-1@20250805": 8192, -} diff --git a/.venv/lib/python3.12/site-packages/anthropic/_decoders/jsonl.py b/.venv/lib/python3.12/site-packages/anthropic/_decoders/jsonl.py deleted file mode 100644 index ac5ac74f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_decoders/jsonl.py +++ /dev/null @@ -1,123 +0,0 @@ -from __future__ import annotations - -import json -from typing_extensions import Generic, TypeVar, Iterator, AsyncIterator - -import httpx - -from .._models import construct_type_unchecked - -_T = TypeVar("_T") - - -class JSONLDecoder(Generic[_T]): - """A decoder for [JSON Lines](https://jsonlines.org) format. - - This class provides an iterator over a byte-iterator that parses each JSON Line - into a given type. - """ - - http_response: httpx.Response - """The HTTP response this decoder was constructed from""" - - def __init__( - self, - *, - raw_iterator: Iterator[bytes], - line_type: type[_T], - http_response: httpx.Response, - ) -> None: - super().__init__() - self.http_response = http_response - self._raw_iterator = raw_iterator - self._line_type = line_type - self._iterator = self.__decode__() - - def close(self) -> None: - """Close the response body stream. - - This is called automatically if you consume the entire stream. - """ - self.http_response.close() - - def __decode__(self) -> Iterator[_T]: - buf = b"" - for chunk in self._raw_iterator: - for line in chunk.splitlines(keepends=True): - buf += line - if buf.endswith((b"\r", b"\n", b"\r\n")): - yield construct_type_unchecked( - value=json.loads(buf), - type_=self._line_type, - ) - buf = b"" - - # flush - if buf: - yield construct_type_unchecked( - value=json.loads(buf), - type_=self._line_type, - ) - - def __next__(self) -> _T: - return self._iterator.__next__() - - def __iter__(self) -> Iterator[_T]: - for item in self._iterator: - yield item - - -class AsyncJSONLDecoder(Generic[_T]): - """A decoder for [JSON Lines](https://jsonlines.org) format. - - This class provides an async iterator over a byte-iterator that parses each JSON Line - into a given type. - """ - - http_response: httpx.Response - - def __init__( - self, - *, - raw_iterator: AsyncIterator[bytes], - line_type: type[_T], - http_response: httpx.Response, - ) -> None: - super().__init__() - self.http_response = http_response - self._raw_iterator = raw_iterator - self._line_type = line_type - self._iterator = self.__decode__() - - async def close(self) -> None: - """Close the response body stream. - - This is called automatically if you consume the entire stream. - """ - await self.http_response.aclose() - - async def __decode__(self) -> AsyncIterator[_T]: - buf = b"" - async for chunk in self._raw_iterator: - for line in chunk.splitlines(keepends=True): - buf += line - if buf.endswith((b"\r", b"\n", b"\r\n")): - yield construct_type_unchecked( - value=json.loads(buf), - type_=self._line_type, - ) - buf = b"" - - # flush - if buf: - yield construct_type_unchecked( - value=json.loads(buf), - type_=self._line_type, - ) - - async def __anext__(self) -> _T: - return await self._iterator.__anext__() - - async def __aiter__(self) -> AsyncIterator[_T]: - async for item in self._iterator: - yield item diff --git a/.venv/lib/python3.12/site-packages/anthropic/_exceptions.py b/.venv/lib/python3.12/site-packages/anthropic/_exceptions.py deleted file mode 100644 index c5b74870..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_exceptions.py +++ /dev/null @@ -1,140 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, cast -from typing_extensions import Literal - -import httpx - -from ._utils import is_dict -from .types.shared.error_type import ErrorType - -__all__ = [ - "BadRequestError", - "AuthenticationError", - "PermissionDeniedError", - "NotFoundError", - "ConflictError", - "UnprocessableEntityError", - "RateLimitError", - "InternalServerError", -] - - -class AnthropicError(Exception): - pass - - -class APIError(AnthropicError): - message: str - request: httpx.Request - - body: object | None - """The API response body. - - If the API responded with a valid JSON structure then this property will be the - decoded result. - - If it isn't a valid JSON structure then this will be the raw response. - - If there was no response associated with this error then it will be `None`. - """ - - def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None: # noqa: ARG002 - super().__init__(message) - self.request = request - self.message = message - self.body = body - - -class APIResponseValidationError(APIError): - response: httpx.Response - status_code: int - - def __init__(self, response: httpx.Response, body: object | None, *, message: str | None = None) -> None: - super().__init__(message or "Data returned by API invalid for expected schema.", response.request, body=body) - self.response = response - self.status_code = response.status_code - - -class APIStatusError(APIError): - """Raised when an API response has a status code of 4xx or 5xx.""" - - response: httpx.Response - status_code: int - request_id: str | None - type: ErrorType | None - - def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None: - super().__init__(message, response.request, body=body) - self.response = response - self.status_code = response.status_code - self.request_id = response.headers.get("request-id") - - self.type = None - if is_dict(body): - error = body.get("error") - if is_dict(error): - self.type = cast(Union[ErrorType, None], error.get("type")) - - -class APIConnectionError(APIError): - def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None: - super().__init__(message, request, body=None) - - -class APITimeoutError(APIConnectionError): - def __init__(self, request: httpx.Request) -> None: - super().__init__( - message="Request timed out or interrupted. This could be due to a network timeout, dropped connection, or request cancellation. See https://docs.anthropic.com/en/api/errors#long-requests for more details.", - request=request, - ) - - -class BadRequestError(APIStatusError): - status_code: Literal[400] = 400 # pyright: ignore[reportIncompatibleVariableOverride] - - -class AuthenticationError(APIStatusError): - status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride] - - -class PermissionDeniedError(APIStatusError): - status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride] - - -class NotFoundError(APIStatusError): - status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride] - - -class ConflictError(APIStatusError): - status_code: Literal[409] = 409 # pyright: ignore[reportIncompatibleVariableOverride] - - -class RequestTooLargeError(APIStatusError): - status_code: Literal[413] = 413 # pyright: ignore[reportIncompatibleVariableOverride] - - -class UnprocessableEntityError(APIStatusError): - status_code: Literal[422] = 422 # pyright: ignore[reportIncompatibleVariableOverride] - - -class RateLimitError(APIStatusError): - status_code: Literal[429] = 429 # pyright: ignore[reportIncompatibleVariableOverride] - - -class ServiceUnavailableError(APIStatusError): - status_code: Literal[503] = 503 # pyright: ignore[reportIncompatibleVariableOverride] - - -class OverloadedError(APIStatusError): - status_code: Literal[529] = 529 # pyright: ignore[reportIncompatibleVariableOverride] - - -class DeadlineExceededError(APIStatusError): - status_code: Literal[504] = 504 # pyright: ignore[reportIncompatibleVariableOverride] - - -class InternalServerError(APIStatusError): - pass diff --git a/.venv/lib/python3.12/site-packages/anthropic/_files.py b/.venv/lib/python3.12/site-packages/anthropic/_files.py deleted file mode 100644 index 4c74556c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_files.py +++ /dev/null @@ -1,173 +0,0 @@ -from __future__ import annotations - -import io -import os -import pathlib -from typing import Sequence, cast, overload -from typing_extensions import TypeVar, TypeGuard - -import anyio - -from ._types import ( - FileTypes, - FileContent, - RequestFiles, - HttpxFileTypes, - Base64FileInput, - HttpxFileContent, - HttpxRequestFiles, -) -from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t - -_T = TypeVar("_T") - - -def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: - return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) - - -def is_file_content(obj: object) -> TypeGuard[FileContent]: - return ( - isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) - ) - - -def assert_is_file_content(obj: object, *, key: str | None = None) -> None: - if not is_file_content(obj): - prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" - raise RuntimeError( - f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/anthropics/anthropic-sdk-python/tree/main#file-uploads" - ) from None - - -@overload -def to_httpx_files(files: None) -> None: ... - - -@overload -def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... - - -def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: - if files is None: - return None - - if is_mapping_t(files): - files = {key: _transform_file(file) for key, file in files.items()} - elif is_sequence_t(files): - files = [(key, _transform_file(file)) for key, file in files] - else: - raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") - - return files - - -def _transform_file(file: FileTypes) -> HttpxFileTypes: - if is_file_content(file): - if isinstance(file, os.PathLike): - path = pathlib.Path(file) - return (path.name, path.read_bytes()) - - return file - - if is_tuple_t(file): - return (file[0], read_file_content(file[1]), *file[2:]) - - raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") - - -def read_file_content(file: FileContent) -> HttpxFileContent: - if isinstance(file, os.PathLike): - return pathlib.Path(file).read_bytes() - return file - - -@overload -async def async_to_httpx_files(files: None) -> None: ... - - -@overload -async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... - - -async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: - if files is None: - return None - - if is_mapping_t(files): - files = {key: await _async_transform_file(file) for key, file in files.items()} - elif is_sequence_t(files): - files = [(key, await _async_transform_file(file)) for key, file in files] - else: - raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") - - return files - - -async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: - if is_file_content(file): - if isinstance(file, os.PathLike): - path = anyio.Path(file) - return (path.name, await path.read_bytes()) - - return file - - if is_tuple_t(file): - return (file[0], await async_read_file_content(file[1]), *file[2:]) - - raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") - - -async def async_read_file_content(file: FileContent) -> HttpxFileContent: - if isinstance(file, os.PathLike): - return await anyio.Path(file).read_bytes() - - return file - - -def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T: - """Copy only the containers along the given paths. - - Used to guard against mutation by extract_files without copying the entire structure. - Only dicts and lists that lie on a path are copied; everything else - is returned by reference. - - For example, given paths=[["foo", "files", "file"]] and the structure: - { - "foo": { - "bar": {"baz": {}}, - "files": {"file": } - } - } - The root dict, "foo", and "files" are copied (they lie on the path). - "bar" and "baz" are returned by reference (off the path). - """ - return _deepcopy_with_paths(item, paths, 0) - - -def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T: - if not paths: - return item - if is_mapping(item): - key_to_paths: dict[str, list[Sequence[str]]] = {} - for path in paths: - if index < len(path): - key_to_paths.setdefault(path[index], []).append(path) - - # if no path continues through this mapping, it won't be mutated and copying it is redundant - if not key_to_paths: - return item - - result = dict(item) - for key, subpaths in key_to_paths.items(): - if key in result: - result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1) - return cast(_T, result) - if is_list(item): - array_paths = [path for path in paths if index < len(path) and path[index] == ""] - - # if no path expects a list here, nothing will be mutated inside it - return by reference - if not array_paths: - return cast(_T, item) - return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item]) - return item diff --git a/.venv/lib/python3.12/site-packages/anthropic/_legacy_response.py b/.venv/lib/python3.12/site-packages/anthropic/_legacy_response.py deleted file mode 100644 index 9f93da7c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_legacy_response.py +++ /dev/null @@ -1,514 +0,0 @@ -from __future__ import annotations - -import os -import inspect -import logging -import datetime -import functools -from typing import ( - TYPE_CHECKING, - Any, - Union, - Generic, - TypeVar, - Callable, - Iterator, - AsyncIterator, - cast, - overload, -) -from typing_extensions import Awaitable, ParamSpec, override, deprecated, get_origin - -import anyio -import httpx -import pydantic - -from ._types import NoneType -from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type -from ._models import BaseModel, is_basemodel, add_request_id -from ._constants import RAW_RESPONSE_HEADER -from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type -from ._exceptions import APIResponseValidationError -from ._decoders.jsonl import JSONLDecoder, AsyncJSONLDecoder - -if TYPE_CHECKING: - from ._models import FinalRequestOptions - from ._base_client import BaseClient - - -P = ParamSpec("P") -R = TypeVar("R") -_T = TypeVar("_T") -_T_co = TypeVar("_T_co", covariant=True) - -log: logging.Logger = logging.getLogger(__name__) - - -class LegacyAPIResponse(Generic[R]): - """This is a legacy class as it will be replaced by `APIResponse` - and `AsyncAPIResponse` in the `_response.py` file in the next major - release. - - For the sync client this will mostly be the same with the exception - of `content` & `text` will be methods instead of properties. In the - async client, all methods will be async. - - A migration script will be provided & the migration in general should - be smooth. - """ - - _cast_to: type[R] - _client: BaseClient[Any, Any] - _parsed_by_type: dict[type[Any], Any] - _stream: bool - _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None - _options: FinalRequestOptions - - http_response: httpx.Response - - retries_taken: int - """The number of retries made. If no retries happened this will be `0`""" - - def __init__( - self, - *, - raw: httpx.Response, - cast_to: type[R], - client: BaseClient[Any, Any], - stream: bool, - stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, - options: FinalRequestOptions, - retries_taken: int = 0, - ) -> None: - self._cast_to = cast_to - self._client = client - self._parsed_by_type = {} - self._stream = stream - self._stream_cls = stream_cls - self._options = options - self.http_response = raw - self.retries_taken = retries_taken - - @property - def request_id(self) -> str | None: - return self.http_response.headers.get("request-id") # type: ignore[no-any-return] - - @overload - def parse(self, *, to: type[_T]) -> _T: ... - - @overload - def parse(self) -> R: ... - - def parse(self, *, to: type[_T] | None = None) -> R | _T: - """Returns the rich python representation of this response's data. - - NOTE: For the async client: this will become a coroutine in the next major version. - - For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. - - You can customise the type that the response is parsed into through - the `to` argument, e.g. - - ```py - from anthropic import BaseModel - - - class MyModel(BaseModel): - foo: str - - - obj = response.parse(to=MyModel) - print(obj.foo) - ``` - - We support parsing: - - `BaseModel` - - `dict` - - `list` - - `Union` - - `str` - - `int` - - `float` - - `httpx.Response` - """ - cache_key = to if to is not None else self._cast_to - cached = self._parsed_by_type.get(cache_key) - if cached is not None: - return cached # type: ignore[no-any-return] - - parsed = self._parse(to=to) - if is_given(self._options.post_parser): - parsed = self._options.post_parser(parsed) - - if isinstance(parsed, BaseModel): - add_request_id(parsed, self.request_id) - - self._parsed_by_type[cache_key] = parsed - return cast(R, parsed) - - @property - def headers(self) -> httpx.Headers: - return self.http_response.headers - - @property - def http_request(self) -> httpx.Request: - return self.http_response.request - - @property - def status_code(self) -> int: - return self.http_response.status_code - - @property - def url(self) -> httpx.URL: - return self.http_response.url - - @property - def method(self) -> str: - return self.http_request.method - - @property - def content(self) -> bytes: - """Return the binary response content. - - NOTE: this will be removed in favour of `.read()` in the - next major version. - """ - return self.http_response.content - - @property - def text(self) -> str: - """Return the decoded response content. - - NOTE: this will be turned into a method in the next major version. - """ - return self.http_response.text - - @property - def http_version(self) -> str: - return self.http_response.http_version - - @property - def is_closed(self) -> bool: - return self.http_response.is_closed - - @property - def elapsed(self) -> datetime.timedelta: - """The time taken for the complete request/response cycle to complete.""" - return self.http_response.elapsed - - def _parse(self, *, to: type[_T] | None = None) -> R | _T: - cast_to = to if to is not None else self._cast_to - - # unwrap `TypeAlias('Name', T)` -> `T` - if is_type_alias_type(cast_to): - cast_to = cast_to.__value__ # type: ignore[unreachable] - - # unwrap `Annotated[T, ...]` -> `T` - if cast_to and is_annotated_type(cast_to): - cast_to = extract_type_arg(cast_to, 0) - - origin = get_origin(cast_to) or cast_to - - if inspect.isclass(origin): - if issubclass(cast(Any, origin), JSONLDecoder): - return cast( - R, - cast("type[JSONLDecoder[Any]]", cast_to)( - raw_iterator=self.http_response.iter_bytes(chunk_size=64), - line_type=extract_type_arg(cast_to, 0), - http_response=self.http_response, - ), - ) - - if issubclass(cast(Any, origin), AsyncJSONLDecoder): - return cast( - R, - cast("type[AsyncJSONLDecoder[Any]]", cast_to)( - raw_iterator=self.http_response.aiter_bytes(chunk_size=64), - line_type=extract_type_arg(cast_to, 0), - http_response=self.http_response, - ), - ) - - if self._stream: - if to: - if not is_stream_class_type(to): - raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") - - return cast( - _T, - to( - cast_to=extract_stream_chunk_type( - to, - failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", - ), - response=self.http_response, - client=cast(Any, self._client), - options=self._options, - ), - ) - - if self._stream_cls: - return cast( - R, - self._stream_cls( - cast_to=extract_stream_chunk_type(self._stream_cls), - response=self.http_response, - client=cast(Any, self._client), - options=self._options, - ), - ) - - stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) - if stream_cls is None: - raise MissingStreamClassError() - - return cast( - R, - stream_cls( - cast_to=cast_to, - response=self.http_response, - client=cast(Any, self._client), - options=self._options, - ), - ) - - if cast_to is NoneType: - return cast(R, None) - - response = self.http_response - if cast_to == str: - return cast(R, response.text) - - if cast_to == int: - return cast(R, int(response.text)) - - if cast_to == float: - return cast(R, float(response.text)) - - if cast_to == bool: - return cast(R, response.text.lower() == "true") - - if inspect.isclass(origin) and issubclass(origin, HttpxBinaryResponseContent): - return cast(R, cast_to(response)) # type: ignore - - if origin == LegacyAPIResponse: - raise RuntimeError("Unexpected state - cast_to is `APIResponse`") - - if inspect.isclass( - origin # pyright: ignore[reportUnknownArgumentType] - ) and issubclass(origin, httpx.Response): - # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response - # and pass that class to our request functions. We cannot change the variance to be either - # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct - # the response class ourselves but that is something that should be supported directly in httpx - # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. - if cast_to != httpx.Response: - raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") - return cast(R, response) - - if ( - inspect.isclass( - origin # pyright: ignore[reportUnknownArgumentType] - ) - and not issubclass(origin, BaseModel) - and issubclass(origin, pydantic.BaseModel) - ): - raise TypeError("Pydantic models must subclass our base model type, e.g. `from anthropic import BaseModel`") - - if ( - cast_to is not object - and not origin is list - and not origin is dict - and not origin is Union - and not issubclass(origin, BaseModel) - ): - raise RuntimeError( - f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." - ) - - # split is required to handle cases where additional information is included - # in the response, e.g. application/json; charset=utf-8 - content_type, *_ = response.headers.get("content-type", "*").split(";") - if not content_type.endswith("json"): - if is_basemodel(cast_to): - try: - data = response.json() - except Exception as exc: - log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) - else: - return self._client._process_response_data( - data=data, - cast_to=cast_to, # type: ignore - response=response, - ) - - if self._client._strict_response_validation: - raise APIResponseValidationError( - response=response, - message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", - body=response.text, - ) - - # If the API responds with content that isn't JSON then we just return - # the (decoded) text without performing any parsing so that you can still - # handle the response however you need to. - return response.text # type: ignore - - data = response.json() - - return self._client._process_response_data( - data=data, - cast_to=cast_to, # type: ignore - response=response, - ) - - @override - def __repr__(self) -> str: - return f"" - - -class MissingStreamClassError(TypeError): - def __init__(self) -> None: - super().__init__( - "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `anthropic._streaming` for reference", - ) - - -def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, LegacyAPIResponse[R]]: - """Higher order function that takes one of our bound API methods and wraps it - to support returning the raw `APIResponse` object directly. - """ - - @functools.wraps(func) - def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: - extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "true" - - kwargs["extra_headers"] = extra_headers - - return cast(LegacyAPIResponse[R], func(*args, **kwargs)) - - return wrapped - - -def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[LegacyAPIResponse[R]]]: - """Higher order function that takes one of our bound API methods and wraps it - to support returning the raw `APIResponse` object directly. - """ - - @functools.wraps(func) - async def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: - extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "true" - - kwargs["extra_headers"] = extra_headers - - return cast(LegacyAPIResponse[R], await func(*args, **kwargs)) - - return wrapped - - -class HttpxBinaryResponseContent: - response: httpx.Response - - def __init__(self, response: httpx.Response) -> None: - self.response = response - - @property - def content(self) -> bytes: - return self.response.content - - @property - def text(self) -> str: - return self.response.text - - @property - def encoding(self) -> str | None: - return self.response.encoding - - @property - def charset_encoding(self) -> str | None: - return self.response.charset_encoding - - def json(self, **kwargs: Any) -> Any: - return self.response.json(**kwargs) - - def read(self) -> bytes: - return self.response.read() - - def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: - return self.response.iter_bytes(chunk_size) - - def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: - return self.response.iter_text(chunk_size) - - def iter_lines(self) -> Iterator[str]: - return self.response.iter_lines() - - def iter_raw(self, chunk_size: int | None = None) -> Iterator[bytes]: - return self.response.iter_raw(chunk_size) - - def write_to_file( - self, - file: str | os.PathLike[str], - ) -> None: - """Write the output to the given file. - - Accepts a filename or any path-like object, e.g. pathlib.Path - - Note: if you want to stream the data to the file instead of writing - all at once then you should use `.with_streaming_response` when making - the API request, e.g. `client.with_streaming_response.foo().stream_to_file('my_filename.txt')` - """ - with open(file, mode="wb") as f: - for data in self.response.iter_bytes(): - f.write(data) - - @deprecated( - "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead" - ) - def stream_to_file( - self, - file: str | os.PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - with open(file, mode="wb") as f: - for data in self.response.iter_bytes(chunk_size): - f.write(data) - - def close(self) -> None: - return self.response.close() - - async def aread(self) -> bytes: - return await self.response.aread() - - async def aiter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: - return self.response.aiter_bytes(chunk_size) - - async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: - return self.response.aiter_text(chunk_size) - - async def aiter_lines(self) -> AsyncIterator[str]: - return self.response.aiter_lines() - - async def aiter_raw(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: - return self.response.aiter_raw(chunk_size) - - @deprecated( - "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead" - ) - async def astream_to_file( - self, - file: str | os.PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - path = anyio.Path(file) - async with await path.open(mode="wb") as f: - async for data in self.response.aiter_bytes(chunk_size): - await f.write(data) - - async def aclose(self) -> None: - return await self.response.aclose() diff --git a/.venv/lib/python3.12/site-packages/anthropic/_models.py b/.venv/lib/python3.12/site-packages/anthropic/_models.py deleted file mode 100644 index 2d979355..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_models.py +++ /dev/null @@ -1,918 +0,0 @@ -from __future__ import annotations - -import os -import inspect -import weakref -from typing import ( - IO, - TYPE_CHECKING, - Any, - Type, - Union, - Generic, - TypeVar, - Callable, - Iterable, - Optional, - AsyncIterable, - cast, -) -from datetime import date, datetime -from typing_extensions import ( - List, - Unpack, - Literal, - ClassVar, - Protocol, - Required, - ParamSpec, - TypedDict, - TypeGuard, - final, - override, - runtime_checkable, -) - -import pydantic -from pydantic.fields import FieldInfo - -from ._types import ( - Body, - IncEx, - Query, - ModelT, - Headers, - Timeout, - NotGiven, - AnyMapping, - HttpxRequestFiles, -) -from ._utils import ( - PropertyInfo, - is_list, - is_given, - json_safe, - lru_cache, - is_mapping, - parse_date, - coerce_boolean, - parse_datetime, - strip_not_given, - extract_type_arg, - is_annotated_type, - is_type_alias_type, - strip_annotated_type, -) -from ._compat import ( - PYDANTIC_V1, - ConfigDict, - GenericModel as BaseGenericModel, - get_args, - is_union, - parse_obj, - get_origin, - is_literal_type, - get_model_config, - get_model_fields, - field_get_default, -) -from ._constants import RAW_RESPONSE_HEADER - -if TYPE_CHECKING: - from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema - -__all__ = ["BaseModel", "GenericModel"] - -_T = TypeVar("_T") -_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") - -P = ParamSpec("P") - - -@runtime_checkable -class _ConfigProtocol(Protocol): - allow_population_by_field_name: bool - - -class BaseModel(pydantic.BaseModel): - if PYDANTIC_V1: - - @property - @override - def model_fields_set(self) -> set[str]: - # a forwards-compat shim for pydantic v2 - return self.__fields_set__ # type: ignore - - class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] - extra: Any = pydantic.Extra.allow # type: ignore - else: - model_config: ClassVar[ConfigDict] = ConfigDict( - extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) - ) - - if TYPE_CHECKING: - _request_id: Optional[str] = None - """The ID of the request, returned via the `request-id` header. Useful for debugging requests and reporting issues to Anthropic. - This will **only** be set for the top-level response object, it will not be defined for nested objects. For example: - - ```py - message = await client.messages.create(...) - message._request_id # req_xxx - message.usage._request_id # raises `AttributeError` - ``` - - Note: unlike other properties that use an `_` prefix, this property - *is* public. Unless documented otherwise, all other `_` prefix properties, - methods and modules are *private*. - """ - - def to_dict( - self, - *, - mode: Literal["json", "python"] = "python", - use_api_names: bool = True, - exclude_unset: bool = True, - exclude_defaults: bool = False, - exclude_none: bool = False, - warnings: bool = True, - ) -> dict[str, object]: - """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. - - By default, fields that were not set by the API will not be included, - and keys will match the API response, *not* the property names from the model. - - For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, - the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). - - Args: - mode: - If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. - If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` - - use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. - exclude_unset: Whether to exclude fields that have not been explicitly set. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. - """ - return self.model_dump( - mode=mode, - by_alias=use_api_names, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - warnings=warnings, - ) - - def to_json( - self, - *, - indent: int | None = 2, - use_api_names: bool = True, - exclude_unset: bool = True, - exclude_defaults: bool = False, - exclude_none: bool = False, - warnings: bool = True, - ) -> str: - """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). - - By default, fields that were not set by the API will not be included, - and keys will match the API response, *not* the property names from the model. - - For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, - the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). - - Args: - indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` - use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. - exclude_unset: Whether to exclude fields that have not been explicitly set. - exclude_defaults: Whether to exclude fields that have the default value. - exclude_none: Whether to exclude fields that have a value of `None`. - warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. - """ - return self.model_dump_json( - indent=indent, - by_alias=use_api_names, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - warnings=warnings, - ) - - @override - def __str__(self) -> str: - # mypy complains about an invalid self arg - return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc] - - # Override the 'construct' method in a way that supports recursive parsing without validation. - # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. - @classmethod - @override - def construct( # pyright: ignore[reportIncompatibleMethodOverride] - __cls: Type[ModelT], - _fields_set: set[str] | None = None, - **values: object, - ) -> ModelT: - m = __cls.__new__(__cls) - fields_values: dict[str, object] = {} - - config = get_model_config(__cls) - populate_by_name = ( - config.allow_population_by_field_name - if isinstance(config, _ConfigProtocol) - else config.get("populate_by_name") - ) - - if _fields_set is None: - _fields_set = set() - - model_fields = get_model_fields(__cls) - for name, field in model_fields.items(): - key = field.alias - if key is None or (key not in values and populate_by_name): - key = name - - if key in values: - fields_values[name] = _construct_field(value=values[key], field=field, key=key) - _fields_set.add(name) - else: - fields_values[name] = field_get_default(field) - - extra_field_type = _get_extra_fields_type(__cls) - - _extra = {} - for key, value in values.items(): - if key not in model_fields: - parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value - - if PYDANTIC_V1: - _fields_set.add(key) - fields_values[key] = parsed - else: - _extra[key] = parsed - - object.__setattr__(m, "__dict__", fields_values) - - if PYDANTIC_V1: - # init_private_attributes() does not exist in v2 - m._init_private_attributes() # type: ignore - - # copied from Pydantic v1's `construct()` method - object.__setattr__(m, "__fields_set__", _fields_set) - else: - # these properties are copied from Pydantic's `model_construct()` method - object.__setattr__(m, "__pydantic_private__", None) - object.__setattr__(m, "__pydantic_extra__", _extra) - object.__setattr__(m, "__pydantic_fields_set__", _fields_set) - - return m - - if not TYPE_CHECKING: - # type checkers incorrectly complain about this assignment - # because the type signatures are technically different - # although not in practice - model_construct = construct - - if PYDANTIC_V1: - # we define aliases for some of the new pydantic v2 methods so - # that we can just document these methods without having to specify - # a specific pydantic version as some users may not know which - # pydantic version they are currently using - - @override - def model_dump( - self, - *, - mode: Literal["json", "python"] | str = "python", - include: IncEx | None = None, - exclude: IncEx | None = None, - context: Any | None = None, - by_alias: bool | None = None, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - exclude_computed_fields: bool = False, - round_trip: bool = False, - warnings: bool | Literal["none", "warn", "error"] = True, - fallback: Callable[[Any], Any] | None = None, - serialize_as_any: bool = False, - ) -> dict[str, Any]: - """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump - - Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. - - Args: - mode: The mode in which `to_python` should run. - If mode is 'json', the output will only contain JSON serializable types. - If mode is 'python', the output may contain non-JSON-serializable Python objects. - include: A set of fields to include in the output. - exclude: A set of fields to exclude from the output. - context: Additional context to pass to the serializer. - by_alias: Whether to use the field's alias in the dictionary key if defined. - exclude_unset: Whether to exclude fields that have not been explicitly set. - exclude_defaults: Whether to exclude fields that are set to their default value. - exclude_none: Whether to exclude fields that have a value of `None`. - exclude_computed_fields: Whether to exclude computed fields. - While this can be useful for round-tripping, it is usually recommended to use the dedicated - `round_trip` parameter instead. - round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. - warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, - "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. - fallback: A function to call when an unknown value is encountered. If not provided, - a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. - serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. - - Returns: - A dictionary representation of the model. - """ - if mode not in {"json", "python"}: - raise ValueError("mode must be either 'json' or 'python'") - if round_trip != False: - raise ValueError("round_trip is only supported in Pydantic v2") - if warnings != True: - raise ValueError("warnings is only supported in Pydantic v2") - if context is not None: - raise ValueError("context is only supported in Pydantic v2") - if serialize_as_any != False: - raise ValueError("serialize_as_any is only supported in Pydantic v2") - if fallback is not None: - raise ValueError("fallback is only supported in Pydantic v2") - if exclude_computed_fields != False: - raise ValueError("exclude_computed_fields is only supported in Pydantic v2") - dumped = super().dict( # pyright: ignore[reportDeprecated] - include=include, - exclude=exclude, - by_alias=by_alias if by_alias is not None else False, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - - return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped - - @override - def model_dump_json( - self, - *, - indent: int | None = None, - ensure_ascii: bool = False, - include: IncEx | None = None, - exclude: IncEx | None = None, - context: Any | None = None, - by_alias: bool | None = None, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - exclude_computed_fields: bool = False, - round_trip: bool = False, - warnings: bool | Literal["none", "warn", "error"] = True, - fallback: Callable[[Any], Any] | None = None, - serialize_as_any: bool = False, - ) -> str: - """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json - - Generates a JSON representation of the model using Pydantic's `to_json` method. - - Args: - indent: Indentation to use in the JSON output. If None is passed, the output will be compact. - include: Field(s) to include in the JSON output. Can take either a string or set of strings. - exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings. - by_alias: Whether to serialize using field aliases. - exclude_unset: Whether to exclude fields that have not been explicitly set. - exclude_defaults: Whether to exclude fields that have the default value. - exclude_none: Whether to exclude fields that have a value of `None`. - round_trip: Whether to use serialization/deserialization between JSON and class instance. - warnings: Whether to show any warnings that occurred during serialization. - - Returns: - A JSON string representation of the model. - """ - if round_trip != False: - raise ValueError("round_trip is only supported in Pydantic v2") - if warnings != True: - raise ValueError("warnings is only supported in Pydantic v2") - if context is not None: - raise ValueError("context is only supported in Pydantic v2") - if serialize_as_any != False: - raise ValueError("serialize_as_any is only supported in Pydantic v2") - if fallback is not None: - raise ValueError("fallback is only supported in Pydantic v2") - if ensure_ascii != False: - raise ValueError("ensure_ascii is only supported in Pydantic v2") - if exclude_computed_fields != False: - raise ValueError("exclude_computed_fields is only supported in Pydantic v2") - return super().json( # type: ignore[reportDeprecated] - indent=indent, - include=include, - exclude=exclude, - by_alias=by_alias if by_alias is not None else False, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - - -def _construct_field(value: object, field: FieldInfo, key: str) -> object: - if value is None: - return field_get_default(field) - - if PYDANTIC_V1: - type_ = cast(type, field.outer_type_) # type: ignore - else: - type_ = field.annotation # type: ignore - - if type_ is None: - raise RuntimeError(f"Unexpected field type is None for {key}") - - return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) - - -def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: - if PYDANTIC_V1: - # TODO - return None - - schema = cls.__pydantic_core_schema__ - if schema["type"] == "model": - fields = schema["schema"] - if fields["type"] == "model-fields": - extras = fields.get("extras_schema") - if extras and "cls" in extras: - # mypy can't narrow the type - return extras["cls"] # type: ignore[no-any-return] - - return None - - -def is_basemodel(type_: type) -> bool: - """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" - if is_union(type_): - for variant in get_args(type_): - if is_basemodel(variant): - return True - - return False - - return is_basemodel_type(type_) - - -def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: - origin = get_origin(type_) or type_ - if not inspect.isclass(origin): - return False - return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) - - -def build( - base_model_cls: Callable[P, _BaseModelT], - *args: P.args, - **kwargs: P.kwargs, -) -> _BaseModelT: - """Construct a BaseModel class without validation. - - This is useful for cases where you need to instantiate a `BaseModel` - from an API response as this provides type-safe params which isn't supported - by helpers like `construct_type()`. - - ```py - build(MyModel, my_field_a="foo", my_field_b=123) - ``` - """ - if args: - raise TypeError( - "Received positional arguments which are not supported; Keyword arguments must be used instead", - ) - - return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) - - -def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: - """Loose coercion to the expected type with construction of nested values. - - Note: the returned value from this function is not guaranteed to match the - given type. - """ - return cast(_T, construct_type(value=value, type_=type_)) - - -def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object: - """Loose coercion to the expected type with construction of nested values. - - If the given value does not match the expected type then it is returned as-is. - """ - - # store a reference to the original type we were given before we extract any inner - # types so that we can properly resolve forward references in `TypeAliasType` annotations - original_type = None - - # we allow `object` as the input type because otherwise, passing things like - # `Literal['value']` will be reported as a type error by type checkers - type_ = cast("type[object]", type_) - if is_type_alias_type(type_): - original_type = type_ # type: ignore[unreachable] - type_ = type_.__value__ # type: ignore[unreachable] - - # unwrap `Annotated[T, ...]` -> `T` - if metadata is not None and len(metadata) > 0: - meta: tuple[Any, ...] = tuple(metadata) - elif is_annotated_type(type_): - meta = get_args(type_)[1:] - type_ = extract_type_arg(type_, 0) - else: - meta = tuple() - - # we need to use the origin class for any types that are subscripted generics - # e.g. Dict[str, object] - origin = get_origin(type_) or type_ - args = get_args(type_) - - if is_union(origin): - try: - return validate_type(type_=cast("type[object]", original_type or type_), value=value) - except Exception: - pass - - # if the type is a discriminated union then we want to construct the right variant - # in the union, even if the data doesn't match exactly, otherwise we'd break code - # that relies on the constructed class types, e.g. - # - # class FooType: - # kind: Literal['foo'] - # value: str - # - # class BarType: - # kind: Literal['bar'] - # value: int - # - # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then - # we'd end up constructing `FooType` when it should be `BarType`. - discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) - if discriminator and is_mapping(value): - variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) - if variant_value and isinstance(variant_value, str): - variant_type = discriminator.mapping.get(variant_value) - if variant_type: - return construct_type(type_=variant_type, value=value) - - # if the data is not valid, use the first variant that doesn't fail while deserializing - for variant in args: - try: - return construct_type(value=value, type_=variant) - except Exception: - continue - - raise RuntimeError(f"Could not convert data into a valid instance of {type_}") - - if origin == dict: - if not is_mapping(value): - return value - - _, items_type = get_args(type_) # Dict[_, items_type] - return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} - - if ( - not is_literal_type(type_) - and inspect.isclass(origin) - and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) - ): - if is_list(value): - return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] - - if is_mapping(value): - if issubclass(type_, BaseModel): - return type_.construct(**value) # type: ignore[arg-type] - - return cast(Any, type_).construct(**value) - - if origin == list: - if not is_list(value): - return value - - inner_type = args[0] # List[inner_type] - return [construct_type(value=entry, type_=inner_type) for entry in value] - - if origin == float: - if isinstance(value, int): - coerced = float(value) - if coerced != value: - return value - return coerced - - return value - - if type_ == datetime: - try: - return parse_datetime(value) # type: ignore - except Exception: - return value - - if type_ == date: - try: - return parse_date(value) # type: ignore - except Exception: - return value - - return value - - -@runtime_checkable -class CachedDiscriminatorType(Protocol): - __discriminator__: DiscriminatorDetails - - -DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() - - -class DiscriminatorDetails: - field_name: str - """The name of the discriminator field in the variant class, e.g. - - ```py - class Foo(BaseModel): - type: Literal['foo'] - ``` - - Will result in field_name='type' - """ - - field_alias_from: str | None - """The name of the discriminator field in the API response, e.g. - - ```py - class Foo(BaseModel): - type: Literal['foo'] = Field(alias='type_from_api') - ``` - - Will result in field_alias_from='type_from_api' - """ - - mapping: dict[str, type] - """Mapping of discriminator value to variant type, e.g. - - {'foo': FooVariant, 'bar': BarVariant} - """ - - def __init__( - self, - *, - mapping: dict[str, type], - discriminator_field: str, - discriminator_alias: str | None, - ) -> None: - self.mapping = mapping - self.field_name = discriminator_field - self.field_alias_from = discriminator_alias - - -def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: - cached = DISCRIMINATOR_CACHE.get(union) - if cached is not None: - return cached - - discriminator_field_name: str | None = None - - for annotation in meta_annotations: - if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: - discriminator_field_name = annotation.discriminator - break - - if not discriminator_field_name: - return None - - mapping: dict[str, type] = {} - discriminator_alias: str | None = None - - for variant in get_args(union): - variant = strip_annotated_type(variant) - if is_basemodel_type(variant): - if PYDANTIC_V1: - field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - if not field_info: - continue - - # Note: if one variant defines an alias then they all should - discriminator_alias = field_info.alias - - if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): - for entry in get_args(annotation): - if isinstance(entry, str): - mapping[entry] = variant - else: - field = _extract_field_schema_pv2(variant, discriminator_field_name) - if not field: - continue - - # Note: if one variant defines an alias then they all should - discriminator_alias = field.get("serialization_alias") - - field_schema = field["schema"] - - if field_schema["type"] == "literal": - for entry in cast("LiteralSchema", field_schema)["expected"]: - if isinstance(entry, str): - mapping[entry] = variant - - if not mapping: - return None - - details = DiscriminatorDetails( - mapping=mapping, - discriminator_field=discriminator_field_name, - discriminator_alias=discriminator_alias, - ) - DISCRIMINATOR_CACHE.setdefault(union, details) - return details - - -def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: - schema = model.__pydantic_core_schema__ - if schema["type"] == "definitions": - schema = schema["schema"] - - if schema["type"] != "model": - return None - - schema = cast("ModelSchema", schema) - fields_schema = schema["schema"] - if fields_schema["type"] != "model-fields": - return None - - fields_schema = cast("ModelFieldsSchema", fields_schema) - field = fields_schema["fields"].get(field_name) - if not field: - return None - - return cast("ModelField", field) # pyright: ignore[reportUnnecessaryCast] - - -def validate_type(*, type_: type[_T], value: object) -> _T: - """Strict validation that the given value matches the expected type""" - if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): - return cast(_T, parse_obj(type_, value)) - - return cast(_T, _validate_non_model_type(type_=type_, value=value)) - - -def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: - """Add a pydantic config for the given type. - - Note: this is a no-op on Pydantic v1. - """ - setattr(typ, "__pydantic_config__", config) # noqa: B010 - - -def add_request_id(obj: BaseModel, request_id: str | None) -> None: - obj._request_id = request_id - - # in Pydantic v1, using setattr like we do above causes the attribute - # to be included when serializing the model which we don't want in this - # case so we need to explicitly exclude it - if PYDANTIC_V1: - try: - exclude_fields = obj.__exclude_fields__ # type: ignore - except AttributeError: - cast(Any, obj).__exclude_fields__ = {"_request_id", "__exclude_fields__"} - else: - cast(Any, obj).__exclude_fields__ = {*(exclude_fields or {}), "_request_id", "__exclude_fields__"} - - -# our use of subclassing here causes weirdness for type checkers, -# so we just pretend that we don't subclass -if TYPE_CHECKING: - GenericModel = BaseModel -else: - - class GenericModel(BaseGenericModel, BaseModel): - pass - - -if not PYDANTIC_V1: - from pydantic import TypeAdapter as _TypeAdapter, computed_field as computed_field - - _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) - - if TYPE_CHECKING: - from pydantic import TypeAdapter - else: - TypeAdapter = _CachedTypeAdapter - - def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: - return TypeAdapter(type_).validate_python(value) - -elif not TYPE_CHECKING: # TODO: condition is weird - - class RootModel(GenericModel, Generic[_T]): - """Used as a placeholder to easily convert runtime types to a Pydantic format - to provide validation. - - For example: - ```py - validated = RootModel[int](__root__="5").__root__ - # validated: 5 - ``` - """ - - __root__: _T - - def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: - model = _create_pydantic_model(type_).validate(value) - return cast(_T, model.__root__) - - def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]: - return RootModel[type_] # type: ignore - - def TypeAdapter(*_args: Any, **_kwargs: Any) -> Any: - raise RuntimeError("attempted to use TypeAdapter in pydantic v1") - - def computed_field(func: Any | None = None, /, **__: Any) -> Any: - def _exc_func(*_: Any, **__: Any) -> Any: - raise RuntimeError("attempted to use computed_field in pydantic v1") - - def _dec(*_: Any, **__: Any) -> Any: - return _exc_func - - if func is not None: - return _dec(func) - else: - return _dec - - -class FinalRequestOptionsInput(TypedDict, total=False): - method: Required[str] - url: Required[str] - params: Query - headers: Headers - max_retries: int - timeout: float | Timeout | None - files: HttpxRequestFiles | None - idempotency_key: str - content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] - json_data: Body - extra_json: AnyMapping - follow_redirects: bool - - -@final -class FinalRequestOptions(pydantic.BaseModel): - method: str - url: str - params: Query = {} - headers: Union[Headers, NotGiven] = NotGiven() - max_retries: Union[int, NotGiven] = NotGiven() - timeout: Union[float, Timeout, None, NotGiven] = NotGiven() - files: Union[HttpxRequestFiles, None] = None - idempotency_key: Union[str, None] = None - post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() - follow_redirects: Union[bool, None] = None - - content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None - # It should be noted that we cannot use `json` here as that would override - # a BaseModel method in an incompatible fashion. - json_data: Union[Body, None] = None - extra_json: Union[AnyMapping, None] = None - - if PYDANTIC_V1: - - class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] - arbitrary_types_allowed: bool = True - else: - model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) - - def get_max_retries(self, max_retries: int) -> int: - if isinstance(self.max_retries, NotGiven): - return max_retries - return self.max_retries - - def _strip_raw_response_header(self) -> None: - if not is_given(self.headers): - return - - if self.headers.get(RAW_RESPONSE_HEADER): - self.headers = {**self.headers} - self.headers.pop(RAW_RESPONSE_HEADER) - - # override the `construct` method so that we can run custom transformations. - # this is necessary as we don't want to do any actual runtime type checking - # (which means we can't use validators) but we do want to ensure that `NotGiven` - # values are not present - # - # type ignore required because we're adding explicit types to `**values` - @classmethod - def construct( # type: ignore - cls, - _fields_set: set[str] | None = None, - **values: Unpack[FinalRequestOptionsInput], - ) -> FinalRequestOptions: - kwargs: dict[str, Any] = { - # we unconditionally call `strip_not_given` on any value - # as it will just ignore any non-mapping types - key: strip_not_given(value) - for key, value in values.items() - } - if PYDANTIC_V1: - return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] - return super().model_construct(_fields_set, **kwargs) - - if not TYPE_CHECKING: - # type checkers incorrectly complain about this assignment - model_construct = construct diff --git a/.venv/lib/python3.12/site-packages/anthropic/_qs.py b/.venv/lib/python3.12/site-packages/anthropic/_qs.py deleted file mode 100644 index de8c99bc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_qs.py +++ /dev/null @@ -1,153 +0,0 @@ -from __future__ import annotations - -from typing import Any, List, Tuple, Union, Mapping, TypeVar -from urllib.parse import parse_qs, urlencode -from typing_extensions import Literal, get_args - -from ._types import NotGiven, not_given -from ._utils import flatten - -_T = TypeVar("_T") - - -ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] -NestedFormat = Literal["dots", "brackets"] - -PrimitiveData = Union[str, int, float, bool, None] -# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] -# https://github.com/microsoft/pyright/issues/3555 -Data = Union[PrimitiveData, List[Any], Tuple[Any], "Mapping[str, Any]"] -Params = Mapping[str, Data] - - -class Querystring: - array_format: ArrayFormat - nested_format: NestedFormat - - def __init__( - self, - *, - array_format: ArrayFormat = "repeat", - nested_format: NestedFormat = "brackets", - ) -> None: - self.array_format = array_format - self.nested_format = nested_format - - def parse(self, query: str) -> Mapping[str, object]: - # Note: custom format syntax is not supported yet - return parse_qs(query) - - def stringify( - self, - params: Params, - *, - array_format: ArrayFormat | NotGiven = not_given, - nested_format: NestedFormat | NotGiven = not_given, - ) -> str: - return urlencode( - self.stringify_items( - params, - array_format=array_format, - nested_format=nested_format, - ) - ) - - def stringify_items( - self, - params: Params, - *, - array_format: ArrayFormat | NotGiven = not_given, - nested_format: NestedFormat | NotGiven = not_given, - ) -> list[tuple[str, str]]: - opts = Options( - qs=self, - array_format=array_format, - nested_format=nested_format, - ) - return flatten([self._stringify_item(key, value, opts) for key, value in params.items()]) - - def _stringify_item( - self, - key: str, - value: Data, - opts: Options, - ) -> list[tuple[str, str]]: - if isinstance(value, Mapping): - items: list[tuple[str, str]] = [] - nested_format = opts.nested_format - for subkey, subvalue in value.items(): - items.extend( - self._stringify_item( - # TODO: error if unknown format - f"{key}.{subkey}" if nested_format == "dots" else f"{key}[{subkey}]", - subvalue, - opts, - ) - ) - return items - - if isinstance(value, (list, tuple)): - array_format = opts.array_format - if array_format == "comma": - return [ - ( - key, - ",".join(self._primitive_value_to_str(item) for item in value if item is not None), - ), - ] - elif array_format == "repeat": - items = [] - for item in value: - items.extend(self._stringify_item(key, item, opts)) - return items - elif array_format == "indices": - items = [] - for i, item in enumerate(value): - items.extend(self._stringify_item(f"{key}[{i}]", item, opts)) - return items - elif array_format == "brackets": - items = [] - key = key + "[]" - for item in value: - items.extend(self._stringify_item(key, item, opts)) - return items - else: - raise NotImplementedError( - f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" - ) - - serialised = self._primitive_value_to_str(value) - if not serialised: - return [] - return [(key, serialised)] - - def _primitive_value_to_str(self, value: PrimitiveData) -> str: - # copied from httpx - if value is True: - return "true" - elif value is False: - return "false" - elif value is None: - return "" - return str(value) - - -_qs = Querystring() -parse = _qs.parse -stringify = _qs.stringify -stringify_items = _qs.stringify_items - - -class Options: - array_format: ArrayFormat - nested_format: NestedFormat - - def __init__( - self, - qs: Querystring = _qs, - *, - array_format: ArrayFormat | NotGiven = not_given, - nested_format: NestedFormat | NotGiven = not_given, - ) -> None: - self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format - self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/.venv/lib/python3.12/site-packages/anthropic/_resource.py b/.venv/lib/python3.12/site-packages/anthropic/_resource.py deleted file mode 100644 index f62cc2ba..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_resource.py +++ /dev/null @@ -1,41 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import time - -import anyio - -from ._base_client import SyncAPIClient, AsyncAPIClient - - -class SyncAPIResource: - _client: SyncAPIClient - - def __init__(self, client: SyncAPIClient) -> None: - self._client = client - self._get = client.get - self._post = client.post - self._patch = client.patch - self._put = client.put - self._delete = client.delete - self._get_api_list = client.get_api_list - - def _sleep(self, seconds: float) -> None: - time.sleep(seconds) - - -class AsyncAPIResource: - _client: AsyncAPIClient - - def __init__(self, client: AsyncAPIClient) -> None: - self._client = client - self._get = client.get - self._post = client.post - self._patch = client.patch - self._put = client.put - self._delete = client.delete - self._get_api_list = client.get_api_list - - async def _sleep(self, seconds: float) -> None: - await anyio.sleep(seconds) diff --git a/.venv/lib/python3.12/site-packages/anthropic/_response.py b/.venv/lib/python3.12/site-packages/anthropic/_response.py deleted file mode 100644 index 1e0257ee..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_response.py +++ /dev/null @@ -1,875 +0,0 @@ -from __future__ import annotations - -import os -import inspect -import logging -import datetime -import functools -from types import TracebackType -from typing import ( - TYPE_CHECKING, - Any, - Union, - Generic, - TypeVar, - Callable, - Iterator, - AsyncIterator, - cast, - overload, -) -from typing_extensions import Awaitable, ParamSpec, override, get_origin - -import anyio -import httpx -import pydantic - -from ._types import NoneType -from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base -from ._models import BaseModel, is_basemodel, add_request_id -from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER -from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type -from ._exceptions import AnthropicError, APIResponseValidationError -from ._decoders.jsonl import JSONLDecoder, AsyncJSONLDecoder - -if TYPE_CHECKING: - from ._models import FinalRequestOptions - from ._base_client import BaseClient - - -P = ParamSpec("P") -R = TypeVar("R") -_T = TypeVar("_T") -_APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]") -_AsyncAPIResponseT = TypeVar("_AsyncAPIResponseT", bound="AsyncAPIResponse[Any]") - -log: logging.Logger = logging.getLogger(__name__) - - -class BaseAPIResponse(Generic[R]): - _cast_to: type[R] - _client: BaseClient[Any, Any] - _parsed_by_type: dict[type[Any], Any] - _is_sse_stream: bool - _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None - _options: FinalRequestOptions - - http_response: httpx.Response - - retries_taken: int - """The number of retries made. If no retries happened this will be `0`""" - - def __init__( - self, - *, - raw: httpx.Response, - cast_to: type[R], - client: BaseClient[Any, Any], - stream: bool, - stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, - options: FinalRequestOptions, - retries_taken: int = 0, - ) -> None: - self._cast_to = cast_to - self._client = client - self._parsed_by_type = {} - self._is_sse_stream = stream - self._stream_cls = stream_cls - self._options = options - self.http_response = raw - self.retries_taken = retries_taken - - @property - def headers(self) -> httpx.Headers: - return self.http_response.headers - - @property - def http_request(self) -> httpx.Request: - """Returns the httpx Request instance associated with the current response.""" - return self.http_response.request - - @property - def status_code(self) -> int: - return self.http_response.status_code - - @property - def url(self) -> httpx.URL: - """Returns the URL for which the request was made.""" - return self.http_response.url - - @property - def method(self) -> str: - return self.http_request.method - - @property - def http_version(self) -> str: - return self.http_response.http_version - - @property - def elapsed(self) -> datetime.timedelta: - """The time taken for the complete request/response cycle to complete.""" - return self.http_response.elapsed - - @property - def is_closed(self) -> bool: - """Whether or not the response body has been closed. - - If this is False then there is response data that has not been read yet. - You must either fully consume the response body or call `.close()` - before discarding the response to prevent resource leaks. - """ - return self.http_response.is_closed - - @override - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" - ) - - def _parse(self, *, to: type[_T] | None = None) -> R | _T: - cast_to = to if to is not None else self._cast_to - - # unwrap `TypeAlias('Name', T)` -> `T` - if is_type_alias_type(cast_to): - cast_to = cast_to.__value__ # type: ignore[unreachable] - - # unwrap `Annotated[T, ...]` -> `T` - if cast_to and is_annotated_type(cast_to): - cast_to = extract_type_arg(cast_to, 0) - - origin = get_origin(cast_to) or cast_to - - if inspect.isclass(origin): - if issubclass(cast(Any, origin), JSONLDecoder): - return cast( - R, - cast("type[JSONLDecoder[Any]]", cast_to)( - raw_iterator=self.http_response.iter_bytes(chunk_size=64), - line_type=extract_type_arg(cast_to, 0), - http_response=self.http_response, - ), - ) - - if issubclass(cast(Any, origin), AsyncJSONLDecoder): - return cast( - R, - cast("type[AsyncJSONLDecoder[Any]]", cast_to)( - raw_iterator=self.http_response.aiter_bytes(chunk_size=64), - line_type=extract_type_arg(cast_to, 0), - http_response=self.http_response, - ), - ) - - if self._is_sse_stream: - if to: - if not is_stream_class_type(to): - raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") - - return cast( - _T, - to( - cast_to=extract_stream_chunk_type( - to, - failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", - ), - response=self.http_response, - client=cast(Any, self._client), - options=self._options, - ), - ) - - if self._stream_cls: - return cast( - R, - self._stream_cls( - cast_to=extract_stream_chunk_type(self._stream_cls), - response=self.http_response, - client=cast(Any, self._client), - options=self._options, - ), - ) - - stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) - if stream_cls is None: - raise MissingStreamClassError() - - return cast( - R, - stream_cls( - cast_to=cast_to, - response=self.http_response, - client=cast(Any, self._client), - options=self._options, - ), - ) - - if cast_to is NoneType: - return cast(R, None) - - response = self.http_response - if cast_to == str: - return cast(R, response.text) - - if cast_to == bytes: - return cast(R, response.content) - - if cast_to == int: - return cast(R, int(response.text)) - - if cast_to == float: - return cast(R, float(response.text)) - - if cast_to == bool: - return cast(R, response.text.lower() == "true") - - # handle the legacy binary response case - if inspect.isclass(cast_to) and cast_to.__name__ == "HttpxBinaryResponseContent": - return cast(R, cast_to(response)) # type: ignore - - if origin == APIResponse: - raise RuntimeError("Unexpected state - cast_to is `APIResponse`") - - if inspect.isclass( - origin # pyright: ignore[reportUnknownArgumentType] - ) and issubclass(origin, httpx.Response): - # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response - # and pass that class to our request functions. We cannot change the variance to be either - # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct - # the response class ourselves but that is something that should be supported directly in httpx - # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. - if cast_to != httpx.Response: - raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") - return cast(R, response) - - if ( - inspect.isclass( - origin # pyright: ignore[reportUnknownArgumentType] - ) - and not issubclass(origin, BaseModel) - and issubclass(origin, pydantic.BaseModel) - ): - raise TypeError("Pydantic models must subclass our base model type, e.g. `from anthropic import BaseModel`") - - if ( - cast_to is not object - and not origin is list - and not origin is dict - and not origin is Union - and not issubclass(origin, BaseModel) - ): - raise RuntimeError( - f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." - ) - - # split is required to handle cases where additional information is included - # in the response, e.g. application/json; charset=utf-8 - content_type, *_ = response.headers.get("content-type", "*").split(";") - if not content_type.endswith("json"): - if is_basemodel(cast_to): - try: - data = response.json() - except Exception as exc: - log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) - else: - return self._client._process_response_data( - data=data, - cast_to=cast_to, # type: ignore - response=response, - ) - - if self._client._strict_response_validation: - raise APIResponseValidationError( - response=response, - message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", - body=response.text, - ) - - # If the API responds with content that isn't JSON then we just return - # the (decoded) text without performing any parsing so that you can still - # handle the response however you need to. - return response.text # type: ignore - - data = response.json() - - return self._client._process_response_data( - data=data, - cast_to=cast_to, # type: ignore - response=response, - ) - - -class APIResponse(BaseAPIResponse[R]): - @property - def request_id(self) -> str | None: - return self.http_response.headers.get("request-id") # type: ignore[no-any-return] - - @overload - def parse(self, *, to: type[_T]) -> _T: ... - - @overload - def parse(self) -> R: ... - - def parse(self, *, to: type[_T] | None = None) -> R | _T: - """Returns the rich python representation of this response's data. - - For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. - - You can customise the type that the response is parsed into through - the `to` argument, e.g. - - ```py - from anthropic import BaseModel - - - class MyModel(BaseModel): - foo: str - - - obj = response.parse(to=MyModel) - print(obj.foo) - ``` - - We support parsing: - - `BaseModel` - - `dict` - - `list` - - `Union` - - `str` - - `int` - - `float` - - `httpx.Response` - """ - cache_key = to if to is not None else self._cast_to - cached = self._parsed_by_type.get(cache_key) - if cached is not None: - return cached # type: ignore[no-any-return] - - if not self._is_sse_stream: - self.read() - - parsed = self._parse(to=to) - if is_given(self._options.post_parser): - parsed = self._options.post_parser(parsed) - - if isinstance(parsed, BaseModel): - add_request_id(parsed, self.request_id) - - self._parsed_by_type[cache_key] = parsed - return cast(R, parsed) - - def read(self) -> bytes: - """Read and return the binary response content.""" - try: - return self.http_response.read() - except httpx.StreamConsumed as exc: - # The default error raised by httpx isn't very - # helpful in our case so we re-raise it with - # a different error message. - raise StreamAlreadyConsumed() from exc - - def text(self) -> str: - """Read and decode the response content into a string.""" - self.read() - return self.http_response.text - - def json(self) -> object: - """Read and decode the JSON response content.""" - self.read() - return self.http_response.json() - - def close(self) -> None: - """Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - self.http_response.close() - - def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: - """ - A byte-iterator over the decoded response content. - - This automatically handles gzip, deflate and brotli encoded responses. - """ - for chunk in self.http_response.iter_bytes(chunk_size): - yield chunk - - def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: - """A str-iterator over the decoded response content - that handles both gzip, deflate, etc but also detects the content's - string encoding. - """ - for chunk in self.http_response.iter_text(chunk_size): - yield chunk - - def iter_lines(self) -> Iterator[str]: - """Like `iter_text()` but will only yield chunks for each line""" - for chunk in self.http_response.iter_lines(): - yield chunk - - -class AsyncAPIResponse(BaseAPIResponse[R]): - @property - def request_id(self) -> str | None: - return self.http_response.headers.get("request-id") # type: ignore[no-any-return] - - @overload - async def parse(self, *, to: type[_T]) -> _T: ... - - @overload - async def parse(self) -> R: ... - - async def parse(self, *, to: type[_T] | None = None) -> R | _T: - """Returns the rich python representation of this response's data. - - For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. - - You can customise the type that the response is parsed into through - the `to` argument, e.g. - - ```py - from anthropic import BaseModel - - - class MyModel(BaseModel): - foo: str - - - obj = response.parse(to=MyModel) - print(obj.foo) - ``` - - We support parsing: - - `BaseModel` - - `dict` - - `list` - - `Union` - - `str` - - `httpx.Response` - """ - cache_key = to if to is not None else self._cast_to - cached = self._parsed_by_type.get(cache_key) - if cached is not None: - return cached # type: ignore[no-any-return] - - if not self._is_sse_stream: - await self.read() - - parsed = self._parse(to=to) - if is_given(self._options.post_parser): - parsed = self._options.post_parser(parsed) - - if isinstance(parsed, BaseModel): - add_request_id(parsed, self.request_id) - - self._parsed_by_type[cache_key] = parsed - return cast(R, parsed) - - async def read(self) -> bytes: - """Read and return the binary response content.""" - try: - return await self.http_response.aread() - except httpx.StreamConsumed as exc: - # the default error raised by httpx isn't very - # helpful in our case so we re-raise it with - # a different error message - raise StreamAlreadyConsumed() from exc - - async def text(self) -> str: - """Read and decode the response content into a string.""" - await self.read() - return self.http_response.text - - async def json(self) -> object: - """Read and decode the JSON response content.""" - await self.read() - return self.http_response.json() - - async def close(self) -> None: - """Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - await self.http_response.aclose() - - async def iter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: - """ - A byte-iterator over the decoded response content. - - This automatically handles gzip, deflate and brotli encoded responses. - """ - async for chunk in self.http_response.aiter_bytes(chunk_size): - yield chunk - - async def iter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: - """A str-iterator over the decoded response content - that handles both gzip, deflate, etc but also detects the content's - string encoding. - """ - async for chunk in self.http_response.aiter_text(chunk_size): - yield chunk - - async def iter_lines(self) -> AsyncIterator[str]: - """Like `iter_text()` but will only yield chunks for each line""" - async for chunk in self.http_response.aiter_lines(): - yield chunk - - -class BinaryAPIResponse(APIResponse[bytes]): - """Subclass of APIResponse providing helpers for dealing with binary data. - - Note: If you want to stream the response data instead of eagerly reading it - all at once then you should use `.with_streaming_response` when making - the API request, e.g. `.with_streaming_response.get_binary_response()` - """ - - def write_to_file( - self, - file: str | os.PathLike[str], - ) -> None: - """Write the output to the given file. - - Accepts a filename or any path-like object, e.g. pathlib.Path - - Note: if you want to stream the data to the file instead of writing - all at once then you should use `.with_streaming_response` when making - the API request, e.g. `.with_streaming_response.get_binary_response()` - """ - with open(file, mode="wb") as f: - for data in self.iter_bytes(): - f.write(data) - - -class AsyncBinaryAPIResponse(AsyncAPIResponse[bytes]): - """Subclass of APIResponse providing helpers for dealing with binary data. - - Note: If you want to stream the response data instead of eagerly reading it - all at once then you should use `.with_streaming_response` when making - the API request, e.g. `.with_streaming_response.get_binary_response()` - """ - - async def write_to_file( - self, - file: str | os.PathLike[str], - ) -> None: - """Write the output to the given file. - - Accepts a filename or any path-like object, e.g. pathlib.Path - - Note: if you want to stream the data to the file instead of writing - all at once then you should use `.with_streaming_response` when making - the API request, e.g. `.with_streaming_response.get_binary_response()` - """ - path = anyio.Path(file) - async with await path.open(mode="wb") as f: - async for data in self.iter_bytes(): - await f.write(data) - - -class StreamedBinaryAPIResponse(APIResponse[bytes]): - def stream_to_file( - self, - file: str | os.PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - """Streams the output to the given file. - - Accepts a filename or any path-like object, e.g. pathlib.Path - """ - with open(file, mode="wb") as f: - for data in self.iter_bytes(chunk_size): - f.write(data) - - -class AsyncStreamedBinaryAPIResponse(AsyncAPIResponse[bytes]): - async def stream_to_file( - self, - file: str | os.PathLike[str], - *, - chunk_size: int | None = None, - ) -> None: - """Streams the output to the given file. - - Accepts a filename or any path-like object, e.g. pathlib.Path - """ - path = anyio.Path(file) - async with await path.open(mode="wb") as f: - async for data in self.iter_bytes(chunk_size): - await f.write(data) - - -class MissingStreamClassError(TypeError): - def __init__(self) -> None: - super().__init__( - "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `anthropic._streaming` for reference", - ) - - -class StreamAlreadyConsumed(AnthropicError): - """ - Attempted to read or stream content, but the content has already - been streamed. - - This can happen if you use a method like `.iter_lines()` and then attempt - to read th entire response body afterwards, e.g. - - ```py - response = await client.post(...) - async for line in response.iter_lines(): - ... # do something with `line` - - content = await response.read() - # ^ error - ``` - - If you want this behaviour you'll need to either manually accumulate the response - content or call `await response.read()` before iterating over the stream. - """ - - def __init__(self) -> None: - message = ( - "Attempted to read or stream some content, but the content has " - "already been streamed. " - "This could be due to attempting to stream the response " - "content more than once." - "\n\n" - "You can fix this by manually accumulating the response content while streaming " - "or by calling `.read()` before starting to stream." - ) - super().__init__(message) - - -class ResponseContextManager(Generic[_APIResponseT]): - """Context manager for ensuring that a request is not made - until it is entered and that the response will always be closed - when the context manager exits - """ - - def __init__(self, request_func: Callable[[], _APIResponseT]) -> None: - self._request_func = request_func - self.__response: _APIResponseT | None = None - - def __enter__(self) -> _APIResponseT: - self.__response = self._request_func() - return self.__response - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - if self.__response is not None: - self.__response.close() - - -class AsyncResponseContextManager(Generic[_AsyncAPIResponseT]): - """Context manager for ensuring that a request is not made - until it is entered and that the response will always be closed - when the context manager exits - """ - - def __init__(self, api_request: Awaitable[_AsyncAPIResponseT]) -> None: - self._api_request = api_request - self.__response: _AsyncAPIResponseT | None = None - - async def __aenter__(self) -> _AsyncAPIResponseT: - self.__response = await self._api_request - return self.__response - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - if self.__response is not None: - await self.__response.close() - - -def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseContextManager[APIResponse[R]]]: - """Higher order function that takes one of our bound API methods and wraps it - to support streaming and returning the raw `APIResponse` object directly. - """ - - @functools.wraps(func) - def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: - extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "stream" - - kwargs["extra_headers"] = extra_headers - - make_request = functools.partial(func, *args, **kwargs) - - return ResponseContextManager(cast(Callable[[], APIResponse[R]], make_request)) - - return wrapped - - -def async_to_streamed_response_wrapper( - func: Callable[P, Awaitable[R]], -) -> Callable[P, AsyncResponseContextManager[AsyncAPIResponse[R]]]: - """Higher order function that takes one of our bound API methods and wraps it - to support streaming and returning the raw `APIResponse` object directly. - """ - - @functools.wraps(func) - def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: - extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "stream" - - kwargs["extra_headers"] = extra_headers - - make_request = func(*args, **kwargs) - - return AsyncResponseContextManager(cast(Awaitable[AsyncAPIResponse[R]], make_request)) - - return wrapped - - -def to_custom_streamed_response_wrapper( - func: Callable[P, object], - response_cls: type[_APIResponseT], -) -> Callable[P, ResponseContextManager[_APIResponseT]]: - """Higher order function that takes one of our bound API methods and an `APIResponse` class - and wraps the method to support streaming and returning the given response class directly. - - Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` - """ - - @functools.wraps(func) - def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: - extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "stream" - extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls - - kwargs["extra_headers"] = extra_headers - - make_request = functools.partial(func, *args, **kwargs) - - return ResponseContextManager(cast(Callable[[], _APIResponseT], make_request)) - - return wrapped - - -def async_to_custom_streamed_response_wrapper( - func: Callable[P, Awaitable[object]], - response_cls: type[_AsyncAPIResponseT], -) -> Callable[P, AsyncResponseContextManager[_AsyncAPIResponseT]]: - """Higher order function that takes one of our bound API methods and an `APIResponse` class - and wraps the method to support streaming and returning the given response class directly. - - Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` - """ - - @functools.wraps(func) - def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: - extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "stream" - extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls - - kwargs["extra_headers"] = extra_headers - - make_request = func(*args, **kwargs) - - return AsyncResponseContextManager(cast(Awaitable[_AsyncAPIResponseT], make_request)) - - return wrapped - - -def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]: - """Higher order function that takes one of our bound API methods and wraps it - to support returning the raw `APIResponse` object directly. - """ - - @functools.wraps(func) - def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: - extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "raw" - - kwargs["extra_headers"] = extra_headers - - return cast(APIResponse[R], func(*args, **kwargs)) - - return wrapped - - -def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]: - """Higher order function that takes one of our bound API methods and wraps it - to support returning the raw `APIResponse` object directly. - """ - - @functools.wraps(func) - async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: - extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "raw" - - kwargs["extra_headers"] = extra_headers - - return cast(AsyncAPIResponse[R], await func(*args, **kwargs)) - - return wrapped - - -def to_custom_raw_response_wrapper( - func: Callable[P, object], - response_cls: type[_APIResponseT], -) -> Callable[P, _APIResponseT]: - """Higher order function that takes one of our bound API methods and an `APIResponse` class - and wraps the method to support returning the given response class directly. - - Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` - """ - - @functools.wraps(func) - def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: - extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "raw" - extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls - - kwargs["extra_headers"] = extra_headers - - return cast(_APIResponseT, func(*args, **kwargs)) - - return wrapped - - -def async_to_custom_raw_response_wrapper( - func: Callable[P, Awaitable[object]], - response_cls: type[_AsyncAPIResponseT], -) -> Callable[P, Awaitable[_AsyncAPIResponseT]]: - """Higher order function that takes one of our bound API methods and an `APIResponse` class - and wraps the method to support returning the given response class directly. - - Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` - """ - - @functools.wraps(func) - def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: - extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} - extra_headers[RAW_RESPONSE_HEADER] = "raw" - extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls - - kwargs["extra_headers"] = extra_headers - - return cast(Awaitable[_AsyncAPIResponseT], func(*args, **kwargs)) - - return wrapped - - -def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type: - """Given a type like `APIResponse[T]`, returns the generic type variable `T`. - - This also handles the case where a concrete subclass is given, e.g. - ```py - class MyResponse(APIResponse[bytes]): - ... - - extract_response_type(MyResponse) -> bytes - ``` - """ - return extract_type_var_from_base( - typ, - generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse)), - index=0, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/_streaming.py b/.venv/lib/python3.12/site-packages/anthropic/_streaming.py deleted file mode 100644 index 9e44f4da..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_streaming.py +++ /dev/null @@ -1,490 +0,0 @@ -# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py -from __future__ import annotations - -import abc -import json -import inspect -import warnings -from types import TracebackType -from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast -from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable - -import httpx - -from ._utils import is_dict, extract_type_var_from_base - -if TYPE_CHECKING: - from ._client import Anthropic, AsyncAnthropic - from ._models import FinalRequestOptions - - -_T = TypeVar("_T") - - -class _SyncStreamMeta(abc.ABCMeta): - @override - def __instancecheck__(self, instance: Any) -> bool: - # we override the `isinstance()` check for `Stream` - # as a previous version of the `MessageStream` class - # inherited from `Stream` & without this workaround, - # changing it to not inherit would be a breaking change. - - from .lib.streaming import MessageStream - - if isinstance(instance, MessageStream): - warnings.warn( - "Using `isinstance()` to check if a `MessageStream` object is an instance of `Stream` is deprecated & will be removed in the next major version", - DeprecationWarning, - stacklevel=2, - ) - return True - - return False - - -class Stream(Generic[_T], metaclass=_SyncStreamMeta): - """Provides the core interface to iterate over a synchronous stream response.""" - - response: httpx.Response - _options: Optional[FinalRequestOptions] = None - _decoder: SSEBytesDecoder - - def __init__( - self, - *, - cast_to: type[_T], - response: httpx.Response, - client: Anthropic, - options: Optional[FinalRequestOptions] = None, - ) -> None: - self.response = response - self._cast_to = cast_to - self._client = client - self._options = options - self._decoder = client._make_sse_decoder() - self._iterator = self.__stream__() - - def __next__(self) -> _T: - return self._iterator.__next__() - - def __iter__(self) -> Iterator[_T]: - for item in self._iterator: - yield item - - def _iter_events(self) -> Iterator[ServerSentEvent]: - yield from self._decoder.iter_bytes(self.response.iter_bytes()) - - def __stream__(self) -> Iterator[_T]: - cast_to = cast(Any, self._cast_to) - response = self.response - process_data = self._client._process_response_data - iterator = self._iter_events() - - try: - for sse in iterator: - if sse.event == "completion": - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - if ( - sse.event == "message_start" - or sse.event == "message_delta" - or sse.event == "message_stop" - or sse.event == "content_block_start" - or sse.event == "content_block_delta" - or sse.event == "content_block_stop" - or sse.event == "message" - or sse.event == "user.message" - or sse.event == "user.interrupt" - or sse.event == "user.tool_confirmation" - or sse.event == "user.custom_tool_result" - or sse.event == "agent.message" - or sse.event == "agent.thinking" - or sse.event == "agent.tool_use" - or sse.event == "agent.tool_result" - or sse.event == "agent.mcp_tool_use" - or sse.event == "agent.mcp_tool_result" - or sse.event == "agent.custom_tool_use" - or sse.event == "agent.thread_context_compacted" - or sse.event == "session.status_running" - or sse.event == "session.status_idle" - or sse.event == "session.status_rescheduled" - or sse.event == "session.status_terminated" - or sse.event == "session.error" - or sse.event == "session.deleted" - or sse.event == "span.model_request_start" - or sse.event == "span.model_request_end" - ): - data = sse.json() - if is_dict(data) and "type" not in data: - data["type"] = sse.event - - yield process_data(data=data, cast_to=cast_to, response=response) - - if sse.event == "ping": - continue - - if sse.event == "error": - body = sse.data - - try: - body = sse.json() - err_msg = f"{body}" - except Exception: - err_msg = sse.data or f"Error code: {response.status_code}" - - raise self._client._make_status_error( - err_msg, - body=body, - response=self.response, - ) - finally: - # Ensure the response is closed even if the consumer doesn't read all data - response.close() - - def __enter__(self) -> Self: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.close() - - def close(self) -> None: - """ - Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - self.response.close() - - -class _AsyncStreamMeta(abc.ABCMeta): - @override - def __instancecheck__(self, instance: Any) -> bool: - # we override the `isinstance()` check for `AsyncStream` - # as a previous version of the `AsyncMessageStream` class - # inherited from `AsyncStream` & without this workaround, - # changing it to not inherit would be a breaking change. - - from .lib.streaming import AsyncMessageStream - - if isinstance(instance, AsyncMessageStream): - warnings.warn( - "Using `isinstance()` to check if a `AsyncMessageStream` object is an instance of `AsyncStream` is deprecated & will be removed in the next major version", - DeprecationWarning, - stacklevel=2, - ) - return True - - return False - - -class AsyncStream(Generic[_T], metaclass=_AsyncStreamMeta): - """Provides the core interface to iterate over an asynchronous stream response.""" - - response: httpx.Response - _options: Optional[FinalRequestOptions] = None - _decoder: SSEDecoder | SSEBytesDecoder - - def __init__( - self, - *, - cast_to: type[_T], - response: httpx.Response, - client: AsyncAnthropic, - options: Optional[FinalRequestOptions] = None, - ) -> None: - self.response = response - self._cast_to = cast_to - self._client = client - self._options = options - self._decoder = client._make_sse_decoder() - self._iterator = self.__stream__() - - async def __anext__(self) -> _T: - return await self._iterator.__anext__() - - async def __aiter__(self) -> AsyncIterator[_T]: - async for item in self._iterator: - yield item - - async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: - async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): - yield sse - - async def __stream__(self) -> AsyncIterator[_T]: - cast_to = cast(Any, self._cast_to) - response = self.response - process_data = self._client._process_response_data - iterator = self._iter_events() - - try: - async for sse in iterator: - if sse.event == "completion": - yield process_data(data=sse.json(), cast_to=cast_to, response=response) - - if ( - sse.event == "message_start" - or sse.event == "message_delta" - or sse.event == "message_stop" - or sse.event == "content_block_start" - or sse.event == "content_block_delta" - or sse.event == "content_block_stop" - or sse.event == "message" - or sse.event == "user.message" - or sse.event == "user.interrupt" - or sse.event == "user.tool_confirmation" - or sse.event == "user.custom_tool_result" - or sse.event == "agent.message" - or sse.event == "agent.thinking" - or sse.event == "agent.tool_use" - or sse.event == "agent.tool_result" - or sse.event == "agent.mcp_tool_use" - or sse.event == "agent.mcp_tool_result" - or sse.event == "agent.custom_tool_use" - or sse.event == "agent.thread_context_compacted" - or sse.event == "session.status_running" - or sse.event == "session.status_idle" - or sse.event == "session.status_rescheduled" - or sse.event == "session.status_terminated" - or sse.event == "session.error" - or sse.event == "session.deleted" - or sse.event == "span.model_request_start" - or sse.event == "span.model_request_end" - ): - data = sse.json() - if is_dict(data) and "type" not in data: - data["type"] = sse.event - - yield process_data(data=data, cast_to=cast_to, response=response) - - if sse.event == "ping": - continue - - if sse.event == "error": - body = sse.data - - try: - body = sse.json() - err_msg = f"{body}" - except Exception: - err_msg = sse.data or f"Error code: {response.status_code}" - - raise self._client._make_status_error( - err_msg, - body=body, - response=self.response, - ) - finally: - # Ensure the response is closed even if the consumer doesn't read all data - await response.aclose() - - async def __aenter__(self) -> Self: - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - await self.close() - - async def close(self) -> None: - """ - Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - await self.response.aclose() - - -class ServerSentEvent: - def __init__( - self, - *, - event: str | None = None, - data: str | None = None, - id: str | None = None, - retry: int | None = None, - ) -> None: - if data is None: - data = "" - - self._id = id - self._data = data - self._event = event or None - self._retry = retry - - @property - def event(self) -> str | None: - return self._event - - @property - def id(self) -> str | None: - return self._id - - @property - def retry(self) -> int | None: - return self._retry - - @property - def data(self) -> str: - return self._data - - def json(self) -> Any: - return json.loads(self.data) - - @override - def __repr__(self) -> str: - return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" - - -class SSEDecoder: - _data: list[str] - _event: str | None - _retry: int | None - _last_event_id: str | None - - def __init__(self) -> None: - self._event = None - self._data = [] - self._last_event_id = None - self._retry = None - - def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: - """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" - for chunk in self._iter_chunks(iterator): - # Split before decoding so splitlines() only uses \r and \n - for raw_line in chunk.splitlines(): - line = raw_line.decode("utf-8") - sse = self.decode(line) - if sse: - yield sse - - def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]: - """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" - data = b"" - for chunk in iterator: - for line in chunk.splitlines(keepends=True): - data += line - if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): - yield data - data = b"" - if data: - yield data - - async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: - """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" - async for chunk in self._aiter_chunks(iterator): - # Split before decoding so splitlines() only uses \r and \n - for raw_line in chunk.splitlines(): - line = raw_line.decode("utf-8") - sse = self.decode(line) - if sse: - yield sse - - async def _aiter_chunks(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[bytes]: - """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" - data = b"" - async for chunk in iterator: - for line in chunk.splitlines(keepends=True): - data += line - if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): - yield data - data = b"" - if data: - yield data - - def decode(self, line: str) -> ServerSentEvent | None: - # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 - - if not line: - if not self._event and not self._data and not self._last_event_id and self._retry is None: - return None - - sse = ServerSentEvent( - event=self._event, - data="\n".join(self._data), - id=self._last_event_id, - retry=self._retry, - ) - - # NOTE: as per the SSE spec, do not reset last_event_id. - self._event = None - self._data = [] - self._retry = None - - return sse - - if line.startswith(":"): - return None - - fieldname, _, value = line.partition(":") - - if value.startswith(" "): - value = value[1:] - - if fieldname == "event": - self._event = value - elif fieldname == "data": - self._data.append(value) - elif fieldname == "id": - if "\0" in value: - pass - else: - self._last_event_id = value - elif fieldname == "retry": - try: - self._retry = int(value) - except (TypeError, ValueError): - pass - else: - pass # Field is ignored. - - return None - - -@runtime_checkable -class SSEBytesDecoder(Protocol): - def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: - """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" - ... - - def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: - """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" - ... - - -def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: - """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" - origin = get_origin(typ) or typ - return inspect.isclass(origin) and issubclass(origin, (Stream, AsyncStream)) - - -def extract_stream_chunk_type( - stream_cls: type, - *, - failure_message: str | None = None, -) -> type: - """Given a type like `Stream[T]`, returns the generic type variable `T`. - - This also handles the case where a concrete subclass is given, e.g. - ```py - class MyStream(Stream[bytes]): - ... - - extract_stream_chunk_type(MyStream) -> bytes - ``` - """ - from ._base_client import Stream, AsyncStream - - return extract_type_var_from_base( - stream_cls, - index=0, - generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), - failure_message=failure_message, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/_types.py b/.venv/lib/python3.12/site-packages/anthropic/_types.py deleted file mode 100644 index 16d1ba3f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_types.py +++ /dev/null @@ -1,272 +0,0 @@ -from __future__ import annotations - -from os import PathLike -from typing import ( - IO, - TYPE_CHECKING, - Any, - Dict, - List, - Type, - Tuple, - Union, - Mapping, - TypeVar, - Callable, - Iterable, - Iterator, - Optional, - Sequence, - AsyncIterable, -) -from typing_extensions import ( - Set, - Literal, - Protocol, - TypeAlias, - TypedDict, - SupportsIndex, - overload, - override, - runtime_checkable, -) - -import httpx -import pydantic -from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport - -if TYPE_CHECKING: - from ._models import BaseModel - from ._response import APIResponse, AsyncAPIResponse - from ._legacy_response import HttpxBinaryResponseContent - -Transport = BaseTransport -AsyncTransport = AsyncBaseTransport -Query = Mapping[str, object] -Body = object -AnyMapping = Mapping[str, object] -ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) -_T = TypeVar("_T") - - -# Approximates httpx internal ProxiesTypes and RequestFiles types -# while adding support for `PathLike` instances -ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] -ProxiesTypes = Union[str, Proxy, ProxiesDict] -if TYPE_CHECKING: - Base64FileInput = Union[IO[bytes], PathLike[str]] - FileContent = Union[IO[bytes], bytes, PathLike[str]] -else: - Base64FileInput = Union[IO[bytes], PathLike] - FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. - - -# Used for sending raw binary data / streaming data in request bodies -# e.g. for file uploads without multipart encoding -BinaryTypes = Union[bytes, bytearray, IO[bytes], Iterable[bytes]] -AsyncBinaryTypes = Union[bytes, bytearray, IO[bytes], AsyncIterable[bytes]] - -FileTypes = Union[ - # file (or bytes) - FileContent, - # (filename, file (or bytes)) - Tuple[Optional[str], FileContent], - # (filename, file (or bytes), content_type) - Tuple[Optional[str], FileContent, Optional[str]], - # (filename, file (or bytes), content_type, headers) - Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], -] -RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] - -# duplicate of the above but without our custom file support -HttpxFileContent = Union[IO[bytes], bytes] -HttpxFileTypes = Union[ - # file (or bytes) - HttpxFileContent, - # (filename, file (or bytes)) - Tuple[Optional[str], HttpxFileContent], - # (filename, file (or bytes), content_type) - Tuple[Optional[str], HttpxFileContent, Optional[str]], - # (filename, file (or bytes), content_type, headers) - Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], -] -HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]] - -# Workaround to support (cast_to: Type[ResponseT]) -> ResponseT -# where ResponseT includes `None`. In order to support directly -# passing `None`, overloads would have to be defined for every -# method that uses `ResponseT` which would lead to an unacceptable -# amount of code duplication and make it unreadable. See _base_client.py -# for example usage. -# -# This unfortunately means that you will either have -# to import this type and pass it explicitly: -# -# from anthropic import NoneType -# client.get('/foo', cast_to=NoneType) -# -# or build it yourself: -# -# client.get('/foo', cast_to=type(None)) -if TYPE_CHECKING: - NoneType: Type[None] -else: - NoneType = type(None) - - -class RequestOptions(TypedDict, total=False): - headers: Headers - max_retries: int - timeout: float | Timeout | None - params: Query - extra_json: AnyMapping - idempotency_key: str - follow_redirects: bool - - -# Sentinel class used until PEP 0661 is accepted -class NotGiven: - """ - For parameters with a meaningful None value, we need to distinguish between - the user explicitly passing None, and the user not passing the parameter at - all. - - User code shouldn't need to use not_given directly. - - For example: - - ```py - def create(timeout: Timeout | None | NotGiven = not_given): ... - - - create(timeout=1) # 1s timeout - create(timeout=None) # No timeout - create() # Default timeout behavior - ``` - """ - - def __bool__(self) -> Literal[False]: - return False - - @override - def __repr__(self) -> str: - return "NOT_GIVEN" - - -not_given = NotGiven() -# for backwards compatibility: -NOT_GIVEN = NotGiven() - - -class Omit: - """ - To explicitly omit something from being sent in a request, use `omit`. - - ```py - # as the default `Content-Type` header is `application/json` that will be sent - client.post("/upload/files", files={"file": b"my raw file content"}) - - # you can't explicitly override the header as it has to be dynamically generated - # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' - client.post(..., headers={"Content-Type": "multipart/form-data"}) - - # instead you can remove the default `application/json` header by passing omit - client.post(..., headers={"Content-Type": omit}) - ``` - """ - - def __bool__(self) -> Literal[False]: - return False - - -omit = Omit() - - -@runtime_checkable -class ModelBuilderProtocol(Protocol): - @classmethod - def build( - cls: type[_T], - *, - response: Response, - data: object, - ) -> _T: ... - - -Headers = Mapping[str, Union[str, Omit]] - - -class HeadersLikeProtocol(Protocol): - def get(self, __key: str) -> str | None: ... - - -HeadersLike = Union[Headers, HeadersLikeProtocol] - -ResponseT = TypeVar( - "ResponseT", - bound=Union[ - object, - str, - None, - "BaseModel", - List[Any], - Dict[str, Any], - Response, - ModelBuilderProtocol, - "APIResponse[Any]", - "AsyncAPIResponse[Any]", - "HttpxBinaryResponseContent", - ], -) - -StrBytesIntFloat = Union[str, bytes, int, float] - -# Note: copied from Pydantic -# https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 -IncEx: TypeAlias = Union[Set[int], Set[str], Mapping[int, Union["IncEx", bool]], Mapping[str, Union["IncEx", bool]]] - -PostParser = Callable[[Any], Any] - - -@runtime_checkable -class InheritsGeneric(Protocol): - """Represents a type that has inherited from `Generic` - - The `__orig_bases__` property can be used to determine the resolved - type variable for a given base class. - """ - - __orig_bases__: tuple[_GenericAlias] - - -class _GenericAlias(Protocol): - __origin__: type[object] - - -class HttpxSendArgs(TypedDict, total=False): - auth: httpx.Auth - follow_redirects: bool - - -_T_co = TypeVar("_T_co", covariant=True) - - -if TYPE_CHECKING: - # This works because str.__contains__ does not accept object (either in typeshed or at runtime) - # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 - # - # Note: index() and count() methods are intentionally omitted to allow pyright to properly - # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. - class SequenceNotStr(Protocol[_T_co]): - @overload - def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... - @overload - def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... - def __contains__(self, value: object, /) -> bool: ... - def __len__(self) -> int: ... - def __iter__(self) -> Iterator[_T_co]: ... - def __reversed__(self) -> Iterator[_T_co]: ... -else: - # just point this to a normal `Sequence` at runtime to avoid having to special case - # deserializing our custom sequence type - SequenceNotStr = Sequence diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/__init__.py deleted file mode 100644 index 420d5b67..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/__init__.py +++ /dev/null @@ -1,65 +0,0 @@ -from ._path import path_template as path_template -from ._sync import asyncify as asyncify -from ._proxy import LazyProxy as LazyProxy -from ._utils import ( - flatten as flatten, - is_dict as is_dict, - is_list as is_list, - is_given as is_given, - is_tuple as is_tuple, - json_safe as json_safe, - lru_cache as lru_cache, - is_mapping as is_mapping, - is_tuple_t as is_tuple_t, - is_iterable as is_iterable, - is_sequence as is_sequence, - coerce_float as coerce_float, - is_mapping_t as is_mapping_t, - removeprefix as removeprefix, - removesuffix as removesuffix, - extract_files as extract_files, - is_sequence_t as is_sequence_t, - required_args as required_args, - coerce_boolean as coerce_boolean, - coerce_integer as coerce_integer, - file_from_path as file_from_path, - strip_not_given as strip_not_given, - get_async_library as get_async_library, - maybe_coerce_float as maybe_coerce_float, - get_required_header as get_required_header, - maybe_coerce_boolean as maybe_coerce_boolean, - maybe_coerce_integer as maybe_coerce_integer, -) -from ._compat import ( - get_args as get_args, - is_union as is_union, - get_origin as get_origin, - is_typeddict as is_typeddict, - is_literal_type as is_literal_type, -) -from ._typing import ( - is_list_type as is_list_type, - is_union_type as is_union_type, - extract_type_arg as extract_type_arg, - is_iterable_type as is_iterable_type, - is_required_type as is_required_type, - is_sequence_type as is_sequence_type, - is_annotated_type as is_annotated_type, - is_type_alias_type as is_type_alias_type, - strip_annotated_type as strip_annotated_type, - extract_type_var_from_base as extract_type_var_from_base, -) -from ._streams import consume_sync_iterator as consume_sync_iterator, consume_async_iterator as consume_async_iterator -from ._transform import ( - PropertyInfo as PropertyInfo, - transform as transform, - async_transform as async_transform, - maybe_transform as maybe_transform, - async_maybe_transform as async_maybe_transform, -) -from ._reflection import ( - function_has_argument as function_has_argument, - assert_overloads_in_sync as assert_overloads_in_sync, - assert_signatures_in_sync as assert_signatures_in_sync, -) -from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_compat.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_compat.py deleted file mode 100644 index 2c70b299..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_compat.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -import sys -import typing_extensions -from typing import Any, Type, Union, Literal, Optional -from datetime import date, datetime -from typing_extensions import get_args as _get_args, get_origin as _get_origin - -from .._types import StrBytesIntFloat -from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime - -_LITERAL_TYPES = {Literal, typing_extensions.Literal} - - -def get_args(tp: type[Any]) -> tuple[Any, ...]: - return _get_args(tp) - - -def get_origin(tp: type[Any]) -> type[Any] | None: - return _get_origin(tp) - - -def is_union(tp: Optional[Type[Any]]) -> bool: - if sys.version_info < (3, 10): - return tp is Union # type: ignore[comparison-overlap] - else: - import types - - return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap] - - -def is_typeddict(tp: Type[Any]) -> bool: - return typing_extensions.is_typeddict(tp) - - -def is_literal_type(tp: Type[Any]) -> bool: - return get_origin(tp) in _LITERAL_TYPES - - -def parse_date(value: Union[date, StrBytesIntFloat]) -> date: - return _parse_date(value) - - -def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: - return _parse_datetime(value) diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_datetime_parse.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_datetime_parse.py deleted file mode 100644 index 7cb9d9e6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_datetime_parse.py +++ /dev/null @@ -1,136 +0,0 @@ -""" -This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py -without the Pydantic v1 specific errors. -""" - -from __future__ import annotations - -import re -from typing import Dict, Union, Optional -from datetime import date, datetime, timezone, timedelta - -from .._types import StrBytesIntFloat - -date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})" -time_expr = ( - r"(?P\d{1,2}):(?P\d{1,2})" - r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?" - r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" -) - -date_re = re.compile(f"{date_expr}$") -datetime_re = re.compile(f"{date_expr}[T ]{time_expr}") - - -EPOCH = datetime(1970, 1, 1) -# if greater than this, the number is in ms, if less than or equal it's in seconds -# (in seconds this is 11th October 2603, in ms it's 20th August 1970) -MS_WATERSHED = int(2e10) -# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 -MAX_NUMBER = int(3e20) - - -def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: - if isinstance(value, (int, float)): - return value - try: - return float(value) - except ValueError: - return None - except TypeError: - raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None - - -def _from_unix_seconds(seconds: Union[int, float]) -> datetime: - if seconds > MAX_NUMBER: - return datetime.max - elif seconds < -MAX_NUMBER: - return datetime.min - - while abs(seconds) > MS_WATERSHED: - seconds /= 1000 - dt = EPOCH + timedelta(seconds=seconds) - return dt.replace(tzinfo=timezone.utc) - - -def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]: - if value == "Z": - return timezone.utc - elif value is not None: - offset_mins = int(value[-2:]) if len(value) > 3 else 0 - offset = 60 * int(value[1:3]) + offset_mins - if value[0] == "-": - offset = -offset - return timezone(timedelta(minutes=offset)) - else: - return None - - -def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: - """ - Parse a datetime/int/float/string and return a datetime.datetime. - - This function supports time zone offsets. When the input contains one, - the output uses a timezone with a fixed offset from UTC. - - Raise ValueError if the input is well formatted but not a valid datetime. - Raise ValueError if the input isn't well formatted. - """ - if isinstance(value, datetime): - return value - - number = _get_numeric(value, "datetime") - if number is not None: - return _from_unix_seconds(number) - - if isinstance(value, bytes): - value = value.decode() - - assert not isinstance(value, (float, int)) - - match = datetime_re.match(value) - if match is None: - raise ValueError("invalid datetime format") - - kw = match.groupdict() - if kw["microsecond"]: - kw["microsecond"] = kw["microsecond"].ljust(6, "0") - - tzinfo = _parse_timezone(kw.pop("tzinfo")) - kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} - kw_["tzinfo"] = tzinfo - - return datetime(**kw_) # type: ignore - - -def parse_date(value: Union[date, StrBytesIntFloat]) -> date: - """ - Parse a date/int/float/string and return a datetime.date. - - Raise ValueError if the input is well formatted but not a valid date. - Raise ValueError if the input isn't well formatted. - """ - if isinstance(value, date): - if isinstance(value, datetime): - return value.date() - else: - return value - - number = _get_numeric(value, "date") - if number is not None: - return _from_unix_seconds(number).date() - - if isinstance(value, bytes): - value = value.decode() - - assert not isinstance(value, (float, int)) - match = date_re.match(value) - if match is None: - raise ValueError("invalid date format") - - kw = {k: int(v) for k, v in match.groupdict().items()} - - try: - return date(**kw) - except ValueError: - raise ValueError("invalid date format") from None diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_httpx.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_httpx.py deleted file mode 100644 index a5cb114c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_httpx.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -This file includes code adapted from HTTPX's utility module -(https://github.com/encode/httpx/blob/336204f0121a9aefdebac5cacd81f912bafe8057/httpx/_utils.py). -We implement custom proxy handling to support configurations like `socket_options`, -which are not currently configurable through the HTTPX client. -For more context, see: https://github.com/encode/httpx/discussions/3514 -""" - -from __future__ import annotations - -import ipaddress -from typing import Mapping -from urllib.request import getproxies - - -def is_ipv4_hostname(hostname: str) -> bool: - try: - ipaddress.IPv4Address(hostname.split("/")[0]) - except Exception: - return False - return True - - -def is_ipv6_hostname(hostname: str) -> bool: - try: - ipaddress.IPv6Address(hostname.split("/")[0]) - except Exception: - return False - return True - - -def get_environment_proxies() -> Mapping[str, str | None]: - """ - Gets the proxy mappings based on environment variables. - We use our own logic to parse these variables, as HTTPX - doesn’t allow full configuration of the underlying - transport when proxies are set via environment variables. - """ - - proxy_info = getproxies() - mounts: dict[str, str | None] = {} - - for scheme in ("http", "https", "all"): - if proxy_info.get(scheme): - hostname = proxy_info[scheme] - mounts[f"{scheme}://"] = hostname if "://" in hostname else f"http://{hostname}" - - no_proxy_hosts = [host.strip() for host in proxy_info.get("no", "").split(",")] - for hostname in no_proxy_hosts: - if hostname == "*": - return {} - elif hostname: - if "://" in hostname: - mounts[hostname] = None - elif is_ipv4_hostname(hostname): - mounts[f"all://{hostname}"] = None - elif is_ipv6_hostname(hostname): - mounts[f"all://[{hostname}]"] = None - elif hostname.lower() == "localhost": - mounts[f"all://{hostname}"] = None - else: - mounts[f"all://*{hostname}"] = None - - return mounts diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_json.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_json.py deleted file mode 100644 index 60584214..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_json.py +++ /dev/null @@ -1,35 +0,0 @@ -import json -from typing import Any -from datetime import datetime -from typing_extensions import override - -import pydantic - -from .._compat import model_dump - - -def openapi_dumps(obj: Any) -> bytes: - """ - Serialize an object to UTF-8 encoded JSON bytes. - - Extends the standard json.dumps with support for additional types - commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc. - """ - return json.dumps( - obj, - cls=_CustomEncoder, - # Uses the same defaults as httpx's JSON serialization - ensure_ascii=False, - separators=(",", ":"), - allow_nan=False, - ).encode() - - -class _CustomEncoder(json.JSONEncoder): - @override - def default(self, o: Any) -> Any: - if isinstance(o, datetime): - return o.isoformat() - if isinstance(o, pydantic.BaseModel): - return model_dump(o, exclude_unset=True, mode="json", by_alias=True) - return super().default(o) diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_logs.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_logs.py deleted file mode 100644 index a409705b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_logs.py +++ /dev/null @@ -1,25 +0,0 @@ -import os -import logging - -logger: logging.Logger = logging.getLogger("anthropic") -httpx_logger: logging.Logger = logging.getLogger("httpx") - - -def _basic_config() -> None: - # e.g. [2023-10-05 14:12:26 - anthropic._base_client:818 - DEBUG] HTTP Request: POST http://127.0.0.1:4010/foo/bar "200 OK" - logging.basicConfig( - format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - ) - - -def setup_logging() -> None: - env = os.environ.get("ANTHROPIC_LOG") - if env == "debug": - _basic_config() - logger.setLevel(logging.DEBUG) - httpx_logger.setLevel(logging.DEBUG) - elif env == "info": - _basic_config() - logger.setLevel(logging.INFO) - httpx_logger.setLevel(logging.INFO) diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_path.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_path.py deleted file mode 100644 index 4d6e1e4c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_path.py +++ /dev/null @@ -1,127 +0,0 @@ -from __future__ import annotations - -import re -from typing import ( - Any, - Mapping, - Callable, -) -from urllib.parse import quote - -# Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E). -_DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$") - -_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}") - - -def _quote_path_segment_part(value: str) -> str: - """Percent-encode `value` for use in a URI path segment. - - Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe. - https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 - """ - # quote() already treats unreserved characters (letters, digits, and -._~) - # as safe, so we only need to add sub-delims, ':', and '@'. - # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted. - return quote(value, safe="!$&'()*+,;=:@") - - -def _quote_query_part(value: str) -> str: - """Percent-encode `value` for use in a URI query string. - - Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe. - https://datatracker.ietf.org/doc/html/rfc3986#section-3.4 - """ - return quote(value, safe="!$'()*+,;:@/?") - - -def _quote_fragment_part(value: str) -> str: - """Percent-encode `value` for use in a URI fragment. - - Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe. - https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 - """ - return quote(value, safe="!$&'()*+,;=:@/?") - - -def _interpolate( - template: str, - values: Mapping[str, Any], - quoter: Callable[[str], str], -) -> str: - """Replace {name} placeholders in `template`, quoting each value with `quoter`. - - Placeholder names are looked up in `values`. - - Raises: - KeyError: If a placeholder is not found in `values`. - """ - # re.split with a capturing group returns alternating - # [text, name, text, name, ..., text] elements. - parts = _PLACEHOLDER_RE.split(template) - - for i in range(1, len(parts), 2): - name = parts[i] - if name not in values: - raise KeyError(f"a value for placeholder {{{name}}} was not provided") - val = values[name] - if val is None: - parts[i] = "null" - elif isinstance(val, bool): - parts[i] = "true" if val else "false" - else: - parts[i] = quoter(str(values[name])) - - return "".join(parts) - - -def path_template(template: str, /, **kwargs: Any) -> str: - """Interpolate {name} placeholders in `template` from keyword arguments. - - Args: - template: The template string containing {name} placeholders. - **kwargs: Keyword arguments to interpolate into the template. - - Returns: - The template with placeholders interpolated and percent-encoded. - - Safe characters for percent-encoding are dependent on the URI component. - Placeholders in path and fragment portions are percent-encoded where the `segment` - and `fragment` sets from RFC 3986 respectively are considered safe. - Placeholders in the query portion are percent-encoded where the `query` set from - RFC 3986 §3.3 is considered safe except for = and & characters. - - Raises: - KeyError: If a placeholder is not found in `kwargs`. - ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments). - """ - # Split the template into path, query, and fragment portions. - fragment_template: str | None = None - query_template: str | None = None - - rest = template - if "#" in rest: - rest, fragment_template = rest.split("#", 1) - if "?" in rest: - rest, query_template = rest.split("?", 1) - path_template = rest - - # Interpolate each portion with the appropriate quoting rules. - path_result = _interpolate(path_template, kwargs, _quote_path_segment_part) - - # Reject dot-segments (. and ..) in the final assembled path. The check - # runs after interpolation so that adjacent placeholders or a mix of static - # text and placeholders that together form a dot-segment are caught. - # Also reject percent-encoded dot-segments to protect against incorrectly - # implemented normalization in servers/proxies. - for segment in path_result.split("/"): - if _DOT_SEGMENT_RE.match(segment): - raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed") - - result = path_result - if query_template is not None: - result += "?" + _interpolate(query_template, kwargs, _quote_query_part) - if fragment_template is not None: - result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part) - - return result diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_proxy.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_proxy.py deleted file mode 100644 index 0f239a33..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_proxy.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import Generic, TypeVar, Iterable, cast -from typing_extensions import override - -T = TypeVar("T") - - -class LazyProxy(Generic[T], ABC): - """Implements data methods to pretend that an instance is another instance. - - This includes forwarding attribute access and other methods. - """ - - # Note: we have to special case proxies that themselves return proxies - # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz` - - def __getattr__(self, attr: str) -> object: - proxied = self.__get_proxied__() - if isinstance(proxied, LazyProxy): - return proxied # pyright: ignore - return getattr(proxied, attr) - - @override - def __repr__(self) -> str: - proxied = self.__get_proxied__() - if isinstance(proxied, LazyProxy): - return proxied.__class__.__name__ - return repr(self.__get_proxied__()) - - @override - def __str__(self) -> str: - proxied = self.__get_proxied__() - if isinstance(proxied, LazyProxy): - return proxied.__class__.__name__ - return str(proxied) - - @override - def __dir__(self) -> Iterable[str]: - proxied = self.__get_proxied__() - if isinstance(proxied, LazyProxy): - return [] - return proxied.__dir__() - - @property # type: ignore - @override - def __class__(self) -> type: # pyright: ignore - try: - proxied = self.__get_proxied__() - except Exception: - return type(self) - if issubclass(type(proxied), LazyProxy): - return type(proxied) - return proxied.__class__ - - def __get_proxied__(self) -> T: - return self.__load__() - - def __as_proxied__(self) -> T: - """Helper method that returns the current proxy, typed as the loaded object""" - return cast(T, self) - - @abstractmethod - def __load__(self) -> T: ... diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_reflection.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_reflection.py deleted file mode 100644 index a5ddb23c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_reflection.py +++ /dev/null @@ -1,80 +0,0 @@ -from __future__ import annotations - -import inspect -import typing_extensions -from typing import Any, Callable - - -def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: - """Returns whether or not the given function has a specific parameter""" - sig = inspect.signature(func) - return arg_name in sig.parameters - - -def assert_signatures_in_sync( - source_func: Callable[..., Any], - check_func: Callable[..., Any], - *, - exclude_params: set[str] = set(), -) -> None: - """Ensure that the signature of the second function matches the first.""" - - check_sig = inspect.signature(check_func) - source_sig = inspect.signature(source_func) - - errors: list[str] = [] - - for name, source_param in source_sig.parameters.items(): - if name in exclude_params: - continue - - custom_param = check_sig.parameters.get(name) - if not custom_param: - errors.append(f"the `{name}` param is missing") - continue - - if custom_param.annotation != source_param.annotation: - errors.append( - f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" - ) - continue - - if errors: - raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) - - -def assert_overloads_in_sync( - source_func: Callable[..., Any], - overloaded_func: Callable[..., Any], - *, - exclude_params: set[str] = set(), -) -> None: - """Ensure that every @overload of overloaded_func contains all params from source_func.""" - source_sig = inspect.signature(source_func) - overloads = typing_extensions.get_overloads(overloaded_func) - - if not overloads: - raise AssertionError(f"No @overload definitions found for {overloaded_func!r}") - - errors: list[str] = [] - - for i, overload_fn in enumerate(overloads): - overload_sig = inspect.signature(overload_fn) - for name, source_param in source_sig.parameters.items(): - if name in exclude_params: - continue - - overload_param = overload_sig.parameters.get(name) - if not overload_param: - errors.append(f"overload {i}: `{name}` param is missing") - continue - - if overload_param.annotation != source_param.annotation: - errors.append( - f"overload {i}: types for `{name}` do not match; source={repr(source_param.annotation)} overload={repr(overload_param.annotation)}" - ) - - if errors: - raise AssertionError( - f"{len(errors)} errors encountered when comparing overload signatures:\n\n" + "\n\n".join(errors) - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_resources_proxy.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_resources_proxy.py deleted file mode 100644 index 1e9c6a9e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_resources_proxy.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import annotations - -from typing import Any -from typing_extensions import override - -from ._proxy import LazyProxy - - -class ResourcesProxy(LazyProxy[Any]): - """A proxy for the `anthropic.resources` module. - - This is used so that we can lazily import `anthropic.resources` only when - needed *and* so that users can just import `anthropic` and reference `anthropic.resources` - """ - - @override - def __load__(self) -> Any: - import importlib - - mod = importlib.import_module("anthropic.resources") - return mod - - -resources = ResourcesProxy().__as_proxied__() diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_streams.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_streams.py deleted file mode 100644 index f4a0208f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_streams.py +++ /dev/null @@ -1,12 +0,0 @@ -from typing import Any -from typing_extensions import Iterator, AsyncIterator - - -def consume_sync_iterator(iterator: Iterator[Any]) -> None: - for _ in iterator: - ... - - -async def consume_async_iterator(iterator: AsyncIterator[Any]) -> None: - async for _ in iterator: - ... diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_sync.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_sync.py deleted file mode 100644 index f6027c18..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_sync.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - -import asyncio -import functools -from typing import TypeVar, Callable, Awaitable -from typing_extensions import ParamSpec - -import anyio -import sniffio -import anyio.to_thread - -T_Retval = TypeVar("T_Retval") -T_ParamSpec = ParamSpec("T_ParamSpec") - - -async def to_thread( - func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs -) -> T_Retval: - if sniffio.current_async_library() == "asyncio": - return await asyncio.to_thread(func, *args, **kwargs) - - return await anyio.to_thread.run_sync( - functools.partial(func, *args, **kwargs), - ) - - -# inspired by `asyncer`, https://github.com/tiangolo/asyncer -def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: - """ - Take a blocking function and create an async one that receives the same - positional and keyword arguments. - - Usage: - - ```python - def blocking_func(arg1, arg2, kwarg1=None): - # blocking code - return result - - - result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) - ``` - - ## Arguments - - `function`: a blocking regular callable (e.g. a function) - - ## Return - - An async function that takes the same positional and keyword arguments as the - original one, that when called runs the same original function in a thread worker - and returns the result. - """ - - async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: - return await to_thread(function, *args, **kwargs) - - return wrapper diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_transform.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_transform.py deleted file mode 100644 index 1e7e5ac8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_transform.py +++ /dev/null @@ -1,457 +0,0 @@ -from __future__ import annotations - -import io -import base64 -import pathlib -from typing import Any, Mapping, TypeVar, cast -from datetime import date, datetime -from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints - -import anyio -import pydantic - -from ._utils import ( - is_list, - is_given, - lru_cache, - is_mapping, - is_iterable, - is_sequence, -) -from .._files import is_base64_file_input -from ._compat import get_origin, is_typeddict -from ._typing import ( - is_list_type, - is_union_type, - extract_type_arg, - is_iterable_type, - is_required_type, - is_sequence_type, - is_annotated_type, - strip_annotated_type, -) - -_T = TypeVar("_T") - - -# TODO: support for drilling globals() and locals() -# TODO: ensure works correctly with forward references in all cases - - -PropertyFormat = Literal["iso8601", "base64", "custom"] - - -class PropertyInfo: - """Metadata class to be used in Annotated types to provide information about a given type. - - For example: - - class MyParams(TypedDict): - account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')] - - This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API. - """ - - alias: str | None - format: PropertyFormat | None - format_template: str | None - discriminator: str | None - - def __init__( - self, - *, - alias: str | None = None, - format: PropertyFormat | None = None, - format_template: str | None = None, - discriminator: str | None = None, - ) -> None: - self.alias = alias - self.format = format - self.format_template = format_template - self.discriminator = discriminator - - @override - def __repr__(self) -> str: - return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" - - -def maybe_transform( - data: object, - expected_type: object, -) -> Any | None: - """Wrapper over `transform()` that allows `None` to be passed. - - See `transform()` for more details. - """ - if data is None: - return None - return transform(data, expected_type) - - -# Wrapper over _transform_recursive providing fake types -def transform( - data: _T, - expected_type: object, -) -> _T: - """Transform dictionaries based off of type information from the given type, for example: - - ```py - class Params(TypedDict, total=False): - card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] - - - transformed = transform({"card_id": ""}, Params) - # {'cardID': ''} - ``` - - Any keys / data that does not have type information given will be included as is. - - It should be noted that the transformations that this function does are not represented in the type system. - """ - transformed = _transform_recursive(data, annotation=cast(type, expected_type)) - return cast(_T, transformed) - - -@lru_cache(maxsize=8096) -def _get_annotated_type(type_: type) -> type | None: - """If the given type is an `Annotated` type then it is returned, if not `None` is returned. - - This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]` - """ - if is_required_type(type_): - # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]` - type_ = get_args(type_)[0] - - if is_annotated_type(type_): - return type_ - - return None - - -def _maybe_transform_key(key: str, type_: type) -> str: - """Transform the given `data` based on the annotations provided in `type_`. - - Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata. - """ - annotated_type = _get_annotated_type(type_) - if annotated_type is None: - # no `Annotated` definition for this type, no transformation needed - return key - - # ignore the first argument as it is the actual type - annotations = get_args(annotated_type)[1:] - for annotation in annotations: - if isinstance(annotation, PropertyInfo) and annotation.alias is not None: - return annotation.alias - - return key - - -def _no_transform_needed(annotation: type) -> bool: - return annotation == float or annotation == int - - -def _transform_recursive( - data: object, - *, - annotation: type, - inner_type: type | None = None, -) -> object: - """Transform the given data against the expected type. - - Args: - annotation: The direct type annotation given to the particular piece of data. - This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc - - inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type - is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in - the list can be transformed using the metadata from the container type. - - Defaults to the same value as the `annotation` argument. - """ - from .._compat import model_dump - - if inner_type is None: - inner_type = annotation - - stripped_type = strip_annotated_type(inner_type) - origin = get_origin(stripped_type) or stripped_type - if is_typeddict(stripped_type) and is_mapping(data): - return _transform_typeddict(data, stripped_type) - - if origin == dict and is_mapping(data): - items_type = get_args(stripped_type)[1] - return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} - - if ( - # List[T] - (is_list_type(stripped_type) and is_list(data)) - # Iterable[T] - or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) - # Sequence[T] - or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) - ): - # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually - # intended as an iterable, so we don't transform it. - if isinstance(data, dict): - return cast(object, data) - - inner_type = extract_type_arg(stripped_type, 0) - if _no_transform_needed(inner_type): - # for some types there is no need to transform anything, so we can get a small - # perf boost from skipping that work. - # - # but we still need to convert to a list to ensure the data is json-serializable - if is_list(data): - return data - return list(data) - - return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] - - if is_union_type(stripped_type): - # For union types we run the transformation against all subtypes to ensure that everything is transformed. - # - # TODO: there may be edge cases where the same normalized field name will transform to two different names - # in different subtypes. - for subtype in get_args(stripped_type): - data = _transform_recursive(data, annotation=annotation, inner_type=subtype) - return data - - if isinstance(data, pydantic.BaseModel): - return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None)) - - annotated_type = _get_annotated_type(annotation) - if annotated_type is None: - return data - - # ignore the first argument as it is the actual type - annotations = get_args(annotated_type)[1:] - for annotation in annotations: - if isinstance(annotation, PropertyInfo) and annotation.format is not None: - return _format_data(data, annotation.format, annotation.format_template) - - return data - - -def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: - if isinstance(data, (date, datetime)): - if format_ == "iso8601": - return data.isoformat() - - if format_ == "custom" and format_template is not None: - return data.strftime(format_template) - - if format_ == "base64" and is_base64_file_input(data): - binary: str | bytes | None = None - - if isinstance(data, pathlib.Path): - binary = data.read_bytes() - elif isinstance(data, io.IOBase): - binary = data.read() - - if isinstance(binary, str): # type: ignore[unreachable] - binary = binary.encode() - - if not isinstance(binary, bytes): - raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") - - return base64.b64encode(binary).decode("ascii") - - return data - - -def _transform_typeddict( - data: Mapping[str, object], - expected_type: type, -) -> Mapping[str, object]: - result: dict[str, object] = {} - annotations = get_type_hints(expected_type, include_extras=True) - for key, value in data.items(): - if not is_given(value): - # we don't need to include omitted values here as they'll - # be stripped out before the request is sent anyway - continue - - type_ = annotations.get(key) - if type_ is None: - # we do not have a type annotation for this field, leave it as is - result[key] = value - else: - result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) - return result - - -async def async_maybe_transform( - data: object, - expected_type: object, -) -> Any | None: - """Wrapper over `async_transform()` that allows `None` to be passed. - - See `async_transform()` for more details. - """ - if data is None: - return None - return await async_transform(data, expected_type) - - -async def async_transform( - data: _T, - expected_type: object, -) -> _T: - """Transform dictionaries based off of type information from the given type, for example: - - ```py - class Params(TypedDict, total=False): - card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] - - - transformed = transform({"card_id": ""}, Params) - # {'cardID': ''} - ``` - - Any keys / data that does not have type information given will be included as is. - - It should be noted that the transformations that this function does are not represented in the type system. - """ - transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) - return cast(_T, transformed) - - -async def _async_transform_recursive( - data: object, - *, - annotation: type, - inner_type: type | None = None, -) -> object: - """Transform the given data against the expected type. - - Args: - annotation: The direct type annotation given to the particular piece of data. - This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc - - inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type - is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in - the list can be transformed using the metadata from the container type. - - Defaults to the same value as the `annotation` argument. - """ - from .._compat import model_dump - - if inner_type is None: - inner_type = annotation - - stripped_type = strip_annotated_type(inner_type) - origin = get_origin(stripped_type) or stripped_type - if is_typeddict(stripped_type) and is_mapping(data): - return await _async_transform_typeddict(data, stripped_type) - - if origin == dict and is_mapping(data): - items_type = get_args(stripped_type)[1] - return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} - - if ( - # List[T] - (is_list_type(stripped_type) and is_list(data)) - # Iterable[T] - or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) - # Sequence[T] - or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) - ): - # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually - # intended as an iterable, so we don't transform it. - if isinstance(data, dict): - return cast(object, data) - - inner_type = extract_type_arg(stripped_type, 0) - if _no_transform_needed(inner_type): - # for some types there is no need to transform anything, so we can get a small - # perf boost from skipping that work. - # - # but we still need to convert to a list to ensure the data is json-serializable - if is_list(data): - return data - return list(data) - - return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] - - if is_union_type(stripped_type): - # For union types we run the transformation against all subtypes to ensure that everything is transformed. - # - # TODO: there may be edge cases where the same normalized field name will transform to two different names - # in different subtypes. - for subtype in get_args(stripped_type): - data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) - return data - - if isinstance(data, pydantic.BaseModel): - return model_dump(data, exclude_unset=True, mode="json", exclude=getattr(data, "__api_exclude__", None)) - - annotated_type = _get_annotated_type(annotation) - if annotated_type is None: - return data - - # ignore the first argument as it is the actual type - annotations = get_args(annotated_type)[1:] - for annotation in annotations: - if isinstance(annotation, PropertyInfo) and annotation.format is not None: - return await _async_format_data(data, annotation.format, annotation.format_template) - - return data - - -async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: - if isinstance(data, (date, datetime)): - if format_ == "iso8601": - return data.isoformat() - - if format_ == "custom" and format_template is not None: - return data.strftime(format_template) - - if format_ == "base64" and is_base64_file_input(data): - binary: str | bytes | None = None - - if isinstance(data, pathlib.Path): - binary = await anyio.Path(data).read_bytes() - elif isinstance(data, io.IOBase): - binary = data.read() - - if isinstance(binary, str): # type: ignore[unreachable] - binary = binary.encode() - - if not isinstance(binary, bytes): - raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") - - return base64.b64encode(binary).decode("ascii") - - return data - - -async def _async_transform_typeddict( - data: Mapping[str, object], - expected_type: type, -) -> Mapping[str, object]: - result: dict[str, object] = {} - annotations = get_type_hints(expected_type, include_extras=True) - for key, value in data.items(): - if not is_given(value): - # we don't need to include omitted values here as they'll - # be stripped out before the request is sent anyway - continue - - type_ = annotations.get(key) - if type_ is None: - # we do not have a type annotation for this field, leave it as is - result[key] = value - else: - result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) - return result - - -@lru_cache(maxsize=8096) -def get_type_hints( - obj: Any, - globalns: dict[str, Any] | None = None, - localns: Mapping[str, Any] | None = None, - include_extras: bool = False, -) -> dict[str, Any]: - return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_typing.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_typing.py deleted file mode 100644 index 9ec1944f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_typing.py +++ /dev/null @@ -1,156 +0,0 @@ -from __future__ import annotations - -import sys -import typing -import typing_extensions -from typing import Any, TypeVar, Iterable, cast -from collections import abc as _c_abc -from typing_extensions import ( - TypeIs, - Required, - Annotated, - get_args, - get_origin, -) - -from ._utils import lru_cache -from .._types import InheritsGeneric -from ._compat import is_union as _is_union - - -def is_annotated_type(typ: type) -> bool: - return get_origin(typ) == Annotated - - -def is_list_type(typ: type) -> bool: - return (get_origin(typ) or typ) == list - - -def is_sequence_type(typ: type) -> bool: - origin = get_origin(typ) or typ - return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence - - -def is_iterable_type(typ: type) -> bool: - """If the given type is `typing.Iterable[T]`""" - origin = get_origin(typ) or typ - return origin == Iterable or origin == _c_abc.Iterable - - -def is_union_type(typ: type) -> bool: - return _is_union(get_origin(typ)) - - -def is_required_type(typ: type) -> bool: - return get_origin(typ) == Required - - -def is_typevar(typ: type) -> bool: - # type ignore is required because type checkers - # think this expression will always return False - return type(typ) == TypeVar # type: ignore - - -_TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,) -if sys.version_info >= (3, 12): - _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) # type: ignore[arg-type] - - -def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: - """Return whether the provided argument is an instance of `TypeAliasType`. - - ```python - type Int = int - is_type_alias_type(Int) - # > True - Str = TypeAliasType("Str", str) - is_type_alias_type(Str) - # > True - ``` - """ - return isinstance(tp, _TYPE_ALIAS_TYPES) - - -# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] -@lru_cache(maxsize=8096) -def strip_annotated_type(typ: type) -> type: - if is_required_type(typ) or is_annotated_type(typ): - return strip_annotated_type(cast(type, get_args(typ)[0])) - - return typ - - -def extract_type_arg(typ: type, index: int) -> type: - args = get_args(typ) - try: - return cast(type, args[index]) - except IndexError as err: - raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err - - -def extract_type_var_from_base( - typ: type, - *, - generic_bases: tuple[type, ...], - index: int, - failure_message: str | None = None, -) -> type: - """Given a type like `Foo[T]`, returns the generic type variable `T`. - - This also handles the case where a concrete subclass is given, e.g. - ```py - class MyResponse(Foo[bytes]): - ... - - extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes - ``` - - And where a generic subclass is given: - ```py - _T = TypeVar('_T') - class MyResponse(Foo[_T]): - ... - - extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes - ``` - """ - cls = cast(object, get_origin(typ) or typ) - if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains] - # we're given the class directly - return extract_type_arg(typ, index) - - # if a subclass is given - # --- - # this is needed as __orig_bases__ is not present in the typeshed stubs - # because it is intended to be for internal use only, however there does - # not seem to be a way to resolve generic TypeVars for inherited subclasses - # without using it. - if isinstance(cls, InheritsGeneric): - target_base_class: Any | None = None - for base in cls.__orig_bases__: - if base.__origin__ in generic_bases: - target_base_class = base - break - - if target_base_class is None: - raise RuntimeError( - "Could not find the generic base class;\n" - "This should never happen;\n" - f"Does {cls} inherit from one of {generic_bases} ?" - ) - - extracted = extract_type_arg(target_base_class, index) - if is_typevar(extracted): - # If the extracted type argument is itself a type variable - # then that means the subclass itself is generic, so we have - # to resolve the type argument from the class itself, not - # the base class. - # - # Note: if there is more than 1 type argument, the subclass could - # change the ordering of the type arguments, this is not currently - # supported. - return extract_type_arg(typ, index) - - return extracted - - raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/.venv/lib/python3.12/site-packages/anthropic/_utils/_utils.py b/.venv/lib/python3.12/site-packages/anthropic/_utils/_utils.py deleted file mode 100644 index 771859f5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_utils/_utils.py +++ /dev/null @@ -1,407 +0,0 @@ -from __future__ import annotations - -import os -import re -import inspect -import functools -from typing import ( - Any, - Tuple, - Mapping, - TypeVar, - Callable, - Iterable, - Sequence, - cast, - overload, -) -from pathlib import Path -from datetime import date, datetime -from typing_extensions import TypeGuard - -import sniffio - -from .._types import Omit, NotGiven, FileTypes, HeadersLike - -_T = TypeVar("_T") -_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) -_MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) -_SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) -CallableT = TypeVar("CallableT", bound=Callable[..., Any]) - - -def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: - return [item for sublist in t for item in sublist] - - -def extract_files( - # TODO: this needs to take Dict but variance issues..... - # create protocol type ? - query: Mapping[str, object], - *, - paths: Sequence[Sequence[str]], -) -> list[tuple[str, FileTypes]]: - """Recursively extract files from the given dictionary based on specified paths. - - A path may look like this ['foo', 'files', '', 'data']. - - Note: this mutates the given dictionary. - """ - files: list[tuple[str, FileTypes]] = [] - for path in paths: - files.extend(_extract_items(query, path, index=0, flattened_key=None)) - return files - - -def _extract_items( - obj: object, - path: Sequence[str], - *, - index: int, - flattened_key: str | None, -) -> list[tuple[str, FileTypes]]: - try: - key = path[index] - except IndexError: - if not is_given(obj): - # no value was provided - we can safely ignore - return [] - - # cyclical import - from .._files import assert_is_file_content - - # We have exhausted the path, return the entry we found. - assert flattened_key is not None - - if is_list(obj): - files: list[tuple[str, FileTypes]] = [] - for entry in obj: - assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") - files.append((flattened_key + "[]", cast(FileTypes, entry))) - return files - - assert_is_file_content(obj, key=flattened_key) - return [(flattened_key, cast(FileTypes, obj))] - - index += 1 - if is_dict(obj): - try: - # Remove the field if there are no more dict keys in the path, - # only "" traversal markers or end. - if all(p == "" for p in path[index:]): - item = obj.pop(key) - else: - item = obj[key] - except KeyError: - # Key was not present in the dictionary, this is not indicative of an error - # as the given path may not point to a required field. We also do not want - # to enforce required fields as the API may differ from the spec in some cases. - return [] - if flattened_key is None: - flattened_key = key - else: - flattened_key += f"[{key}]" - return _extract_items( - item, - path, - index=index, - flattened_key=flattened_key, - ) - elif is_list(obj): - if key != "": - return [] - - return flatten( - [ - _extract_items( - item, - path, - index=index, - flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", - ) - for item in obj - ] - ) - - # Something unexpected was passed, just ignore it. - return [] - - -def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: - return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) - - -# Type safe methods for narrowing types with TypeVars. -# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], -# however this cause Pyright to rightfully report errors. As we know we don't -# care about the contained types we can safely use `object` in its place. -# -# There are two separate functions defined, `is_*` and `is_*_t` for different use cases. -# `is_*` is for when you're dealing with an unknown input -# `is_*_t` is for when you're narrowing a known union type to a specific subset - - -def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: - return isinstance(obj, tuple) - - -def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: - return isinstance(obj, tuple) - - -def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: - return isinstance(obj, Sequence) - - -def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: - return isinstance(obj, Sequence) - - -def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: - return isinstance(obj, Mapping) - - -def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: - return isinstance(obj, Mapping) - - -def is_dict(obj: object) -> TypeGuard[dict[object, object]]: - return isinstance(obj, dict) - - -def is_list(obj: object) -> TypeGuard[list[object]]: - return isinstance(obj, list) - - -def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: - return isinstance(obj, Iterable) - - -# copied from https://github.com/Rapptz/RoboDanny -def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: - size = len(seq) - if size == 0: - return "" - - if size == 1: - return seq[0] - - if size == 2: - return f"{seq[0]} {final} {seq[1]}" - - return delim.join(seq[:-1]) + f" {final} {seq[-1]}" - - -def quote(string: str) -> str: - """Add single quotation marks around the given string. Does *not* do any escaping.""" - return f"'{string}'" - - -def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: - """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. - - Useful for enforcing runtime validation of overloaded functions. - - Example usage: - ```py - @overload - def foo(*, a: str) -> str: ... - - - @overload - def foo(*, b: bool) -> str: ... - - - # This enforces the same constraints that a static type checker would - # i.e. that either a or b must be passed to the function - @required_args(["a"], ["b"]) - def foo(*, a: str | None = None, b: bool | None = None) -> str: ... - ``` - """ - - def inner(func: CallableT) -> CallableT: - params = inspect.signature(func).parameters - positional = [ - name - for name, param in params.items() - if param.kind - in { - param.POSITIONAL_ONLY, - param.POSITIONAL_OR_KEYWORD, - } - ] - - @functools.wraps(func) - def wrapper(*args: object, **kwargs: object) -> object: - given_params: set[str] = set() - for i, _ in enumerate(args): - try: - given_params.add(positional[i]) - except IndexError: - raise TypeError( - f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" - ) from None - - for key in kwargs.keys(): - given_params.add(key) - - for variant in variants: - matches = all((param in given_params for param in variant)) - if matches: - break - else: # no break - if len(variants) > 1: - variations = human_join( - ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants] - ) - msg = f"Missing required arguments; Expected either {variations} arguments to be given" - else: - assert len(variants) > 0 - - # TODO: this error message is not deterministic - missing = list(set(variants[0]) - given_params) - if len(missing) > 1: - msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" - else: - msg = f"Missing required argument: {quote(missing[0])}" - raise TypeError(msg) - return func(*args, **kwargs) - - return wrapper # type: ignore - - return inner - - -_K = TypeVar("_K") -_V = TypeVar("_V") - - -@overload -def strip_not_given(obj: None) -> None: ... - - -@overload -def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... - - -@overload -def strip_not_given(obj: object) -> object: ... - - -def strip_not_given(obj: object | None) -> object: - """Remove all top-level keys where their values are instances of `NotGiven`""" - if obj is None: - return None - - if not is_mapping(obj): - return obj - - return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} - - -def coerce_integer(val: str) -> int: - return int(val, base=10) - - -def coerce_float(val: str) -> float: - return float(val) - - -def coerce_boolean(val: str) -> bool: - return val == "true" or val == "1" or val == "on" - - -def maybe_coerce_integer(val: str | None) -> int | None: - if val is None: - return None - return coerce_integer(val) - - -def maybe_coerce_float(val: str | None) -> float | None: - if val is None: - return None - return coerce_float(val) - - -def maybe_coerce_boolean(val: str | None) -> bool | None: - if val is None: - return None - return coerce_boolean(val) - - -def removeprefix(string: str, prefix: str) -> str: - """Remove a prefix from a string. - - Backport of `str.removeprefix` for Python < 3.9 - """ - if string.startswith(prefix): - return string[len(prefix) :] - return string - - -def removesuffix(string: str, suffix: str) -> str: - """Remove a suffix from a string. - - Backport of `str.removesuffix` for Python < 3.9 - """ - if string.endswith(suffix): - return string[: -len(suffix)] - return string - - -def file_from_path(path: str) -> FileTypes: - contents = Path(path).read_bytes() - file_name = os.path.basename(path) - return (file_name, contents) - - -def get_required_header(headers: HeadersLike, header: str) -> str: - lower_header = header.lower() - if is_mapping_t(headers): - # mypy doesn't understand the type narrowing here - for k, v in headers.items(): # type: ignore - if k.lower() == lower_header and isinstance(v, str): - return v - - # to deal with the case where the header looks like Stainless-Event-Id - intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) - - for normalized_header in [header, lower_header, header.upper(), intercaps_header]: - value = headers.get(normalized_header) - if value: - return value - - raise ValueError(f"Could not find {header} header") - - -def get_async_library() -> str: - try: - return sniffio.current_async_library() - except Exception: - return "false" - - -def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: - """A version of functools.lru_cache that retains the type signature - for the wrapped function arguments. - """ - wrapper = functools.lru_cache( # noqa: TID251 - maxsize=maxsize, - ) - return cast(Any, wrapper) # type: ignore[no-any-return] - - -def json_safe(data: object) -> object: - """Translates a mapping / sequence recursively in the same fashion - as `pydantic` v2's `model_dump(mode="json")`. - """ - if is_mapping(data): - return {json_safe(key): json_safe(value) for key, value in data.items()} - - if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): - return [json_safe(item) for item in data] - - if isinstance(data, (datetime, date)): - return data.isoformat() - - return data diff --git a/.venv/lib/python3.12/site-packages/anthropic/_version.py b/.venv/lib/python3.12/site-packages/anthropic/_version.py deleted file mode 100644 index 92734611..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/_version.py +++ /dev/null @@ -1,4 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -__title__ = "anthropic" -__version__ = "0.97.0" # x-release-please-version diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/.keep b/.venv/lib/python3.12/site-packages/anthropic/lib/.keep deleted file mode 100644 index 5e2c99fd..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/.keep +++ /dev/null @@ -1,4 +0,0 @@ -File generated from our OpenAPI spec by Stainless. - -This directory can be used to store custom files to expand the SDK. -It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/lib/__init__.py deleted file mode 100644 index 076480d7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from ._files import files_from_dir as files_from_dir, async_files_from_dir as async_files_from_dir diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/_extras/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/lib/_extras/__init__.py deleted file mode 100644 index 4e3037ee..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/_extras/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from ._google_auth import google_auth as google_auth diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/_extras/_common.py b/.venv/lib/python3.12/site-packages/anthropic/lib/_extras/_common.py deleted file mode 100644 index 5d2b7f6a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/_extras/_common.py +++ /dev/null @@ -1,13 +0,0 @@ -from ..._exceptions import AnthropicError - -INSTRUCTIONS = """ - -Anthropic error: missing required dependency `{library}`. - - $ pip install anthropic[{extra}] -""" - - -class MissingDependencyError(AnthropicError): - def __init__(self, *, library: str, extra: str) -> None: - super().__init__(INSTRUCTIONS.format(library=library, extra=extra)) diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/_extras/_google_auth.py b/.venv/lib/python3.12/site-packages/anthropic/lib/_extras/_google_auth.py deleted file mode 100644 index 16cc7909..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/_extras/_google_auth.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any -from typing_extensions import ClassVar, override - -from ._common import MissingDependencyError -from ..._utils import LazyProxy - -if TYPE_CHECKING: - import google.auth # type: ignore - - google_auth = google.auth - - -class GoogleAuthProxy(LazyProxy[Any]): - should_cache: ClassVar[bool] = True - - @override - def __load__(self) -> Any: - try: - import google.auth # type: ignore - except ImportError as err: - raise MissingDependencyError(extra="vertex", library="google-auth") from err - - return google.auth - - -if not TYPE_CHECKING: - google_auth = GoogleAuthProxy() diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/_files.py b/.venv/lib/python3.12/site-packages/anthropic/lib/_files.py deleted file mode 100644 index ad7b7e57..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/_files.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import annotations - -import os -from pathlib import Path - -import anyio - -from .._types import FileTypes - - -def files_from_dir(directory: str | os.PathLike[str]) -> list[FileTypes]: - path = Path(directory) - - files: list[FileTypes] = [] - _collect_files(path, path.parent, files) - return files - - -def _collect_files(directory: Path, relative_to: Path, files: list[FileTypes]) -> None: - for path in directory.iterdir(): - if path.is_dir(): - _collect_files(path, relative_to, files) - continue - - files.append((path.relative_to(relative_to).as_posix(), path.read_bytes())) - - -async def async_files_from_dir(directory: str | os.PathLike[str]) -> list[FileTypes]: - path = anyio.Path(directory) - - files: list[FileTypes] = [] - await _async_collect_files(path, path.parent, files) - return files - - -async def _async_collect_files(directory: anyio.Path, relative_to: anyio.Path, files: list[FileTypes]) -> None: - async for path in directory.iterdir(): - if await path.is_dir(): - await _async_collect_files(path, relative_to, files) - continue - - files.append((path.relative_to(relative_to).as_posix(), await path.read_bytes())) diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/_parse/_response.py b/.venv/lib/python3.12/site-packages/anthropic/lib/_parse/_response.py deleted file mode 100644 index ee8326e5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/_parse/_response.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -from typing_extensions import TypeVar - -from ..._types import NotGiven -from ..._models import TypeAdapter, construct_type_unchecked -from ..._utils._utils import is_given -from ...types.message import Message -from ...types.parsed_message import ParsedMessage, ParsedTextBlock, ParsedContentBlock -from ...types.beta.beta_message import BetaMessage -from ...types.beta.parsed_beta_message import ParsedBetaMessage, ParsedBetaTextBlock, ParsedBetaContentBlock - -ResponseFormatT = TypeVar("ResponseFormatT", default=None) - - -def parse_text(text: str, output_format: ResponseFormatT | NotGiven) -> ResponseFormatT | None: - if is_given(output_format): - adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) - return adapted_type.validate_json(text) - return None - - -def parse_beta_response( - *, - output_format: ResponseFormatT | NotGiven, - response: BetaMessage, -) -> ParsedBetaMessage[ResponseFormatT]: - content_list: list[ParsedBetaContentBlock[ResponseFormatT]] = [] - for content in response.content: - if content.type == "text": - content_list.append( - construct_type_unchecked( - type_=ParsedBetaTextBlock[ResponseFormatT], - value={**content.to_dict(), "parsed_output": parse_text(content.text, output_format)}, - ) - ) - else: - content_list.append(content) # type: ignore - - return construct_type_unchecked( - type_=ParsedBetaMessage[ResponseFormatT], - value={ - **response.to_dict(), - "content": content_list, - }, - ) - - -def parse_response( - *, - output_format: ResponseFormatT | NotGiven, - response: Message, -) -> ParsedMessage[ResponseFormatT]: - content_list: list[ParsedContentBlock[ResponseFormatT]] = [] - for content in response.content: - if content.type == "text": - content_list.append( - construct_type_unchecked( - type_=ParsedTextBlock[ResponseFormatT], - value={**content.to_dict(), "parsed_output": parse_text(content.text, output_format)}, - ) - ) - else: - content_list.append(content) # type: ignore - - return construct_type_unchecked( - type_=ParsedMessage[ResponseFormatT], - value={ - **response.to_dict(), - "content": content_list, - }, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/_parse/_transform.py b/.venv/lib/python3.12/site-packages/anthropic/lib/_parse/_transform.py deleted file mode 100644 index 755ad280..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/_parse/_transform.py +++ /dev/null @@ -1,171 +0,0 @@ -from __future__ import annotations - -import inspect -from typing import Any, Literal, Optional, cast -from typing_extensions import assert_never - -import pydantic - -from ..._utils import is_list - -SupportedTypes = Literal[ - "object", - "array", - "string", - "integer", - "number", - "boolean", - "null", -] - -SupportedStringFormats = { - "date-time", - "time", - "date", - "duration", - "email", - "hostname", - "uri", - "ipv4", - "ipv6", - "uuid", -} - - -def get_transformed_string( - schema: dict[str, Any], -) -> dict[str, Any]: - """Transforms a JSON schema of type string to ensure it conforms to the API's expectations. - - Specifically, it ensures that if the schema is of type "string" and does not already - specify a "format", it sets the format to "text". - - Args: - schema: The original JSON schema. - - Returns: - The transformed JSON schema. - """ - if schema.get("type") == "string" and "format" not in schema: - schema["format"] = "text" - return schema - - -def transform_schema( - json_schema: type[pydantic.BaseModel] | dict[str, Any], -) -> dict[str, Any]: - """ - Transforms a JSON schema to ensure it conforms to the API's expectations. - - Args: - json_schema (Dict[str, Any]): The original JSON schema. - - Returns: - The transformed JSON schema. - - Examples: - >>> transform_schema( - ... { - ... "type": "integer", - ... "minimum": 1, - ... "maximum": 10, - ... "description": "A number", - ... } - ... ) - {'type': 'integer', 'description': 'A number\n\n{minimum: 1, maximum: 10}'} - """ - if inspect.isclass(json_schema) and issubclass(json_schema, pydantic.BaseModel): # pyright: ignore[reportUnnecessaryIsInstance] - json_schema = json_schema.model_json_schema() - - strict_schema: dict[str, Any] = {} - json_schema = {**json_schema} - - ref = json_schema.pop("$ref", None) - if ref is not None: - strict_schema["$ref"] = ref - return strict_schema - - defs = json_schema.pop("$defs", None) - if defs is not None: - strict_defs: dict[str, Any] = {} - strict_schema["$defs"] = strict_defs - - for name, schema in defs.items(): - strict_defs[name] = transform_schema(schema) - - type_: Optional[SupportedTypes] = json_schema.pop("type", None) - any_of = json_schema.pop("anyOf", None) - one_of = json_schema.pop("oneOf", None) - all_of = json_schema.pop("allOf", None) - - if is_list(any_of): - strict_schema["anyOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in any_of] - elif is_list(one_of): - strict_schema["anyOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in one_of] - elif is_list(all_of): - strict_schema["allOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in all_of] - else: - if type_ is None: - raise ValueError("Schema must have a 'type', 'anyOf', 'oneOf', or 'allOf' field.") - - strict_schema["type"] = type_ - - enum = json_schema.pop("enum", None) - if is_list(enum): - strict_schema["enum"] = enum - - description = json_schema.pop("description", None) - if description is not None: - strict_schema["description"] = description - - title = json_schema.pop("title", None) - if title is not None: - strict_schema["title"] = title - - if type_ == "object": - strict_schema["properties"] = { - key: transform_schema(prop_schema) for key, prop_schema in json_schema.pop("properties", {}).items() - } - json_schema.pop("additionalProperties", None) - strict_schema["additionalProperties"] = False - - required = json_schema.pop("required", None) - if required is not None: - strict_schema["required"] = required - - elif type_ == "string": - format = json_schema.pop("format", None) - if format and format in SupportedStringFormats: - strict_schema["format"] = format - elif format: - # add it back so its treated as an extra property and appended to the description - json_schema["format"] = format - elif type_ == "array": - items = json_schema.pop("items", None) - if items is not None: - strict_schema["items"] = transform_schema(items) - - min_items = json_schema.pop("minItems", None) - if min_items is not None and min_items == 0 or min_items == 1: - strict_schema["minItems"] = min_items - elif min_items is not None: - # add it back so its treated as an extra property and appended to the description - json_schema["minItems"] = min_items - - elif type_ == "boolean" or type_ == "integer" or type_ == "number" or type_ == "null" or type_ is None: - pass - else: - assert_never(type_) - - # if there are any propes leftover then they aren't supported, so we add them to the description - # so that the model *might* follow them. - if json_schema: - description = strict_schema.get("description") - strict_schema["description"] = ( - (description + "\n\n" if description is not None else "") - + "{" - + ", ".join(f"{key}: {value}" for key, value in json_schema.items()) - + "}" - ) - - return strict_schema diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/_stainless_helpers.py b/.venv/lib/python3.12/site-packages/anthropic/lib/_stainless_helpers.py deleted file mode 100644 index 6b894142..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/_stainless_helpers.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Tracking for SDK helper usage via the x-stainless-helper header.""" - -from __future__ import annotations - -from typing import Any, Dict, cast - -_HELPER_ATTR = "_stainless_helper" - - -def tag_helper(obj: Any, name: str) -> None: - """Mark an object as created by a named SDK helper.""" - try: - object.__setattr__(obj, _HELPER_ATTR, name) - except (AttributeError, TypeError): - pass - - -def get_helper_tag(obj: object) -> str | None: - """Get the helper name from an object, if any.""" - return getattr(obj, _HELPER_ATTR, None) # type: ignore[return-value] - - -def collect_helpers( - tools: Any = None, - messages: Any = None, -) -> list[str]: - """Collect deduplicated helper names from tools and messages.""" - helpers: set[str] = set() - - if tools: - for tool in tools: - tag = get_helper_tag(tool) - if tag is not None: - helpers.add(tag) - - if messages: - for message in messages: - tag = get_helper_tag(message) - if tag is not None: - helpers.add(tag) - - # Check content blocks within messages - if isinstance(message, dict): - blocks: Any = cast(Dict[str, Any], message).get("content") - else: - blocks = getattr(message, "content", None) - if isinstance(blocks, list): - for block in cast(list[object], blocks): - tag = get_helper_tag(block) - if tag is not None: - helpers.add(tag) - - return list(helpers) - - -def stainless_helper_header( - tools: Any = None, - messages: Any = None, -) -> dict[str, str]: - """Build x-stainless-helper header dict from tools and messages. - - Returns an empty dict if no helpers are found. - """ - helpers = collect_helpers(tools, messages) - if not helpers: - return {} - return {"x-stainless-helper": ", ".join(helpers)} - - -def stainless_helper_header_from_file(file: object) -> dict[str, str]: - """Build x-stainless-helper header dict from a file object.""" - tag = get_helper_tag(file) - if tag is None: - return {} - return {"x-stainless-helper": tag} diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/aws/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/lib/aws/__init__.py deleted file mode 100644 index 4fc4e6a6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/aws/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from ._client import AnthropicAWS as AnthropicAWS, AsyncAnthropicAWS as AsyncAnthropicAWS diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/aws/_auth.py b/.venv/lib/python3.12/site-packages/anthropic/lib/aws/_auth.py deleted file mode 100644 index 9da6e357..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/aws/_auth.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -import httpx - -from ..._utils import lru_cache - -if TYPE_CHECKING: - import boto3 - - -@lru_cache(maxsize=512) -def _get_session( - *, - aws_access_key: str | None, - aws_secret_key: str | None, - aws_session_token: str | None, - region: str | None, - profile: str | None, -) -> boto3.Session: - import boto3 - - return boto3.Session( - profile_name=profile, - region_name=region, - aws_access_key_id=aws_access_key, - aws_secret_access_key=aws_secret_key, - aws_session_token=aws_session_token, - ) - - -def get_auth_headers( - *, - method: str, - url: str, - headers: httpx.Headers, - aws_access_key: str | None, - aws_secret_key: str | None, - aws_session_token: str | None, - region: str | None, - profile: str | None, - data: str | None, - service_name: str, -) -> dict[str, str]: - from botocore.auth import SigV4Auth - from botocore.awsrequest import AWSRequest - - session = _get_session( - profile=profile, - region=region, - aws_access_key=aws_access_key, - aws_secret_key=aws_secret_key, - aws_session_token=aws_session_token, - ) - - # The connection header may be stripped by a proxy somewhere, so the receiver - # of this message may not see this header, so we remove it from the set of headers - # that are signed. - new_headers = {k: v for k, v in dict(headers).items() if k.lower() != "connection"} - - request = AWSRequest(method=method.upper(), url=url, headers=new_headers, data=data) - credentials = session.get_credentials() - if not credentials: - raise RuntimeError("Could not resolve AWS credentials from session") - - signer = SigV4Auth(credentials, service_name, session.region_name) - signer.add_auth(request) - - prepped = request.prepare() - - return {key: value for key, value in dict(prepped.headers).items() if value is not None} diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/aws/_client.py b/.venv/lib/python3.12/site-packages/anthropic/lib/aws/_client.py deleted file mode 100644 index 2564e76f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/aws/_client.py +++ /dev/null @@ -1,401 +0,0 @@ -from __future__ import annotations - -from typing import Any, Mapping -from typing_extensions import Self, override - -import httpx - -from ..._types import NOT_GIVEN, Omit, Headers, Timeout, NotGiven -from ..._client import Anthropic, AsyncAnthropic -from ._credentials import ( - resolve_region, - resolve_api_key, - resolve_base_url, - resolve_auth_mode, - resolve_workspace_id, - validate_credentials, -) -from ..._exceptions import AnthropicError -from ..._base_client import DEFAULT_MAX_RETRIES - - -class AnthropicAWS(Anthropic): - aws_access_key: str | None - aws_secret_key: str | None - aws_region: str | None - aws_profile: str | None - aws_session_token: str | None - workspace_id: str | None - _use_sigv4: bool - _skip_auth: bool - - def __init__( - self, - *, - api_key: str | None = None, - aws_access_key: str | None = None, - aws_secret_key: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - aws_session_token: str | None = None, - workspace_id: str | None = None, - skip_auth: bool = False, - base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.Client | None = None, - _strict_response_validation: bool = False, - # Passed through to parent but not used for AWS auth - auth_token: str | None = None, - ) -> None: - self._skip_auth = skip_auth - - validate_credentials(aws_access_key=aws_access_key, aws_secret_key=aws_secret_key) - - if skip_auth: - self._use_sigv4 = False - resolved_api_key = None - else: - self._use_sigv4 = resolve_auth_mode( - api_key=api_key, - aws_access_key=aws_access_key, - aws_secret_key=aws_secret_key, - aws_profile=aws_profile, - ) - resolved_api_key = resolve_api_key(api_key=api_key, use_sigv4=self._use_sigv4) - - resolved_region = resolve_region(aws_region) - - if self._use_sigv4 and resolved_region is None: - raise AnthropicError( - "No AWS region was provided. Set the `aws_region` argument or the `AWS_REGION`/`AWS_DEFAULT_REGION` environment variable." - ) - - self.aws_access_key = aws_access_key - self.aws_secret_key = aws_secret_key - self.aws_region = resolved_region - self.aws_profile = aws_profile - self.aws_session_token = aws_session_token - - if skip_auth: - self.workspace_id = workspace_id - else: - resolved_workspace_id = resolve_workspace_id(workspace_id) - if resolved_workspace_id is None: - raise AnthropicError( - "No workspace ID found. Set the `workspace_id` argument or the `ANTHROPIC_AWS_WORKSPACE_ID` environment variable." - ) - self.workspace_id = resolved_workspace_id - - if not skip_auth: - resolved_base_url = resolve_base_url( - str(base_url) if base_url is not None else None, - region=resolved_region, - ) - if resolved_base_url is None: - raise AnthropicError( - "No AWS region was provided and no base_url was given. " - "Set the `aws_region` argument, the `AWS_REGION`/`AWS_DEFAULT_REGION` environment variable, " - "or provide a `base_url` directly." - ) - base_url = resolved_base_url - - super().__init__( - api_key=resolved_api_key, - auth_token=auth_token, - base_url=base_url, # type: ignore[arg-type] - timeout=timeout, - max_retries=max_retries, - default_headers=default_headers, - default_query=default_query, - http_client=http_client, - _strict_response_validation=_strict_response_validation, - ) - - @property - @override - def default_headers(self) -> dict[str, str | Omit]: - headers = {**super().default_headers} - if self.workspace_id is not None: - headers["anthropic-workspace-id"] = self.workspace_id - return headers - - @property - @override - def _api_key_auth(self) -> dict[str, str]: - if self._use_sigv4 or self._skip_auth: - return {} - return super()._api_key_auth - - @override - def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: - if self._use_sigv4 or self._skip_auth: - return - super()._validate_headers(headers, custom_headers) - - @override - def _prepare_request(self, request: httpx.Request) -> None: - if not self._use_sigv4: - return - - from ._auth import get_auth_headers - - data = request.read().decode() - - headers = get_auth_headers( - method=request.method, - url=str(request.url), - headers=request.headers, - aws_access_key=self.aws_access_key, - aws_secret_key=self.aws_secret_key, - aws_session_token=self.aws_session_token, - region=self.aws_region, - profile=self.aws_profile, - data=data, - service_name="aws-external-anthropic", - ) - request.headers.update(headers) - - @override - def copy( - self, - *, - api_key: str | None = None, - aws_access_key: str | None = None, - aws_secret_key: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - aws_session_token: str | None = None, - workspace_id: str | None = None, - skip_auth: bool | None = None, - auth_token: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - # If region is changing and no explicit base_url, let __init__ derive it - resolved_base_url = base_url or (None if aws_region else self.base_url) - - return super().copy( - api_key=api_key or self.api_key, - auth_token=auth_token, - base_url=resolved_base_url, - timeout=timeout, - http_client=http_client, - max_retries=max_retries, - default_headers=default_headers, - set_default_headers=set_default_headers, - default_query=default_query, - set_default_query=set_default_query, - _extra_kwargs={ - "aws_access_key": aws_access_key or self.aws_access_key, - "aws_secret_key": aws_secret_key or self.aws_secret_key, - "aws_region": aws_region or self.aws_region, - "aws_profile": aws_profile or self.aws_profile, - "aws_session_token": aws_session_token or self.aws_session_token, - "workspace_id": workspace_id or self.workspace_id, - "skip_auth": skip_auth if skip_auth is not None else self._skip_auth, - **_extra_kwargs, - }, - ) - - with_options = copy - - -class AsyncAnthropicAWS(AsyncAnthropic): - aws_access_key: str | None - aws_secret_key: str | None - aws_region: str | None - aws_profile: str | None - aws_session_token: str | None - workspace_id: str | None - _use_sigv4: bool - _skip_auth: bool - - def __init__( - self, - *, - api_key: str | None = None, - aws_access_key: str | None = None, - aws_secret_key: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - aws_session_token: str | None = None, - workspace_id: str | None = None, - skip_auth: bool = False, - base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.AsyncClient | None = None, - _strict_response_validation: bool = False, - # Accepted for compatibility with AsyncAnthropic.copy() but not used - auth_token: str | None = None, - ) -> None: - self._skip_auth = skip_auth - - validate_credentials(aws_access_key=aws_access_key, aws_secret_key=aws_secret_key) - - if skip_auth: - self._use_sigv4 = False - resolved_api_key = None - else: - self._use_sigv4 = resolve_auth_mode( - api_key=api_key, - aws_access_key=aws_access_key, - aws_secret_key=aws_secret_key, - aws_profile=aws_profile, - ) - resolved_api_key = resolve_api_key(api_key=api_key, use_sigv4=self._use_sigv4) - - resolved_region = resolve_region(aws_region) - - if self._use_sigv4 and resolved_region is None: - raise AnthropicError( - "No AWS region was provided. Set the `aws_region` argument or the `AWS_REGION`/`AWS_DEFAULT_REGION` environment variable." - ) - - self.aws_access_key = aws_access_key - self.aws_secret_key = aws_secret_key - self.aws_region = resolved_region - self.aws_profile = aws_profile - self.aws_session_token = aws_session_token - - if skip_auth: - self.workspace_id = workspace_id - else: - resolved_workspace_id = resolve_workspace_id(workspace_id) - if resolved_workspace_id is None: - raise AnthropicError( - "No workspace ID found. Set the `workspace_id` argument or the `ANTHROPIC_AWS_WORKSPACE_ID` environment variable." - ) - self.workspace_id = resolved_workspace_id - - if not skip_auth: - resolved_base_url = resolve_base_url( - str(base_url) if base_url is not None else None, - region=resolved_region, - ) - if resolved_base_url is None: - raise AnthropicError( - "No AWS region was provided and no base_url was given. " - "Set the `aws_region` argument, the `AWS_REGION`/`AWS_DEFAULT_REGION` environment variable, " - "or provide a `base_url` directly." - ) - base_url = resolved_base_url - - super().__init__( - api_key=resolved_api_key, - auth_token=auth_token, - base_url=base_url, # type: ignore[arg-type] - timeout=timeout, - max_retries=max_retries, - default_headers=default_headers, - default_query=default_query, - http_client=http_client, - _strict_response_validation=_strict_response_validation, - ) - - @property - @override - def default_headers(self) -> dict[str, str | Omit]: - headers = {**super().default_headers} - if self.workspace_id is not None: - headers["anthropic-workspace-id"] = self.workspace_id - return headers - - @property - @override - def _api_key_auth(self) -> dict[str, str]: - if self._use_sigv4 or self._skip_auth: - return {} - return super()._api_key_auth - - @override - def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: - if self._use_sigv4 or self._skip_auth: - return - super()._validate_headers(headers, custom_headers) - - @override - async def _prepare_request(self, request: httpx.Request) -> None: - if not self._use_sigv4: - return - - from ._auth import get_auth_headers - - data = request.read().decode() - - headers = get_auth_headers( - method=request.method, - url=str(request.url), - headers=request.headers, - aws_access_key=self.aws_access_key, - aws_secret_key=self.aws_secret_key, - aws_session_token=self.aws_session_token, - region=self.aws_region, - profile=self.aws_profile, - data=data, - service_name="aws-external-anthropic", - ) - request.headers.update(headers) - - @override - def copy( - self, - *, - api_key: str | None = None, - aws_access_key: str | None = None, - aws_secret_key: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - aws_session_token: str | None = None, - workspace_id: str | None = None, - skip_auth: bool | None = None, - auth_token: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - # If region is changing and no explicit base_url, let __init__ derive it - resolved_base_url = base_url or (None if aws_region else self.base_url) - - return super().copy( - api_key=api_key or self.api_key, - auth_token=auth_token, - base_url=resolved_base_url, - timeout=timeout, - http_client=http_client, - max_retries=max_retries, - default_headers=default_headers, - set_default_headers=set_default_headers, - default_query=default_query, - set_default_query=set_default_query, - _extra_kwargs={ - "aws_access_key": aws_access_key or self.aws_access_key, - "aws_secret_key": aws_secret_key or self.aws_secret_key, - "aws_region": aws_region or self.aws_region, - "aws_profile": aws_profile or self.aws_profile, - "aws_session_token": aws_session_token or self.aws_session_token, - "workspace_id": workspace_id or self.workspace_id, - "skip_auth": skip_auth if skip_auth is not None else self._skip_auth, - **_extra_kwargs, - }, - ) - - with_options = copy diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/aws/_credentials.py b/.venv/lib/python3.12/site-packages/anthropic/lib/aws/_credentials.py deleted file mode 100644 index d6f591dd..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/aws/_credentials.py +++ /dev/null @@ -1,129 +0,0 @@ -from __future__ import annotations - -import os -from typing import Sequence - - -def validate_credentials( - *, - aws_access_key: str | None, - aws_secret_key: str | None, -) -> None: - """Raise if only one of aws_access_key/aws_secret_key is provided.""" - if (aws_access_key is not None) != (aws_secret_key is not None): - provided = "aws_access_key" if aws_access_key is not None else "aws_secret_key" - missing = "aws_secret_key" if aws_access_key is not None else "aws_access_key" - raise ValueError( - f"`{provided}` was provided without `{missing}`. " - f"Both must be provided together, or neither (to use the default credential chain)." - ) - - -def _read_env(*env_vars: str) -> str | None: - """Return the first non-None value from the given env vars, or None.""" - for var in env_vars: - value = os.environ.get(var) - if value is not None: - return value - return None - - -def resolve_auth_mode( - *, - api_key: str | None, - aws_access_key: str | None, - aws_secret_key: str | None, - aws_profile: str | None, - api_key_env_vars: Sequence[str] = ("ANTHROPIC_AWS_API_KEY",), -) -> bool: - """Determine whether to use SigV4 auth. Returns True for SigV4, False for API key. - - Auth precedence: - 1. api_key constructor arg → API key mode - 2. aws_access_key + aws_secret_key constructor args → SigV4 - 3. aws_profile constructor arg → SigV4 - 4. API key env var(s) → API key mode (checked in order; first match wins) - 5. Default AWS credential chain → SigV4 - """ - if api_key is not None: - return False - - if aws_access_key is not None or aws_secret_key is not None: - return True - - if aws_profile is not None: - return True - - # No explicit constructor args that signal SigV4 — check env vars - if _read_env(*api_key_env_vars) is not None: - return False - - # Fall back to default AWS credential chain - return True - - -def resolve_api_key( - *, - api_key: str | None, - use_sigv4: bool, - api_key_env_vars: Sequence[str] = ("ANTHROPIC_AWS_API_KEY",), -) -> str | None: - """Resolve the API key. Returns None if using SigV4.""" - if api_key is not None: - return api_key - - if not use_sigv4: - # Must be from env var - return _read_env(*api_key_env_vars) - - return None - - -def resolve_region(aws_region: str | None) -> str | None: - """Resolve the AWS region from constructor arg or env var. - - Does not silently default — returns None if no region is available. - """ - if aws_region is not None: - return aws_region - - return os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") - - -def resolve_workspace_id( - workspace_id: str | None, - *, - workspace_id_env_vars: Sequence[str] = ("ANTHROPIC_AWS_WORKSPACE_ID",), -) -> str | None: - """Resolve the workspace ID from constructor arg or env var(s). - - Returns None if no workspace ID is available (caller should raise). - """ - if workspace_id is not None: - return workspace_id - - return _read_env(*workspace_id_env_vars) - - -def resolve_base_url( - base_url: str | None, - *, - region: str | None, - base_url_env_vars: Sequence[str] = ("ANTHROPIC_AWS_BASE_URL",), - url_template: str = "https://aws-external-anthropic.{region}.api.aws", -) -> str | None: - """Resolve the base URL from constructor arg, env var, or region. - - Returns None if no base URL is resolvable (caller should raise). - """ - if base_url is not None: - return base_url - - env_url = _read_env(*base_url_env_vars) - if env_url is not None: - return env_url - - if region is not None: - return url_template.format(region=region) - - return None diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/__init__.py deleted file mode 100644 index e4fac291..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from ._client import AnthropicBedrock as AnthropicBedrock, AsyncAnthropicBedrock as AsyncAnthropicBedrock -from ._mantle import ( - AnthropicBedrockMantle as AnthropicBedrockMantle, - AsyncAnthropicBedrockMantle as AsyncAnthropicBedrockMantle, -) diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_auth.py b/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_auth.py deleted file mode 100644 index 0a8b2109..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_auth.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -import httpx - -from ..._utils import lru_cache - -if TYPE_CHECKING: - import boto3 - - -@lru_cache(maxsize=512) -def _get_session( - *, - aws_access_key: str | None, - aws_secret_key: str | None, - aws_session_token: str | None, - region: str | None, - profile: str | None, -) -> boto3.Session: - import boto3 - - return boto3.Session( - profile_name=profile, - region_name=region, - aws_access_key_id=aws_access_key, - aws_secret_access_key=aws_secret_key, - aws_session_token=aws_session_token, - ) - - -def get_auth_headers( - *, - method: str, - url: str, - headers: httpx.Headers, - aws_access_key: str | None, - aws_secret_key: str | None, - aws_session_token: str | None, - region: str | None, - profile: str | None, - data: str | None, -) -> dict[str, str]: - from botocore.auth import SigV4Auth - from botocore.awsrequest import AWSRequest - - session = _get_session( - profile=profile, - region=region, - aws_access_key=aws_access_key, - aws_secret_key=aws_secret_key, - aws_session_token=aws_session_token, - ) - - # The connection header may be stripped by a proxy somewhere, so the receiver - # of this message may not see this header, so we remove it from the set of headers - # that are signed. - headers = headers.copy() - del headers["connection"] - - request = AWSRequest(method=method.upper(), url=url, headers=headers, data=data) - credentials = session.get_credentials() - if not credentials: - raise RuntimeError("could not resolve credentials from session") - - signer = SigV4Auth(credentials, "bedrock", session.region_name) - signer.add_auth(request) - - prepped = request.prepare() - - return {key: value for key, value in dict(prepped.headers).items() if value is not None} diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_beta.py b/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_beta.py deleted file mode 100644 index f2a91b42..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_beta.py +++ /dev/null @@ -1,102 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ._beta_messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, - MessagesWithStreamingResponse, - AsyncMessagesWithStreamingResponse, -) - -__all__ = ["Beta", "AsyncBeta"] - - -class Beta(SyncAPIResource): - @cached_property - def messages(self) -> Messages: - return Messages(self._client) - - @cached_property - def with_raw_response(self) -> BetaWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return the - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return BetaWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> BetaWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return BetaWithStreamingResponse(self) - - -class AsyncBeta(AsyncAPIResource): - @cached_property - def messages(self) -> AsyncMessages: - return AsyncMessages(self._client) - - @cached_property - def with_raw_response(self) -> AsyncBetaWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return the - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncBetaWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncBetaWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncBetaWithStreamingResponse(self) - - -class BetaWithRawResponse: - def __init__(self, beta: Beta) -> None: - self._beta = beta - - @cached_property - def messages(self) -> MessagesWithRawResponse: - return MessagesWithRawResponse(self._beta.messages) - - -class AsyncBetaWithRawResponse: - def __init__(self, beta: AsyncBeta) -> None: - self._beta = beta - - @cached_property - def messages(self) -> AsyncMessagesWithRawResponse: - return AsyncMessagesWithRawResponse(self._beta.messages) - - -class BetaWithStreamingResponse: - def __init__(self, beta: Beta) -> None: - self._beta = beta - - @cached_property - def messages(self) -> MessagesWithStreamingResponse: - return MessagesWithStreamingResponse(self._beta.messages) - - -class AsyncBetaWithStreamingResponse: - def __init__(self, beta: AsyncBeta) -> None: - self._beta = beta - - @cached_property - def messages(self) -> AsyncMessagesWithStreamingResponse: - return AsyncMessagesWithStreamingResponse(self._beta.messages) diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_beta_messages.py b/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_beta_messages.py deleted file mode 100644 index 332f6fba..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_beta_messages.py +++ /dev/null @@ -1,93 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from ... import _legacy_response -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...resources.beta import Messages as FirstPartyMessagesAPI, AsyncMessages as FirstPartyAsyncMessagesAPI - -__all__ = ["Messages", "AsyncMessages"] - - -class Messages(SyncAPIResource): - create = FirstPartyMessagesAPI.create - - @cached_property - def with_raw_response(self) -> MessagesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return the - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return MessagesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MessagesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return MessagesWithStreamingResponse(self) - - -class AsyncMessages(AsyncAPIResource): - create = FirstPartyAsyncMessagesAPI.create - - @cached_property - def with_raw_response(self) -> AsyncMessagesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return the - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncMessagesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncMessagesWithStreamingResponse(self) - - -class MessagesWithRawResponse: - def __init__(self, messages: Messages) -> None: - self._messages = messages - - self.create = _legacy_response.to_raw_response_wrapper( - messages.create, - ) - - -class AsyncMessagesWithRawResponse: - def __init__(self, messages: AsyncMessages) -> None: - self._messages = messages - - self.create = _legacy_response.async_to_raw_response_wrapper( - messages.create, - ) - - -class MessagesWithStreamingResponse: - def __init__(self, messages: Messages) -> None: - self._messages = messages - - self.create = to_streamed_response_wrapper( - messages.create, - ) - - -class AsyncMessagesWithStreamingResponse: - def __init__(self, messages: AsyncMessages) -> None: - self._messages = messages - - self.create = async_to_streamed_response_wrapper( - messages.create, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_client.py b/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_client.py deleted file mode 100644 index cda0690d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_client.py +++ /dev/null @@ -1,458 +0,0 @@ -from __future__ import annotations - -import os -import logging -import urllib.parse -from typing import Any, Union, Mapping, TypeVar -from typing_extensions import Self, override - -import httpx - -from ... import _exceptions -from ._beta import Beta, AsyncBeta -from ..._types import NOT_GIVEN, Timeout, NotGiven -from ..._utils import is_dict, is_given -from ..._compat import model_copy -from ..._version import __version__ -from ..._streaming import Stream, AsyncStream -from ..._exceptions import AnthropicError, APIStatusError -from ..._base_client import ( - DEFAULT_MAX_RETRIES, - BaseClient, - SyncAPIClient, - AsyncAPIClient, - FinalRequestOptions, -) -from ._stream_decoder import AWSEventStreamDecoder -from ...resources.messages import Messages, AsyncMessages -from ...resources.completions import Completions, AsyncCompletions - -log: logging.Logger = logging.getLogger(__name__) - -DEFAULT_VERSION = "bedrock-2023-05-31" - -_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) -_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) - - -def _prepare_options(input_options: FinalRequestOptions) -> FinalRequestOptions: - options = model_copy(input_options, deep=True) - - if is_dict(options.json_data): - options.json_data.setdefault("anthropic_version", DEFAULT_VERSION) - - if is_given(options.headers): - betas = options.headers.get("anthropic-beta") - if betas: - options.json_data.setdefault("anthropic_beta", betas.split(",")) - - if options.url in {"/v1/complete", "/v1/messages", "/v1/messages?beta=true"} and options.method == "post": - if not is_dict(options.json_data): - raise RuntimeError("Expected dictionary json_data for post /completions endpoint") - - model = options.json_data.pop("model", None) - model = urllib.parse.quote(str(model), safe=":") - stream = options.json_data.pop("stream", False) - if stream: - options.url = f"/model/{model}/invoke-with-response-stream" - else: - options.url = f"/model/{model}/invoke" - - if options.url.startswith("/v1/messages/batches"): - raise AnthropicError("The Batch API is not supported in Bedrock yet") - - if options.url == "/v1/messages/count_tokens": - raise AnthropicError("Token counting is not supported in Bedrock yet") - - return options - - -def _infer_region() -> str: - """ - Infer the AWS region from the environment variables or - from the boto3 session if available. - """ - aws_region = os.environ.get("AWS_REGION") - if aws_region is None: - try: - import boto3 - - session = boto3.Session() - if session.region_name: - aws_region = session.region_name - except ImportError: - pass - - if aws_region is None: - log.warning("No AWS region specified, defaulting to us-east-1") - aws_region = "us-east-1" # fall back to legacy behavior - - return aws_region - - -class BaseBedrockClient(BaseClient[_HttpxClientT, _DefaultStreamT]): - @override - def _make_status_error( - self, - err_msg: str, - *, - body: object, - response: httpx.Response, - ) -> APIStatusError: - if response.status_code == 400: - return _exceptions.BadRequestError(err_msg, response=response, body=body) - - if response.status_code == 401: - return _exceptions.AuthenticationError(err_msg, response=response, body=body) - - if response.status_code == 403: - return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) - - if response.status_code == 404: - return _exceptions.NotFoundError(err_msg, response=response, body=body) - - if response.status_code == 409: - return _exceptions.ConflictError(err_msg, response=response, body=body) - - if response.status_code == 422: - return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) - - if response.status_code == 429: - return _exceptions.RateLimitError(err_msg, response=response, body=body) - - if response.status_code == 503: - return _exceptions.ServiceUnavailableError(err_msg, response=response, body=body) - - if response.status_code >= 500: - return _exceptions.InternalServerError(err_msg, response=response, body=body) - return APIStatusError(err_msg, response=response, body=body) - - -class AnthropicBedrock(BaseBedrockClient[httpx.Client, Stream[Any]], SyncAPIClient): - messages: Messages - completions: Completions - beta: Beta - - def __init__( - self, - aws_secret_key: str | None = None, - aws_access_key: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - aws_session_token: str | None = None, - api_key: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. - http_client: httpx.Client | None = None, - # Enable or disable schema validation for data returned by the API. - # When enabled an error APIResponseValidationError is raised - # if the API responds with invalid data for the expected schema. - # - # This parameter may be removed or changed in the future. - # If you rely on this feature, please open a GitHub issue - # outlining your use-case to help us decide if it should be - # part of our public interface in the future. - _strict_response_validation: bool = False, - ) -> None: - if api_key is None: - api_key = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") - - has_aws_credentials = ( - aws_access_key is not None - or aws_secret_key is not None - or aws_session_token is not None - or aws_profile is not None - ) - if api_key is not None and has_aws_credentials: - raise ValueError( - "Cannot specify both `api_key` and AWS credentials (`aws_access_key`, `aws_secret_key`, `aws_session_token`, `aws_profile`)" - ) - - self.api_key: str | None = api_key - - self.aws_secret_key = aws_secret_key - - self.aws_access_key = aws_access_key - - self.aws_region = _infer_region() if aws_region is None else aws_region - self.aws_profile = aws_profile - - self.aws_session_token = aws_session_token - - if base_url is None: - base_url = os.environ.get("ANTHROPIC_BEDROCK_BASE_URL") - if base_url is None: - base_url = f"https://bedrock-runtime.{self.aws_region}.amazonaws.com" - - super().__init__( - version=__version__, - base_url=base_url, - timeout=timeout, - max_retries=max_retries, - custom_headers=default_headers, - custom_query=default_query, - http_client=http_client, - _strict_response_validation=_strict_response_validation, - ) - - self.beta = Beta(self) - self.messages = Messages(self) - self.completions = Completions(self) - - @override - def _make_sse_decoder(self) -> AWSEventStreamDecoder: - return AWSEventStreamDecoder() - - @override - def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: - return _prepare_options(options) - - @override - def _prepare_request(self, request: httpx.Request) -> None: - if self.api_key is not None: - request.headers["Authorization"] = f"Bearer {self.api_key}" - return - - from ._auth import get_auth_headers - - data = request.read().decode() - - headers = get_auth_headers( - method=request.method, - url=str(request.url), - headers=request.headers, - aws_access_key=self.aws_access_key, - aws_secret_key=self.aws_secret_key, - aws_session_token=self.aws_session_token, - region=self.aws_region or "us-east-1", - profile=self.aws_profile, - data=data, - ) - request.headers.update(headers) - - def copy( - self, - *, - aws_secret_key: str | None = None, - aws_access_key: str | None = None, - aws_region: str | None = None, - aws_session_token: str | None = None, - api_key: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - """ - Create a new client instance re-using the same options given to the current client with optional overriding. - """ - if default_headers is not None and set_default_headers is not None: - raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") - - if default_query is not None and set_default_query is not None: - raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - - headers = self._custom_headers - if default_headers is not None: - headers = {**headers, **default_headers} - elif set_default_headers is not None: - headers = set_default_headers - - params = self._custom_query - if default_query is not None: - params = {**params, **default_query} - elif set_default_query is not None: - params = set_default_query - - return self.__class__( - aws_secret_key=aws_secret_key or self.aws_secret_key, - aws_access_key=aws_access_key or self.aws_access_key, - aws_region=aws_region or self.aws_region, - aws_session_token=aws_session_token or self.aws_session_token, - api_key=api_key or self.api_key, - base_url=base_url or self.base_url, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - **_extra_kwargs, - ) - - # Alias for `copy` for nicer inline usage, e.g. - # client.with_options(timeout=10).foo.create(...) - with_options = copy - - -class AsyncAnthropicBedrock(BaseBedrockClient[httpx.AsyncClient, AsyncStream[Any]], AsyncAPIClient): - messages: AsyncMessages - completions: AsyncCompletions - beta: AsyncBeta - - def __init__( - self, - aws_secret_key: str | None = None, - aws_access_key: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - aws_session_token: str | None = None, - api_key: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. - http_client: httpx.AsyncClient | None = None, - # Enable or disable schema validation for data returned by the API. - # When enabled an error APIResponseValidationError is raised - # if the API responds with invalid data for the expected schema. - # - # This parameter may be removed or changed in the future. - # If you rely on this feature, please open a GitHub issue - # outlining your use-case to help us decide if it should be - # part of our public interface in the future. - _strict_response_validation: bool = False, - ) -> None: - if api_key is None: - api_key = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") - - has_aws_credentials = ( - aws_access_key is not None - or aws_secret_key is not None - or aws_session_token is not None - or aws_profile is not None - ) - if api_key is not None and has_aws_credentials: - raise ValueError( - "Cannot specify both `api_key` and AWS credentials (`aws_access_key`, `aws_secret_key`, `aws_session_token`, `aws_profile`)" - ) - - self.api_key: str | None = api_key - - self.aws_secret_key = aws_secret_key - - self.aws_access_key = aws_access_key - - self.aws_region = _infer_region() if aws_region is None else aws_region - self.aws_profile = aws_profile - - self.aws_session_token = aws_session_token - - if base_url is None: - base_url = os.environ.get("ANTHROPIC_BEDROCK_BASE_URL") - if base_url is None: - base_url = f"https://bedrock-runtime.{self.aws_region}.amazonaws.com" - - super().__init__( - version=__version__, - base_url=base_url, - timeout=timeout, - max_retries=max_retries, - custom_headers=default_headers, - custom_query=default_query, - http_client=http_client, - _strict_response_validation=_strict_response_validation, - ) - - self.messages = AsyncMessages(self) - self.completions = AsyncCompletions(self) - self.beta = AsyncBeta(self) - - @override - def _make_sse_decoder(self) -> AWSEventStreamDecoder: - return AWSEventStreamDecoder() - - @override - async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: - return _prepare_options(options) - - @override - async def _prepare_request(self, request: httpx.Request) -> None: - if self.api_key is not None: - request.headers["Authorization"] = f"Bearer {self.api_key}" - return - - from ._auth import get_auth_headers - - data = request.read().decode() - - headers = get_auth_headers( - method=request.method, - url=str(request.url), - headers=request.headers, - aws_access_key=self.aws_access_key, - aws_secret_key=self.aws_secret_key, - aws_session_token=self.aws_session_token, - region=self.aws_region or "us-east-1", - profile=self.aws_profile, - data=data, - ) - request.headers.update(headers) - - def copy( - self, - *, - aws_secret_key: str | None = None, - aws_access_key: str | None = None, - aws_region: str | None = None, - aws_session_token: str | None = None, - api_key: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - """ - Create a new client instance re-using the same options given to the current client with optional overriding. - """ - if default_headers is not None and set_default_headers is not None: - raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") - - if default_query is not None and set_default_query is not None: - raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - - headers = self._custom_headers - if default_headers is not None: - headers = {**headers, **default_headers} - elif set_default_headers is not None: - headers = set_default_headers - - params = self._custom_query - if default_query is not None: - params = {**params, **default_query} - elif set_default_query is not None: - params = set_default_query - - return self.__class__( - aws_secret_key=aws_secret_key or self.aws_secret_key, - aws_access_key=aws_access_key or self.aws_access_key, - aws_region=aws_region or self.aws_region, - aws_session_token=aws_session_token or self.aws_session_token, - api_key=api_key or self.api_key, - base_url=base_url or self.base_url, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - **_extra_kwargs, - ) - - # Alias for `copy` for nicer inline usage, e.g. - # client.with_options(timeout=10).foo.create(...) - with_options = copy diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_mantle.py b/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_mantle.py deleted file mode 100644 index 5507462a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_mantle.py +++ /dev/null @@ -1,512 +0,0 @@ -from __future__ import annotations - -import os -from typing import Any, Union, Mapping, TypeVar -from typing_extensions import Self, override - -import httpx - -from ... import _exceptions -from ..._qs import Querystring -from ..._types import NOT_GIVEN, Omit, Timeout, NotGiven -from ..._utils import is_given -from ..._compat import cached_property -from ..._version import __version__ -from ..aws._auth import get_auth_headers -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._streaming import Stream, AsyncStream -from ..._exceptions import AnthropicError, APIStatusError -from ..._base_client import ( - DEFAULT_MAX_RETRIES, - BaseClient, - SyncAPIClient, - AsyncAPIClient, -) -from ..aws._credentials import ( - resolve_region, - resolve_api_key, - resolve_auth_mode, - validate_credentials, -) -from ...resources.messages import Messages, AsyncMessages -from ...resources.beta.messages import Messages as BetaMessages, AsyncMessages as AsyncBetaMessages - -DEFAULT_SERVICE_NAME = "bedrock-mantle" - -_MANTLE_API_KEY_ENV_VARS = ("AWS_BEARER_TOKEN_BEDROCK", "ANTHROPIC_AWS_API_KEY") - -_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) -_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) - - -# --- Beta resources (messages-only) --- - - -class MantleBeta(SyncAPIResource): - @cached_property - def messages(self) -> BetaMessages: - return BetaMessages(self._client) - - -class AsyncMantleBeta(AsyncAPIResource): - @cached_property - def messages(self) -> AsyncBetaMessages: - return AsyncBetaMessages(self._client) - - -# --- Base --- - - -class BaseMantleClient(BaseClient[_HttpxClientT, _DefaultStreamT]): - @override - def _make_status_error( - self, - err_msg: str, - *, - body: object, - response: httpx.Response, - ) -> APIStatusError: - if response.status_code == 400: - return _exceptions.BadRequestError(err_msg, response=response, body=body) - - if response.status_code == 401: - return _exceptions.AuthenticationError(err_msg, response=response, body=body) - - if response.status_code == 403: - return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) - - if response.status_code == 404: - return _exceptions.NotFoundError(err_msg, response=response, body=body) - - if response.status_code == 409: - return _exceptions.ConflictError(err_msg, response=response, body=body) - - if response.status_code == 413: - return _exceptions.RequestTooLargeError(err_msg, response=response, body=body) - - if response.status_code == 422: - return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) - - if response.status_code == 429: - return _exceptions.RateLimitError(err_msg, response=response, body=body) - - if response.status_code == 529: - return _exceptions.OverloadedError(err_msg, response=response, body=body) - - if response.status_code >= 500: - return _exceptions.InternalServerError(err_msg, response=response, body=body) - return APIStatusError(err_msg, response=response, body=body) - - -# --- Shared init logic --- - - -def _resolve_mantle_config( - *, - api_key: str | None, - aws_access_key: str | None, - aws_secret_key: str | None, - aws_region: str | None, - aws_profile: str | None, - skip_auth: bool, - base_url: str | httpx.URL | None, - default_headers: Mapping[str, str] | None, -) -> tuple[str | None, str | httpx.URL, bool, dict[str, str]]: - """Resolve and validate all Mantle client configuration. - - Returns (resolved_api_key, resolved_base_url, use_sigv4, merged_headers). - """ - if skip_auth: - use_sigv4 = False - resolved_api_key = None - else: - validate_credentials(aws_access_key=aws_access_key, aws_secret_key=aws_secret_key) - - use_sigv4 = resolve_auth_mode( - api_key=api_key, - aws_access_key=aws_access_key, - aws_secret_key=aws_secret_key, - aws_profile=aws_profile, - api_key_env_vars=_MANTLE_API_KEY_ENV_VARS, - ) - - resolved_api_key = resolve_api_key( - api_key=api_key, - use_sigv4=use_sigv4, - api_key_env_vars=_MANTLE_API_KEY_ENV_VARS, - ) - - resolved_region = resolve_region(aws_region) - - if base_url is None: - base_url = os.environ.get("ANTHROPIC_BEDROCK_MANTLE_BASE_URL") - if base_url is None: - if resolved_region is None: - raise AnthropicError( - "No AWS region or base URL found. Set `aws_region` in the constructor, " - "the `AWS_REGION` / `AWS_DEFAULT_REGION` environment variable, or provide " - "a `base_url` / `ANTHROPIC_BEDROCK_MANTLE_BASE_URL` environment variable." - ) - base_url = f"https://bedrock-mantle.{resolved_region}.api.aws/anthropic" - - merged_headers: dict[str, str] = {} - if default_headers: - merged_headers.update(default_headers) - - return resolved_api_key, base_url, use_sigv4, merged_headers - - -# --- Sync client --- - - -class AnthropicBedrockMantle(BaseMantleClient[httpx.Client, Stream[Any]], SyncAPIClient): - messages: Messages - beta: MantleBeta - - aws_region: str | None - aws_access_key: str | None - aws_secret_key: str | None - aws_session_token: str | None - aws_profile: str | None - skip_auth: bool - - _use_sigv4: bool - - def __init__( - self, - *, - aws_access_key: str | None = None, - aws_secret_key: str | None = None, - aws_session_token: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - api_key: str | None = None, - skip_auth: bool = False, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.Client | None = None, - _strict_response_validation: bool = False, - ) -> None: - resolved_api_key, resolved_base_url, use_sigv4, merged_headers = _resolve_mantle_config( - api_key=api_key, - aws_access_key=aws_access_key, - aws_secret_key=aws_secret_key, - aws_region=aws_region, - aws_profile=aws_profile, - skip_auth=skip_auth, - base_url=base_url, - default_headers=default_headers, - ) - - resolved_region = resolve_region(aws_region) - - super().__init__( - version=__version__, - base_url=resolved_base_url, - timeout=timeout, - max_retries=max_retries, - custom_headers=merged_headers, - custom_query=default_query, - http_client=http_client, - _strict_response_validation=_strict_response_validation, - ) - - self.api_key = resolved_api_key - self.aws_region = resolved_region - self.aws_access_key = aws_access_key - self.aws_secret_key = aws_secret_key - self.aws_session_token = aws_session_token - self.aws_profile = aws_profile - self.skip_auth = skip_auth - self._use_sigv4 = use_sigv4 - - self.messages = Messages(self) - self.beta = MantleBeta(self) - - @property - @override - def qs(self) -> Querystring: - return Querystring(array_format="comma") - - @property - @override - def auth_headers(self) -> dict[str, str]: - if self.skip_auth or self._use_sigv4: - return {} - api_key = self.api_key - if api_key is None: - return {} - return {"Authorization": f"Bearer {api_key}"} - - @property - @override - def default_headers(self) -> dict[str, str | Omit]: - return { - **super().default_headers, - "X-Stainless-Async": "false", - "anthropic-version": "2023-06-01", - **self._custom_headers, - } - - @override - def _validate_headers(self, headers: Any, custom_headers: Any) -> None: - pass - - @override - def _prepare_request(self, request: httpx.Request) -> None: - if self.skip_auth or not self._use_sigv4: - return - - data = request.read().decode() - - headers = get_auth_headers( - method=request.method, - url=str(request.url), - headers=request.headers, - aws_access_key=self.aws_access_key, - aws_secret_key=self.aws_secret_key, - aws_session_token=self.aws_session_token, - region=self.aws_region, - profile=self.aws_profile, - data=data, - service_name=DEFAULT_SERVICE_NAME, - ) - request.headers.update(headers) - - def copy( - self, - *, - api_key: str | None = None, - aws_access_key: str | None = None, - aws_secret_key: str | None = None, - aws_session_token: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - skip_auth: bool | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - if default_headers is not None and set_default_headers is not None: - raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") - - if default_query is not None and set_default_query is not None: - raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - - headers = self._custom_headers - if default_headers is not None: - headers = {**headers, **default_headers} - elif set_default_headers is not None: - headers = set_default_headers - - params = self._custom_query - if default_query is not None: - params = {**params, **default_query} - elif set_default_query is not None: - params = set_default_query - - return self.__class__( - api_key=api_key or self.api_key, - aws_access_key=aws_access_key or self.aws_access_key, - aws_secret_key=aws_secret_key or self.aws_secret_key, - aws_session_token=aws_session_token or self.aws_session_token, - aws_region=aws_region or self.aws_region, - aws_profile=aws_profile or self.aws_profile, - skip_auth=skip_auth if skip_auth is not None else self.skip_auth, - base_url=base_url or self.base_url, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - **_extra_kwargs, - ) - - with_options = copy - - -# --- Async client --- - - -class AsyncAnthropicBedrockMantle(BaseMantleClient[httpx.AsyncClient, AsyncStream[Any]], AsyncAPIClient): - messages: AsyncMessages - beta: AsyncMantleBeta - - aws_region: str | None - aws_access_key: str | None - aws_secret_key: str | None - aws_session_token: str | None - aws_profile: str | None - skip_auth: bool - - _use_sigv4: bool - - def __init__( - self, - *, - aws_access_key: str | None = None, - aws_secret_key: str | None = None, - aws_session_token: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - api_key: str | None = None, - skip_auth: bool = False, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.AsyncClient | None = None, - _strict_response_validation: bool = False, - ) -> None: - resolved_api_key, resolved_base_url, use_sigv4, merged_headers = _resolve_mantle_config( - api_key=api_key, - aws_access_key=aws_access_key, - aws_secret_key=aws_secret_key, - aws_region=aws_region, - aws_profile=aws_profile, - skip_auth=skip_auth, - base_url=base_url, - default_headers=default_headers, - ) - - resolved_region = resolve_region(aws_region) - - super().__init__( - version=__version__, - base_url=resolved_base_url, - timeout=timeout, - max_retries=max_retries, - custom_headers=merged_headers, - custom_query=default_query, - http_client=http_client, - _strict_response_validation=_strict_response_validation, - ) - - self.api_key = resolved_api_key - self.aws_region = resolved_region - self.aws_access_key = aws_access_key - self.aws_secret_key = aws_secret_key - self.aws_session_token = aws_session_token - self.aws_profile = aws_profile - self.skip_auth = skip_auth - self._use_sigv4 = use_sigv4 - - self.messages = AsyncMessages(self) - self.beta = AsyncMantleBeta(self) - - @property - @override - def qs(self) -> Querystring: - return Querystring(array_format="comma") - - @property - @override - def auth_headers(self) -> dict[str, str]: - if self.skip_auth or self._use_sigv4: - return {} - api_key = self.api_key - if api_key is None: - return {} - return {"Authorization": f"Bearer {api_key}"} - - @property - @override - def default_headers(self) -> dict[str, str | Omit]: - return { - **super().default_headers, - "X-Stainless-Async": "async:asyncio", - "anthropic-version": "2023-06-01", - **self._custom_headers, - } - - @override - def _validate_headers(self, headers: Any, custom_headers: Any) -> None: - pass - - @override - async def _prepare_request(self, request: httpx.Request) -> None: - if self.skip_auth or not self._use_sigv4: - return - - data = request.read().decode() - - headers = get_auth_headers( - method=request.method, - url=str(request.url), - headers=request.headers, - aws_access_key=self.aws_access_key, - aws_secret_key=self.aws_secret_key, - aws_session_token=self.aws_session_token, - region=self.aws_region, - profile=self.aws_profile, - data=data, - service_name=DEFAULT_SERVICE_NAME, - ) - request.headers.update(headers) - - def copy( - self, - *, - api_key: str | None = None, - aws_access_key: str | None = None, - aws_secret_key: str | None = None, - aws_session_token: str | None = None, - aws_region: str | None = None, - aws_profile: str | None = None, - skip_auth: bool | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - if default_headers is not None and set_default_headers is not None: - raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") - - if default_query is not None and set_default_query is not None: - raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - - headers = self._custom_headers - if default_headers is not None: - headers = {**headers, **default_headers} - elif set_default_headers is not None: - headers = set_default_headers - - params = self._custom_query - if default_query is not None: - params = {**params, **default_query} - elif set_default_query is not None: - params = set_default_query - - return self.__class__( - api_key=api_key or self.api_key, - aws_access_key=aws_access_key or self.aws_access_key, - aws_secret_key=aws_secret_key or self.aws_secret_key, - aws_session_token=aws_session_token or self.aws_session_token, - aws_region=aws_region or self.aws_region, - aws_profile=aws_profile or self.aws_profile, - skip_auth=skip_auth if skip_auth is not None else self.skip_auth, - base_url=base_url or self.base_url, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - **_extra_kwargs, - ) - - with_options = copy diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_stream.py b/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_stream.py deleted file mode 100644 index 6512c468..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_stream.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import annotations - -from typing import TypeVar - -import httpx - -from ..._client import Anthropic, AsyncAnthropic -from ..._streaming import Stream, AsyncStream -from ._stream_decoder import AWSEventStreamDecoder - -_T = TypeVar("_T") - - -class BedrockStream(Stream[_T]): - def __init__( - self, - *, - cast_to: type[_T], - response: httpx.Response, - client: Anthropic, - ) -> None: - super().__init__(cast_to=cast_to, response=response, client=client) - - self._decoder = AWSEventStreamDecoder() - - -class AsyncBedrockStream(AsyncStream[_T]): - def __init__( - self, - *, - cast_to: type[_T], - response: httpx.Response, - client: AsyncAnthropic, - ) -> None: - super().__init__(cast_to=cast_to, response=response, client=client) - - self._decoder = AWSEventStreamDecoder() diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_stream_decoder.py b/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_stream_decoder.py deleted file mode 100644 index 02e81a3c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/bedrock/_stream_decoder.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Iterator, AsyncIterator - -from ..._utils import lru_cache -from ..._streaming import ServerSentEvent - -if TYPE_CHECKING: - from botocore.model import Shape - from botocore.eventstream import EventStreamMessage - - -@lru_cache(maxsize=None) -def get_response_stream_shape() -> Shape: - from botocore.model import ServiceModel - from botocore.loaders import Loader - - loader = Loader() - bedrock_service_dict = loader.load_service_model("bedrock-runtime", "service-2") - bedrock_service_model = ServiceModel(bedrock_service_dict) - return bedrock_service_model.shape_for("ResponseStream") - - -class AWSEventStreamDecoder: - def __init__(self) -> None: - from botocore.parsers import EventStreamJSONParser - - self.parser = EventStreamJSONParser() - - def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: - """Given an iterator that yields lines, iterate over it & yield every event encountered""" - from botocore.eventstream import EventStreamBuffer - - event_stream_buffer = EventStreamBuffer() - for chunk in iterator: - event_stream_buffer.add_data(chunk) - for event in event_stream_buffer: - message = self._parse_message_from_event(event) - if message: - yield ServerSentEvent(data=message, event="completion") - - async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: - """Given an async iterator that yields lines, iterate over it & yield every event encountered""" - from botocore.eventstream import EventStreamBuffer - - event_stream_buffer = EventStreamBuffer() - async for chunk in iterator: - event_stream_buffer.add_data(chunk) - for event in event_stream_buffer: - message = self._parse_message_from_event(event) - if message: - yield ServerSentEvent(data=message, event="completion") - - def _parse_message_from_event(self, event: EventStreamMessage) -> str | None: - response_dict = event.to_response_dict() - parsed_response = self.parser.parse(response_dict, get_response_stream_shape()) - if response_dict["status_code"] != 200: - raise ValueError(f"Bad response code, expected 200: {response_dict}") - - chunk = parsed_response.get("chunk") - if not chunk: - return None - - return chunk.get("bytes").decode() # type: ignore[no-any-return] diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/foundry.md b/.venv/lib/python3.12/site-packages/anthropic/lib/foundry.md deleted file mode 100644 index dd0a3475..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/foundry.md +++ /dev/null @@ -1,127 +0,0 @@ -# Anthropic Foundry - -To use this library with Foundry, use the `AnthropicFoundry` class instead of the `Anthropic` class. - - -## Installation - -```bash -pip install anthropic -``` - -## Usage - -### Basic Usage with API Key - -```python -from anthropic import AnthropicFoundry - -client = AnthropicFoundry( - api_key="...", # defaults to ANTHROPIC_FOUNDRY_API_KEY environment variable - resource="my-resource", # your Foundry resource -) - -message = client.messages.create( - model="claude-3-5-sonnet-20241022", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello!"}], -) - -print(message.content[0].text) -``` - -### Using Azure AD Token Provider - -For enhanced security, you can use Azure AD (Microsoft Entra) authentication instead of an API key: - -```python -from anthropic import AnthropicFoundry -from azure.identity import DefaultAzureCredential -from azure.identity import get_bearer_token_provider - -credential = DefaultAzureCredential() -token_provider = get_bearer_token_provider( - credential, - "https://ai.azure.com/.default" -) - -client = AnthropicFoundry( - azure_ad_token_provider=token_provider, - resource="my-resource", -) - -message = client.messages.create( - model="claude-3-5-sonnet-20241022", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello!"}], -) - -print(message.content[0].text) -``` - -## Examples - -### Streaming Messages - -```python -from anthropic import AnthropicFoundry - -client = AnthropicFoundry( - api_key="...", - resource="my-resource", -) - -with client.messages.stream( - model="claude-3-5-sonnet-20241022", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a haiku about programming"}], -) as stream: - for text in stream.text_stream: - print(text, end="", flush=True) -``` - -### Async Usage - -```python -from anthropic import AsyncAnthropicFoundry - -async def main(): - client = AsyncAnthropicFoundry( - api_key="...", - resource="my-resource", - ) - - message = await client.messages.create( - model="claude-3-5-sonnet-20241022", - max_tokens=1024, - messages=[{"role": "user", "content": "Hello!"}], - ) - - print(message.content[0].text) - -import asyncio -asyncio.run(main()) -``` - -### Async Streaming - -```python -from anthropic import AsyncAnthropicFoundry - -async def main(): - client = AsyncAnthropicFoundry( - api_key="...", - resource="my-resource", - ) - - async with client.messages.stream( - model="claude-3-5-sonnet-20241022", - max_tokens=1024, - messages=[{"role": "user", "content": "Write a haiku about programming"}], - ) as stream: - async for text in stream.text_stream: - print(text, end="", flush=True) - -import asyncio -asyncio.run(main()) -``` \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/foundry.py b/.venv/lib/python3.12/site-packages/anthropic/lib/foundry.py deleted file mode 100644 index 98641046..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/foundry.py +++ /dev/null @@ -1,443 +0,0 @@ -from __future__ import annotations - -import os -import inspect -from typing import Any, Union, Mapping, TypeVar, Callable, Awaitable, cast, overload -from functools import cached_property -from typing_extensions import Self, override - -import httpx - -from .._types import NOT_GIVEN, Omit, Timeout, NotGiven -from .._utils import is_given -from .._client import Anthropic, AsyncAnthropic -from .._compat import model_copy -from .._models import FinalRequestOptions -from .._streaming import Stream, AsyncStream -from .._exceptions import AnthropicError -from .._base_client import DEFAULT_MAX_RETRIES, BaseClient -from ..resources.beta import Beta, AsyncBeta -from ..resources.messages import Messages, AsyncMessages -from ..resources.beta.messages import Messages as BetaMessages, AsyncMessages as AsyncBetaMessages - -AzureADTokenProvider = Callable[[], str] -AsyncAzureADTokenProvider = Callable[[], "str | Awaitable[str]"] -_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) -_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) - - -class MutuallyExclusiveAuthError(AnthropicError): - def __init__(self) -> None: - super().__init__( - "The `api_key` and `azure_ad_token_provider` arguments are mutually exclusive; Only one can be passed at a time" - ) - - -class BaseFoundryClient(BaseClient[_HttpxClientT, _DefaultStreamT]): ... - - -class MessagesFoundry(Messages): - @cached_property - @override - def batches(self) -> None: # type: ignore[override] - """Batches endpoint is not supported for Anthropic Foundry client.""" - return None - - -class BetaFoundryMessages(BetaMessages): - @cached_property - @override - def batches(self) -> None: # type: ignore[override] - """Batches endpoint is not supported for Anthropic Foundry client.""" - return None - - -class BetaFoundry(Beta): - @cached_property - @override - def messages(self) -> BetaMessages: # type: ignore[override] - """Return beta messages resource instance with excluded unsupported endpoints.""" - return BetaFoundryMessages(self._client) - - -class AsyncMessagesFoundry(AsyncMessages): - @cached_property - @override - def batches(self) -> None: # type: ignore[override] - """Batches endpoint is not supported for Anthropic Foundry client.""" - return None - - -class AsyncBetaFoundryMessages(AsyncBetaMessages): - @cached_property - @override - def batches(self) -> None: # type: ignore[override] - """Batches endpoint is not supported for Anthropic Foundry client.""" - return None - - -class AsyncBetaFoundry(AsyncBeta): - @cached_property - @override - def messages(self) -> AsyncBetaMessages: # type: ignore[override] - """Return beta messages resource instance with excluded unsupported endpoints.""" - return AsyncBetaFoundryMessages(self._client) - - -# ============================================================================== - - -class AnthropicFoundry(BaseFoundryClient[httpx.Client, Stream[Any]], Anthropic): - @overload - def __init__( - self, - *, - resource: str | None = None, - api_key: str | None = None, - azure_ad_token_provider: AzureADTokenProvider | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.Client | None = None, - _strict_response_validation: bool = False, - ) -> None: ... - - @overload - def __init__( - self, - *, - base_url: str, - api_key: str | None = None, - azure_ad_token_provider: AzureADTokenProvider | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.Client | None = None, - _strict_response_validation: bool = False, - ) -> None: ... - - def __init__( - self, - *, - resource: str | None = None, - api_key: str | None = None, - azure_ad_token_provider: AzureADTokenProvider | None = None, - base_url: str | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.Client | None = None, - _strict_response_validation: bool = False, - ) -> None: - """Construct a new synchronous Anthropic Foundry client instance. - - This automatically infers the following arguments from their corresponding environment variables if they are not provided: - - `api_key` from `ANTHROPIC_FOUNDRY_API_KEY` - - `resource` from `ANTHROPIC_FOUNDRY_RESOURCE` - - `base_url` from `ANTHROPIC_FOUNDRY_BASE_URL` - - Args: - resource: Your Foundry resource name, e.g. `example-resource` for `https://example-resource.services.ai.azure.com/anthropic/` - azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. - """ - api_key = api_key if api_key is not None else os.environ.get("ANTHROPIC_FOUNDRY_API_KEY") - resource = resource if resource is not None else os.environ.get("ANTHROPIC_FOUNDRY_RESOURCE") - base_url = base_url if base_url is not None else os.environ.get("ANTHROPIC_FOUNDRY_BASE_URL") - - if api_key is None and azure_ad_token_provider is None: - raise AnthropicError( - "Missing credentials. Please pass one of `api_key`, `azure_ad_token_provider`, or the `ANTHROPIC_FOUNDRY_API_KEY` environment variable." - ) - - if base_url is None: - if resource is None: - raise ValueError( - "Must provide one of the `base_url` or `resource` arguments, or the `ANTHROPIC_FOUNDRY_RESOURCE` environment variable" - ) - base_url = f"https://{resource}.services.ai.azure.com/anthropic/" - elif resource is not None: - raise ValueError("base_url and resource are mutually exclusive") - - super().__init__( - api_key=api_key, - base_url=base_url, - timeout=timeout, - max_retries=max_retries, - default_headers=default_headers, - default_query=default_query, - http_client=http_client, - _strict_response_validation=_strict_response_validation, - ) - self._azure_ad_token_provider = azure_ad_token_provider - - @cached_property - @override - def models(self) -> None: # type: ignore[override] - """Models endpoint is not supported for Anthropic Foundry client.""" - return None - - @cached_property - @override - def messages(self) -> MessagesFoundry: # type: ignore[override] - """Return messages resource instance with excluded unsupported endpoints.""" - return MessagesFoundry(client=self) - - @cached_property - @override - def beta(self) -> Beta: # type: ignore[override] - """Return beta resource instance with excluded unsupported endpoints.""" - return BetaFoundry(self) - - @override - def copy( - self, - *, - api_key: str | None = None, - azure_ad_token_provider: AzureADTokenProvider | None = None, - auth_token: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - """ - Create a new client instance re-using the same options given to the current client with optional overriding. - """ - return super().copy( - api_key=api_key, - auth_token=auth_token, - base_url=base_url, - timeout=timeout, - http_client=http_client, - max_retries=max_retries, - default_headers=default_headers, - set_default_headers=set_default_headers, - default_query=default_query, - set_default_query=set_default_query, - _extra_kwargs={ - "azure_ad_token_provider": azure_ad_token_provider or self._azure_ad_token_provider, - **_extra_kwargs, - }, - ) - - with_options = copy - - def _get_azure_ad_token(self) -> str | None: - provider = self._azure_ad_token_provider - if provider is not None: - token = provider() - if not token or not isinstance(token, str): # pyright: ignore[reportUnnecessaryIsInstance] - raise ValueError( - f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", - ) - return token - - return None - - @override - def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: - headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} - - options = model_copy(options) - options.headers = headers - - azure_ad_token = self._get_azure_ad_token() - if azure_ad_token is not None: - if headers.get("Authorization") is None: - headers["Authorization"] = f"Bearer {azure_ad_token}" - elif self.api_key is not None: - if headers.get("api-key") is None: - assert self.api_key is not None - headers["api-key"] = self.api_key - else: - # should never be hit - raise ValueError("Unable to handle auth") - - return options - - -class AsyncAnthropicFoundry(BaseFoundryClient[httpx.AsyncClient, AsyncStream[Any]], AsyncAnthropic): - @overload - def __init__( - self, - *, - resource: str | None = None, - api_key: str | None = None, - azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.AsyncClient | None = None, - _strict_response_validation: bool = False, - ) -> None: ... - - @overload - def __init__( - self, - *, - base_url: str, - api_key: str | None = None, - azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.AsyncClient | None = None, - _strict_response_validation: bool = False, - ) -> None: ... - - def __init__( - self, - *, - resource: str | None = None, - api_key: str | None = None, - azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, - base_url: str | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - http_client: httpx.AsyncClient | None = None, - _strict_response_validation: bool = False, - ) -> None: - """Construct a new asynchronous Anthropic Foundry client instance. - - This automatically infers the following arguments from their corresponding environment variables if they are not provided: - - `api_key` from `ANTHROPIC_FOUNDRY_API_KEY` - - `resource` from `ANTHROPIC_FOUNDRY_RESOURCE` - - `base_url` from `ANTHROPIC_FOUNDRY_BASE_URL` - - Args: - resource: Your Foundry resource name, e.g. `example-resource` for `https://example-resource.services.ai.azure.com/anthropic/` - azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. - """ - api_key = api_key if api_key is not None else os.environ.get("ANTHROPIC_FOUNDRY_API_KEY") - resource = resource if resource is not None else os.environ.get("ANTHROPIC_FOUNDRY_RESOURCE") - base_url = base_url if base_url is not None else os.environ.get("ANTHROPIC_FOUNDRY_BASE_URL") - - if api_key is None and azure_ad_token_provider is None: - raise AnthropicError( - "Missing credentials. Please pass one of `api_key`, `azure_ad_token_provider`, or the `ANTHROPIC_FOUNDRY_API_KEY` environment variable." - ) - - if base_url is None: - if resource is None: - raise ValueError( - "Must provide one of the `base_url` or `resource` arguments, or the `ANTHROPIC_FOUNDRY_RESOURCE` environment variable" - ) - base_url = f"https://{resource}.services.ai.azure.com/anthropic/" - elif resource is not None: - raise ValueError("base_url and resource are mutually exclusive") - - super().__init__( - api_key=api_key, - base_url=base_url, - timeout=timeout, - max_retries=max_retries, - default_headers=default_headers, - default_query=default_query, - http_client=http_client, - _strict_response_validation=_strict_response_validation, - ) - self._azure_ad_token_provider = azure_ad_token_provider - - @cached_property - @override - def models(self) -> None: # type: ignore[override] - """Models endpoint is not supported for Azure Anthropic client.""" - return None - - @cached_property - @override - def messages(self) -> AsyncMessagesFoundry: # type: ignore[override] - """Return messages resource instance with excluded unsupported endpoints.""" - return AsyncMessagesFoundry(client=self) - - @cached_property - @override - def beta(self) -> AsyncBetaFoundry: # type: ignore[override] - """Return beta resource instance with excluded unsupported endpoints.""" - return AsyncBetaFoundry(client=self) - - @override - def copy( - self, - *, - api_key: str | None = None, - azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, - auth_token: str | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - """ - Create a new client instance re-using the same options given to the current client with optional overriding. - """ - return super().copy( - api_key=api_key, - auth_token=auth_token, - base_url=base_url, - timeout=timeout, - http_client=http_client, - max_retries=max_retries, - default_headers=default_headers, - set_default_headers=set_default_headers, - default_query=default_query, - set_default_query=set_default_query, - _extra_kwargs={ - "azure_ad_token_provider": azure_ad_token_provider or self._azure_ad_token_provider, - **_extra_kwargs, - }, - ) - - with_options = copy - - async def _get_azure_ad_token(self) -> str | None: - provider = self._azure_ad_token_provider - if provider is not None: - token = provider() - if inspect.isawaitable(token): - token = await token - if not token or not isinstance(cast(Any, token), str): - raise ValueError( - f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", - ) - return str(token) - - return None - - @override - async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: - headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} - - options = model_copy(options) - options.headers = headers - - azure_ad_token = await self._get_azure_ad_token() - if azure_ad_token is not None: - if headers.get("Authorization") is None: - headers["Authorization"] = f"Bearer {azure_ad_token}" - elif self.api_key is not None: - assert self.api_key is not None - if headers.get("api-key") is None: - headers["api-key"] = self.api_key - else: - # should never be hit - raise ValueError("Unable to handle auth") - - return options diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/__init__.py deleted file mode 100644 index 2f717c00..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing_extensions import TypeAlias - -from ._types import ( - TextEvent as TextEvent, - InputJsonEvent as InputJsonEvent, - MessageStopEvent as MessageStopEvent, - MessageStreamEvent as MessageStreamEvent, - ContentBlockStopEvent as ContentBlockStopEvent, - ParsedMessageStopEvent as ParsedMessageStopEvent, - ParsedMessageStreamEvent as ParsedMessageStreamEvent, - ParsedContentBlockStopEvent as ParsedContentBlockStopEvent, -) -from ._messages import ( - MessageStream as MessageStream, - AsyncMessageStream as AsyncMessageStream, - MessageStreamManager as MessageStreamManager, - AsyncMessageStreamManager as AsyncMessageStreamManager, -) -from ._beta_types import ( - BetaInputJsonEvent as BetaInputJsonEvent, - ParsedBetaTextEvent as ParsedBetaTextEvent, - ParsedBetaMessageStopEvent as ParsedBetaMessageStopEvent, - ParsedBetaMessageStreamEvent as ParsedBetaMessageStreamEvent, - ParsedBetaContentBlockStopEvent as ParsedBetaContentBlockStopEvent, -) - -# For backwards compatibility -BetaTextEvent: TypeAlias = ParsedBetaTextEvent -BetaMessageStopEvent: TypeAlias = ParsedBetaMessageStopEvent[object] -BetaMessageStreamEvent: TypeAlias = ParsedBetaMessageStreamEvent -BetaContentBlockStopEvent: TypeAlias = ParsedBetaContentBlockStopEvent[object] - - -from ._beta_messages import ( - BetaMessageStream as BetaMessageStream, - BetaAsyncMessageStream as BetaAsyncMessageStream, - BetaMessageStreamManager as BetaMessageStreamManager, - BetaAsyncMessageStreamManager as BetaAsyncMessageStreamManager, -) diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_beta_messages.py b/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_beta_messages.py deleted file mode 100644 index c1447a8d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_beta_messages.py +++ /dev/null @@ -1,555 +0,0 @@ -from __future__ import annotations - -import builtins -from types import TracebackType -from typing import TYPE_CHECKING, Any, Type, Generic, Callable, cast -from typing_extensions import Self, Iterator, Awaitable, AsyncIterator, assert_never - -import httpx -from pydantic import BaseModel - -from anthropic.types.beta.beta_tool_use_block import BetaToolUseBlock -from anthropic.types.beta.beta_mcp_tool_use_block import BetaMCPToolUseBlock -from anthropic.types.beta.beta_server_tool_use_block import BetaServerToolUseBlock - -from ..._types import NOT_GIVEN, NotGiven -from ..._utils import consume_sync_iterator, consume_async_iterator -from ..._models import build, construct_type, construct_type_unchecked -from ._beta_types import ( - BetaCitationEvent, - BetaThinkingEvent, - BetaInputJsonEvent, - BetaSignatureEvent, - BetaCompactionEvent, - ParsedBetaTextEvent, - ParsedBetaMessageStopEvent, - ParsedBetaMessageStreamEvent, - ParsedBetaContentBlockStopEvent, -) -from ..._streaming import Stream, AsyncStream -from ...types.beta import BetaRawMessageStreamEvent -from ..._utils._utils import is_given -from .._parse._response import ResponseFormatT, parse_text -from ...types.beta.parsed_beta_message import ParsedBetaMessage, ParsedBetaContentBlock - - -class BetaMessageStream(Generic[ResponseFormatT]): - text_stream: Iterator[str] - """Iterator over just the text deltas in the stream. - - ```py - for text in stream.text_stream: - print(text, end="", flush=True) - print() - ``` - """ - - def __init__( - self, - raw_stream: Stream[BetaRawMessageStreamEvent], - output_format: ResponseFormatT | NotGiven, - ) -> None: - self._raw_stream = raw_stream - self.text_stream = self.__stream_text__() - self._iterator = self.__stream__() - self.__final_message_snapshot: ParsedBetaMessage[ResponseFormatT] | None = None - self.__output_format = output_format - - @property - def response(self) -> httpx.Response: - return self._raw_stream.response - - @property - def request_id(self) -> str | None: - return self.response.headers.get("request-id") # type: ignore[no-any-return] - - def __next__(self) -> ParsedBetaMessageStreamEvent[ResponseFormatT]: - return self._iterator.__next__() - - def __iter__(self) -> Iterator[ParsedBetaMessageStreamEvent[ResponseFormatT]]: - for item in self._iterator: - yield item - - def __enter__(self) -> Self: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.close() - - def close(self) -> None: - """ - Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - self._raw_stream.close() - - def get_final_message(self) -> ParsedBetaMessage[ResponseFormatT]: - """Waits until the stream has been read to completion and returns - the accumulated `Message` object. - """ - self.until_done() - assert self.__final_message_snapshot is not None - return self.__final_message_snapshot - - def get_final_text(self) -> str: - """Returns all `text` content blocks concatenated together. - - > [!NOTE] - > Currently the API will only respond with a single content block. - - Will raise an error if no `text` content blocks were returned. - """ - message = self.get_final_message() - text_blocks: list[str] = [] - for block in message.content: - if block.type == "text": - text_blocks.append(block.text) - - if not text_blocks: - raise RuntimeError( - f".get_final_text() can only be called when the API returns a `text` content block.\nThe API returned {','.join([b.type for b in message.content])} content block type(s) that you can access by calling get_final_message().content" - ) - - return "".join(text_blocks) - - def until_done(self) -> None: - """Blocks until the stream has been consumed""" - consume_sync_iterator(self) - - # properties - @property - def current_message_snapshot(self) -> ParsedBetaMessage[ResponseFormatT]: - assert self.__final_message_snapshot is not None - return self.__final_message_snapshot - - def __stream__(self) -> Iterator[ParsedBetaMessageStreamEvent[ResponseFormatT]]: - for sse_event in self._raw_stream: - self.__final_message_snapshot = accumulate_event( - event=sse_event, - current_snapshot=self.__final_message_snapshot, - request_headers=self.response.request.headers, - output_format=self.__output_format, - ) - - events_to_fire = build_events(event=sse_event, message_snapshot=self.current_message_snapshot) - for event in events_to_fire: - yield event - - def __stream_text__(self) -> Iterator[str]: - for chunk in self: - if chunk.type == "content_block_delta" and chunk.delta.type == "text_delta": - yield chunk.delta.text - - -class BetaMessageStreamManager(Generic[ResponseFormatT]): - """Wrapper over MessageStream that is returned by `.stream()`. - - ```py - with client.beta.messages.stream(...) as stream: - for chunk in stream: - ... - ``` - """ - - def __init__( - self, - api_request: Callable[[], Stream[BetaRawMessageStreamEvent]], - *, - output_format: ResponseFormatT | NotGiven, - ) -> None: - self.__stream: BetaMessageStream[ResponseFormatT] | None = None - self.__api_request = api_request - self.__output_format = output_format - - def __enter__(self) -> BetaMessageStream[ResponseFormatT]: - raw_stream = self.__api_request() - self.__stream = BetaMessageStream(raw_stream, output_format=self.__output_format) - return self.__stream - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - if self.__stream is not None: - self.__stream.close() - - -class BetaAsyncMessageStream(Generic[ResponseFormatT]): - text_stream: AsyncIterator[str] - """Async iterator over just the text deltas in the stream. - - ```py - async for text in stream.text_stream: - print(text, end="", flush=True) - print() - ``` - """ - - def __init__( - self, - raw_stream: AsyncStream[BetaRawMessageStreamEvent], - output_format: ResponseFormatT | NotGiven, - ) -> None: - self._raw_stream = raw_stream - self.text_stream = self.__stream_text__() - self._iterator = self.__stream__() - self.__final_message_snapshot: ParsedBetaMessage[ResponseFormatT] | None = None - self.__output_format = output_format - - @property - def response(self) -> httpx.Response: - return self._raw_stream.response - - @property - def request_id(self) -> str | None: - return self.response.headers.get("request-id") # type: ignore[no-any-return] - - async def __anext__(self) -> ParsedBetaMessageStreamEvent[ResponseFormatT]: - return await self._iterator.__anext__() - - async def __aiter__(self) -> AsyncIterator[ParsedBetaMessageStreamEvent[ResponseFormatT]]: - async for item in self._iterator: - yield item - - async def __aenter__(self) -> Self: - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - await self.close() - - async def close(self) -> None: - """ - Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - await self._raw_stream.close() - - async def get_final_message(self) -> ParsedBetaMessage[ResponseFormatT]: - """Waits until the stream has been read to completion and returns - the accumulated `Message` object. - """ - await self.until_done() - assert self.__final_message_snapshot is not None - return self.__final_message_snapshot - - async def get_final_text(self) -> str: - """Returns all `text` content blocks concatenated together. - - > [!NOTE] - > Currently the API will only respond with a single content block. - - Will raise an error if no `text` content blocks were returned. - """ - message = await self.get_final_message() - text_blocks: list[str] = [] - for block in message.content: - if block.type == "text": - text_blocks.append(block.text) - - if not text_blocks: - raise RuntimeError( - f".get_final_text() can only be called when the API returns a `text` content block.\nThe API returned {','.join([b.type for b in message.content])} content block type(s) that you can access by calling get_final_message().content" - ) - - return "".join(text_blocks) - - async def until_done(self) -> None: - """Waits until the stream has been consumed""" - await consume_async_iterator(self) - - # properties - @property - def current_message_snapshot(self) -> ParsedBetaMessage[ResponseFormatT]: - assert self.__final_message_snapshot is not None - return self.__final_message_snapshot - - async def __stream__(self) -> AsyncIterator[ParsedBetaMessageStreamEvent[ResponseFormatT]]: - async for sse_event in self._raw_stream: - self.__final_message_snapshot = accumulate_event( - event=sse_event, - current_snapshot=self.__final_message_snapshot, - request_headers=self.response.request.headers, - output_format=self.__output_format, - ) - - events_to_fire = build_events(event=sse_event, message_snapshot=self.current_message_snapshot) - for event in events_to_fire: - yield event - - async def __stream_text__(self) -> AsyncIterator[str]: - async for chunk in self: - if chunk.type == "content_block_delta" and chunk.delta.type == "text_delta": - yield chunk.delta.text - - -class BetaAsyncMessageStreamManager(Generic[ResponseFormatT]): - """Wrapper over BetaAsyncMessageStream that is returned by `.stream()` - so that an async context manager can be used without `await`ing the - original client call. - - ```py - async with client.beta.messages.stream(...) as stream: - async for chunk in stream: - ... - ``` - """ - - def __init__( - self, - api_request: Awaitable[AsyncStream[BetaRawMessageStreamEvent]], - *, - output_format: ResponseFormatT | NotGiven = NOT_GIVEN, - ) -> None: - self.__stream: BetaAsyncMessageStream[ResponseFormatT] | None = None - self.__api_request = api_request - self.__output_format = output_format - - async def __aenter__(self) -> BetaAsyncMessageStream[ResponseFormatT]: - raw_stream = await self.__api_request - self.__stream = BetaAsyncMessageStream(raw_stream, output_format=self.__output_format) - return self.__stream - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - if self.__stream is not None: - await self.__stream.close() - - -def build_events( - *, - event: BetaRawMessageStreamEvent, - message_snapshot: ParsedBetaMessage[ResponseFormatT], -) -> list[ParsedBetaMessageStreamEvent[ResponseFormatT]]: - events_to_fire: list[ParsedBetaMessageStreamEvent[ResponseFormatT]] = [] - - if event.type == "message_start": - events_to_fire.append(event) - elif event.type == "message_delta": - events_to_fire.append(event) - elif event.type == "message_stop": - events_to_fire.append( - build(ParsedBetaMessageStopEvent[ResponseFormatT], type="message_stop", message=message_snapshot) - ) - elif event.type == "content_block_start": - events_to_fire.append(event) - elif event.type == "content_block_delta": - events_to_fire.append(event) - - content_block = message_snapshot.content[event.index] - if event.delta.type == "text_delta": - if content_block.type == "text": - events_to_fire.append( - build( - ParsedBetaTextEvent, - type="text", - text=event.delta.text, - snapshot=content_block.text, - ) - ) - elif event.delta.type == "input_json_delta": - if content_block.type == "tool_use" or content_block.type == "mcp_tool_use": - events_to_fire.append( - build( - BetaInputJsonEvent, - type="input_json", - partial_json=event.delta.partial_json, - snapshot=content_block.input, - ) - ) - elif event.delta.type == "citations_delta": - if content_block.type == "text": - events_to_fire.append( - build( - BetaCitationEvent, - type="citation", - citation=event.delta.citation, - snapshot=content_block.citations or [], - ) - ) - elif event.delta.type == "thinking_delta": - if content_block.type == "thinking": - events_to_fire.append( - build( - BetaThinkingEvent, - type="thinking", - thinking=event.delta.thinking, - snapshot=content_block.thinking, - ) - ) - elif event.delta.type == "signature_delta": - if content_block.type == "thinking": - events_to_fire.append( - build( - BetaSignatureEvent, - type="signature", - signature=content_block.signature, - ) - ) - pass - elif event.delta.type == "compaction_delta": - if content_block.type == "compaction": - events_to_fire.append( - build( - BetaCompactionEvent, - type="compaction", - content=content_block.content, - ) - ) - else: - # we only want exhaustive checking for linters, not at runtime - if TYPE_CHECKING: # type: ignore[unreachable] - assert_never(event.delta) - elif event.type == "content_block_stop": - content_block = message_snapshot.content[event.index] - - event_to_fire = build( - ParsedBetaContentBlockStopEvent, - type="content_block_stop", - index=event.index, - content_block=content_block, - ) - - events_to_fire.append(event_to_fire) - else: - # we only want exhaustive checking for linters, not at runtime - if TYPE_CHECKING: # type: ignore[unreachable] - assert_never(event) - - return events_to_fire - - -JSON_BUF_PROPERTY = "__json_buf" - -TRACKS_TOOL_INPUT = ( - BetaToolUseBlock, - BetaServerToolUseBlock, - BetaMCPToolUseBlock, -) - - -def accumulate_event( - *, - event: BetaRawMessageStreamEvent, - current_snapshot: ParsedBetaMessage[ResponseFormatT] | None, - request_headers: httpx.Headers, - output_format: ResponseFormatT | NotGiven = NOT_GIVEN, -) -> ParsedBetaMessage[ResponseFormatT]: - if not isinstance(cast(Any, event), BaseModel): - event = cast( # pyright: ignore[reportUnnecessaryCast] - BetaRawMessageStreamEvent, - construct_type_unchecked( - type_=cast(Type[BetaRawMessageStreamEvent], BetaRawMessageStreamEvent), - value=event, - ), - ) - if not isinstance(cast(Any, event), BaseModel): - raise TypeError( - f"Unexpected event runtime type, after deserialising twice - {event} - {builtins.type(event)}" - ) - - if current_snapshot is None: - if event.type == "message_start": - return cast( - ParsedBetaMessage[ResponseFormatT], ParsedBetaMessage.construct(**cast(Any, event.message.to_dict())) - ) - - raise RuntimeError(f'Unexpected event order, got {event.type} before "message_start"') - - if event.type == "content_block_start": - # TODO: check index - current_snapshot.content.append( - cast( - Any, # Pydantic does not support generic unions at runtime - construct_type(type_=ParsedBetaContentBlock, value=event.content_block.to_dict()), - ), - ) - elif event.type == "content_block_delta": - content = current_snapshot.content[event.index] - if event.delta.type == "text_delta": - if content.type == "text": - content.text += event.delta.text - elif event.delta.type == "input_json_delta": - if isinstance(content, TRACKS_TOOL_INPUT): - from jiter import from_json - - # we need to keep track of the raw JSON string as well so that we can - # re-parse it for each delta, for now we just store it as an untyped - # property on the snapshot - json_buf = cast(bytes, getattr(content, JSON_BUF_PROPERTY, b"")) - json_buf += bytes(event.delta.partial_json, "utf-8") - - if json_buf: - try: - anthropic_beta = request_headers.get("anthropic-beta", "") if request_headers else "" - - if "fine-grained-tool-streaming-2025-05-14" in anthropic_beta: - content.input = from_json(json_buf, partial_mode="trailing-strings") - else: - content.input = from_json(json_buf, partial_mode=True) - except ValueError as e: - raise ValueError( - f"Unable to parse tool parameter JSON from model. Please retry your request or adjust your prompt. Error: {e}. JSON: {json_buf.decode('utf-8')}" - ) from e - - setattr(content, JSON_BUF_PROPERTY, json_buf) - elif event.delta.type == "citations_delta": - if content.type == "text": - if not content.citations: - content.citations = [event.delta.citation] - else: - content.citations.append(event.delta.citation) - elif event.delta.type == "thinking_delta": - if content.type == "thinking": - content.thinking += event.delta.thinking - elif event.delta.type == "signature_delta": - if content.type == "thinking": - content.signature = event.delta.signature - elif event.delta.type == "compaction_delta": - if content.type == "compaction": - content.content = event.delta.content - else: - # we only want exhaustive checking for linters, not at runtime - if TYPE_CHECKING: # type: ignore[unreachable] - assert_never(event.delta) - elif event.type == "content_block_stop": - content_block = current_snapshot.content[event.index] - if content_block.type == "text" and is_given(output_format): - content_block.parsed_output = parse_text(content_block.text, output_format) - elif event.type == "message_delta": - current_snapshot.container = event.delta.container - current_snapshot.stop_reason = event.delta.stop_reason - current_snapshot.stop_sequence = event.delta.stop_sequence - current_snapshot.usage.output_tokens = event.usage.output_tokens - current_snapshot.context_management = event.context_management - - # Update other usage fields if they exist in the event - if event.usage.input_tokens is not None: - current_snapshot.usage.input_tokens = event.usage.input_tokens - if event.usage.cache_creation_input_tokens is not None: - current_snapshot.usage.cache_creation_input_tokens = event.usage.cache_creation_input_tokens - if event.usage.cache_read_input_tokens is not None: - current_snapshot.usage.cache_read_input_tokens = event.usage.cache_read_input_tokens - if event.usage.server_tool_use is not None: - current_snapshot.usage.server_tool_use = event.usage.server_tool_use - if event.usage.iterations is not None: - current_snapshot.usage.iterations = event.usage.iterations - - return current_snapshot diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_beta_types.py b/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_beta_types.py deleted file mode 100644 index 3dcf3535..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_beta_types.py +++ /dev/null @@ -1,116 +0,0 @@ -from typing import TYPE_CHECKING, Any, Dict, Union, Generic, cast -from typing_extensions import List, Literal, Annotated - -import jiter - -from ..._models import BaseModel, GenericModel -from ...types.beta import ( - BetaRawMessageStopEvent, - BetaRawMessageDeltaEvent, - BetaRawMessageStartEvent, - BetaRawContentBlockStopEvent, - BetaRawContentBlockDeltaEvent, - BetaRawContentBlockStartEvent, -) -from .._parse._response import ResponseFormatT -from ..._utils._transform import PropertyInfo -from ...types.beta.parsed_beta_message import ParsedBetaMessage, ParsedBetaContentBlock -from ...types.beta.beta_citations_delta import Citation - - -class ParsedBetaTextEvent(BaseModel): - type: Literal["text"] - - text: str - """The text delta""" - - snapshot: str - """The entire accumulated text""" - - def parsed_snapshot(self) -> Dict[str, Any]: - return cast(Dict[str, Any], jiter.from_json(self.snapshot.encode("utf-8"), partial_mode="trailing-strings")) - - -class BetaCitationEvent(BaseModel): - type: Literal["citation"] - - citation: Citation - """The new citation""" - - snapshot: List[Citation] - """All of the accumulated citations""" - - -class BetaThinkingEvent(BaseModel): - type: Literal["thinking"] - - thinking: str - """The thinking delta""" - - snapshot: str - """The accumulated thinking so far""" - - -class BetaSignatureEvent(BaseModel): - type: Literal["signature"] - - signature: str - """The signature of the thinking block""" - - -class BetaInputJsonEvent(BaseModel): - type: Literal["input_json"] - - partial_json: str - """A partial JSON string delta - - e.g. `'"San Francisco,'` - """ - - snapshot: object - """The currently accumulated parsed object. - - - e.g. `{'location': 'San Francisco, CA'}` - """ - - -class BetaCompactionEvent(BaseModel): - type: Literal["compaction"] - - content: Union[str, None] - """The compaction content""" - - -class ParsedBetaMessageStopEvent(BetaRawMessageStopEvent, GenericModel, Generic[ResponseFormatT]): - type: Literal["message_stop"] - - message: ParsedBetaMessage[ResponseFormatT] - - -class ParsedBetaContentBlockStopEvent(BetaRawContentBlockStopEvent, GenericModel, Generic[ResponseFormatT]): - type: Literal["content_block_stop"] - - if TYPE_CHECKING: - content_block: ParsedBetaContentBlock[ResponseFormatT] - else: - content_block: ParsedBetaContentBlock - - -ParsedBetaMessageStreamEvent = Annotated[ - Union[ - ParsedBetaTextEvent, - BetaCitationEvent, - BetaThinkingEvent, - BetaSignatureEvent, - BetaInputJsonEvent, - BetaCompactionEvent, - BetaRawMessageStartEvent, - BetaRawMessageDeltaEvent, - ParsedBetaMessageStopEvent[ResponseFormatT], - BetaRawContentBlockStartEvent, - BetaRawContentBlockDeltaEvent, - ParsedBetaContentBlockStopEvent[ResponseFormatT], - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_messages.py b/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_messages.py deleted file mode 100644 index b6b5f538..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_messages.py +++ /dev/null @@ -1,518 +0,0 @@ -from __future__ import annotations - -from types import TracebackType -from typing import TYPE_CHECKING, Any, Type, Generic, Callable, cast -from typing_extensions import Self, Iterator, Awaitable, AsyncIterator, assert_never - -import httpx -from pydantic import BaseModel - -from anthropic.types.tool_use_block import ToolUseBlock -from anthropic.types.server_tool_use_block import ServerToolUseBlock - -from ._types import ( - TextEvent, - CitationEvent, - ThinkingEvent, - InputJsonEvent, - SignatureEvent, - ParsedMessageStopEvent, - ParsedMessageStreamEvent, - ParsedContentBlockStopEvent, -) -from ...types import RawMessageStreamEvent -from ..._types import NOT_GIVEN, NotGiven -from ..._utils import consume_sync_iterator, consume_async_iterator -from ..._models import build, construct_type, construct_type_unchecked -from ..._streaming import Stream, AsyncStream -from ..._utils._utils import is_given -from .._parse._response import ResponseFormatT, parse_text -from ...types.parsed_message import ParsedMessage, ParsedContentBlock - - -class MessageStream(Generic[ResponseFormatT]): - text_stream: Iterator[str] - """Iterator over just the text deltas in the stream. - - ```py - for text in stream.text_stream: - print(text, end="", flush=True) - print() - ``` - """ - - def __init__( - self, - raw_stream: Stream[RawMessageStreamEvent], - output_format: ResponseFormatT | NotGiven, - ) -> None: - self._raw_stream = raw_stream - self.text_stream = self.__stream_text__() - self._iterator = self.__stream__() - self.__final_message_snapshot: ParsedMessage[ResponseFormatT] | None = None - self.__output_format = output_format - - @property - def response(self) -> httpx.Response: - return self._raw_stream.response - - @property - def request_id(self) -> str | None: - return self.response.headers.get("request-id") # type: ignore[no-any-return] - - def __next__(self) -> ParsedMessageStreamEvent[ResponseFormatT]: - return self._iterator.__next__() - - def __iter__(self) -> Iterator[ParsedMessageStreamEvent[ResponseFormatT]]: - for item in self._iterator: - yield item - - def __enter__(self) -> Self: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.close() - - def close(self) -> None: - """ - Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - self._raw_stream.close() - - def get_final_message(self) -> ParsedMessage[ResponseFormatT]: - """Waits until the stream has been read to completion and returns - the accumulated `Message` object. - """ - self.until_done() - assert self.__final_message_snapshot is not None - return self.__final_message_snapshot - - def get_final_text(self) -> str: - """Returns all `text` content blocks concatenated together. - - > [!NOTE] - > Currently the API will only respond with a single content block. - - Will raise an error if no `text` content blocks were returned. - """ - message = self.get_final_message() - text_blocks: list[str] = [] - for block in message.content: - if block.type == "text": - text_blocks.append(block.text) - - if not text_blocks: - raise RuntimeError( - f".get_final_text() can only be called when the API returns a `text` content block.\nThe API returned {','.join([b.type for b in message.content])} content block type(s) that you can access by calling get_final_message().content" - ) - - return "".join(text_blocks) - - def until_done(self) -> None: - """Blocks until the stream has been consumed""" - consume_sync_iterator(self) - - # properties - @property - def current_message_snapshot(self) -> ParsedMessage[ResponseFormatT]: - assert self.__final_message_snapshot is not None - return self.__final_message_snapshot - - def __stream__(self) -> Iterator[ParsedMessageStreamEvent[ResponseFormatT]]: - for sse_event in self._raw_stream: - self.__final_message_snapshot = accumulate_event( - event=sse_event, - current_snapshot=self.__final_message_snapshot, - output_format=self.__output_format, - ) - - events_to_fire = build_events(event=sse_event, message_snapshot=self.current_message_snapshot) - for event in events_to_fire: - yield event - - def __stream_text__(self) -> Iterator[str]: - for chunk in self: - if chunk.type == "content_block_delta" and chunk.delta.type == "text_delta": - yield chunk.delta.text - - -class MessageStreamManager(Generic[ResponseFormatT]): - """Wrapper over MessageStream that is returned by `.stream()`. - - ```py - with client.messages.stream(...) as stream: - for chunk in stream: - ... - ``` - """ - - def __init__( - self, - api_request: Callable[[], Stream[RawMessageStreamEvent]], - *, - output_format: ResponseFormatT | NotGiven, - ) -> None: - self.__stream: MessageStream[ResponseFormatT] | None = None - self.__api_request = api_request - self.__output_format = output_format - - def __enter__(self) -> MessageStream[ResponseFormatT]: - raw_stream = self.__api_request() - self.__stream = MessageStream(raw_stream, output_format=self.__output_format) - return self.__stream - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - if self.__stream is not None: - self.__stream.close() - - -class AsyncMessageStream(Generic[ResponseFormatT]): - text_stream: AsyncIterator[str] - """Async iterator over just the text deltas in the stream. - - ```py - async for text in stream.text_stream: - print(text, end="", flush=True) - print() - ``` - """ - - def __init__( - self, - raw_stream: AsyncStream[RawMessageStreamEvent], - output_format: ResponseFormatT | NotGiven, - ) -> None: - self._raw_stream = raw_stream - self.text_stream = self.__stream_text__() - self._iterator = self.__stream__() - self.__final_message_snapshot: ParsedMessage[ResponseFormatT] | None = None - self.__output_format = output_format - - @property - def response(self) -> httpx.Response: - return self._raw_stream.response - - @property - def request_id(self) -> str | None: - return self.response.headers.get("request-id") # type: ignore[no-any-return] - - async def __anext__(self) -> ParsedMessageStreamEvent[ResponseFormatT]: - return await self._iterator.__anext__() - - async def __aiter__(self) -> AsyncIterator[ParsedMessageStreamEvent[ResponseFormatT]]: - async for item in self._iterator: - yield item - - async def __aenter__(self) -> Self: - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - await self.close() - - async def close(self) -> None: - """ - Close the response and release the connection. - - Automatically called if the response body is read to completion. - """ - await self._raw_stream.close() - - async def get_final_message(self) -> ParsedMessage[ResponseFormatT]: - """Waits until the stream has been read to completion and returns - the accumulated `Message` object. - """ - await self.until_done() - assert self.__final_message_snapshot is not None - return self.__final_message_snapshot - - async def get_final_text(self) -> str: - """Returns all `text` content blocks concatenated together. - - > [!NOTE] - > Currently the API will only respond with a single content block. - - Will raise an error if no `text` content blocks were returned. - """ - message = await self.get_final_message() - text_blocks: list[str] = [] - for block in message.content: - if block.type == "text": - text_blocks.append(block.text) - - if not text_blocks: - raise RuntimeError( - f".get_final_text() can only be called when the API returns a `text` content block.\nThe API returned {','.join([b.type for b in message.content])} content block type(s) that you can access by calling get_final_message().content" - ) - - return "".join(text_blocks) - - async def until_done(self) -> None: - """Waits until the stream has been consumed""" - await consume_async_iterator(self) - - # properties - @property - def current_message_snapshot(self) -> ParsedMessage[ResponseFormatT]: - assert self.__final_message_snapshot is not None - return self.__final_message_snapshot - - async def __stream__(self) -> AsyncIterator[ParsedMessageStreamEvent[ResponseFormatT]]: - async for sse_event in self._raw_stream: - self.__final_message_snapshot = accumulate_event( - event=sse_event, - current_snapshot=self.__final_message_snapshot, - output_format=self.__output_format, - ) - - events_to_fire = build_events(event=sse_event, message_snapshot=self.current_message_snapshot) - for event in events_to_fire: - yield event - - async def __stream_text__(self) -> AsyncIterator[str]: - async for chunk in self: - if chunk.type == "content_block_delta" and chunk.delta.type == "text_delta": - yield chunk.delta.text - - -class AsyncMessageStreamManager(Generic[ResponseFormatT]): - """Wrapper over AsyncMessageStream that is returned by `.stream()` - so that an async context manager can be used without `await`ing the - original client call. - - ```py - async with client.messages.stream(...) as stream: - async for chunk in stream: - ... - ``` - """ - - def __init__( - self, - api_request: Awaitable[AsyncStream[RawMessageStreamEvent]], - *, - output_format: ResponseFormatT | NotGiven = NOT_GIVEN, - ) -> None: - self.__stream: AsyncMessageStream[ResponseFormatT] | None = None - self.__api_request = api_request - self.__output_format = output_format - - async def __aenter__(self) -> AsyncMessageStream[ResponseFormatT]: - raw_stream = await self.__api_request - self.__stream = AsyncMessageStream(raw_stream, output_format=self.__output_format) - return self.__stream - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - if self.__stream is not None: - await self.__stream.close() - - -def build_events( - *, - event: RawMessageStreamEvent, - message_snapshot: ParsedMessage[ResponseFormatT], -) -> list[ParsedMessageStreamEvent[ResponseFormatT]]: - events_to_fire: list[ParsedMessageStreamEvent[ResponseFormatT]] = [] - - if event.type == "message_start": - events_to_fire.append(event) - elif event.type == "message_delta": - events_to_fire.append(event) - elif event.type == "message_stop": - events_to_fire.append( - build(ParsedMessageStopEvent[ResponseFormatT], type="message_stop", message=message_snapshot) - ) - elif event.type == "content_block_start": - events_to_fire.append(event) - elif event.type == "content_block_delta": - events_to_fire.append(event) - - content_block = message_snapshot.content[event.index] - if event.delta.type == "text_delta": - if content_block.type == "text": - events_to_fire.append( - build( - TextEvent, - type="text", - text=event.delta.text, - snapshot=content_block.text, - ) - ) - elif event.delta.type == "input_json_delta": - if content_block.type == "tool_use": - events_to_fire.append( - build( - InputJsonEvent, - type="input_json", - partial_json=event.delta.partial_json, - snapshot=content_block.input, - ) - ) - elif event.delta.type == "citations_delta": - if content_block.type == "text": - events_to_fire.append( - build( - CitationEvent, - type="citation", - citation=event.delta.citation, - snapshot=content_block.citations or [], - ) - ) - elif event.delta.type == "thinking_delta": - if content_block.type == "thinking": - events_to_fire.append( - build( - ThinkingEvent, - type="thinking", - thinking=event.delta.thinking, - snapshot=content_block.thinking, - ) - ) - elif event.delta.type == "signature_delta": - if content_block.type == "thinking": - events_to_fire.append( - build( - SignatureEvent, - type="signature", - signature=content_block.signature, - ) - ) - pass - else: - # we only want exhaustive checking for linters, not at runtime - if TYPE_CHECKING: # type: ignore[unreachable] - assert_never(event.delta) - elif event.type == "content_block_stop": - content_block = message_snapshot.content[event.index] - - event_to_fire = build( - ParsedContentBlockStopEvent, - type="content_block_stop", - index=event.index, - content_block=content_block, - ) - - events_to_fire.append(event_to_fire) - else: - # we only want exhaustive checking for linters, not at runtime - if TYPE_CHECKING: # type: ignore[unreachable] - assert_never(event) - - return events_to_fire - - -JSON_BUF_PROPERTY = "__json_buf" - -TRACKS_TOOL_INPUT = ( - ToolUseBlock, - ServerToolUseBlock, -) - - -def accumulate_event( - *, - event: RawMessageStreamEvent, - current_snapshot: ParsedMessage[ResponseFormatT] | None, - output_format: ResponseFormatT | NotGiven = NOT_GIVEN, -) -> ParsedMessage[ResponseFormatT]: - if not isinstance(cast(Any, event), BaseModel): - event = cast( # pyright: ignore[reportUnnecessaryCast] - RawMessageStreamEvent, - construct_type_unchecked( - type_=cast(Type[RawMessageStreamEvent], RawMessageStreamEvent), - value=event, - ), - ) - if not isinstance(cast(Any, event), BaseModel): - raise TypeError(f"Unexpected event runtime type, after deserialising twice - {event} - {type(event)}") - - if current_snapshot is None: - if event.type == "message_start": - return cast(ParsedMessage[ResponseFormatT], ParsedMessage.construct(**cast(Any, event.message.to_dict()))) - - raise RuntimeError(f'Unexpected event order, got {event.type} before "message_start"') - - if event.type == "content_block_start": - # TODO: check index - current_snapshot.content.append( - cast( - Any, # Pydantic does not support generic unions at runtime - construct_type(type_=ParsedContentBlock, value=event.content_block.model_dump()), - ), - ) - elif event.type == "content_block_delta": - content = current_snapshot.content[event.index] - if event.delta.type == "text_delta": - if content.type == "text": - content.text += event.delta.text - elif event.delta.type == "input_json_delta": - if isinstance(content, TRACKS_TOOL_INPUT): - from jiter import from_json - - # we need to keep track of the raw JSON string as well so that we can - # re-parse it for each delta, for now we just store it as an untyped - # property on the snapshot - json_buf = cast(bytes, getattr(content, JSON_BUF_PROPERTY, b"")) - json_buf += bytes(event.delta.partial_json, "utf-8") - - if json_buf: - content.input = from_json(json_buf, partial_mode=True) - - setattr(content, JSON_BUF_PROPERTY, json_buf) - elif event.delta.type == "citations_delta": - if content.type == "text": - if not content.citations: - content.citations = [event.delta.citation] - else: - content.citations.append(event.delta.citation) - elif event.delta.type == "thinking_delta": - if content.type == "thinking": - content.thinking += event.delta.thinking - elif event.delta.type == "signature_delta": - if content.type == "thinking": - content.signature = event.delta.signature - else: - # we only want exhaustive checking for linters, not at runtime - if TYPE_CHECKING: # type: ignore[unreachable] - assert_never(event.delta) - elif event.type == "content_block_stop": - content_block = current_snapshot.content[event.index] - if content_block.type == "text" and is_given(output_format): - content_block.parsed_output = parse_text(content_block.text, output_format) - elif event.type == "message_delta": - current_snapshot.stop_reason = event.delta.stop_reason - current_snapshot.stop_sequence = event.delta.stop_sequence - current_snapshot.usage.output_tokens = event.usage.output_tokens - - # Update other usage fields if they exist in the event - if event.usage.input_tokens is not None: - current_snapshot.usage.input_tokens = event.usage.input_tokens - if event.usage.cache_creation_input_tokens is not None: - current_snapshot.usage.cache_creation_input_tokens = event.usage.cache_creation_input_tokens - if event.usage.cache_read_input_tokens is not None: - current_snapshot.usage.cache_read_input_tokens = event.usage.cache_read_input_tokens - if event.usage.server_tool_use is not None: - current_snapshot.usage.server_tool_use = event.usage.server_tool_use - - return current_snapshot diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_types.py b/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_types.py deleted file mode 100644 index 7399e76f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/streaming/_types.py +++ /dev/null @@ -1,140 +0,0 @@ -from typing import TYPE_CHECKING, Any, Dict, Union, Generic, cast -from typing_extensions import List, Literal, Annotated - -import jiter - -from ...types import ( - Message, - ContentBlock, - MessageDeltaEvent as RawMessageDeltaEvent, - MessageStartEvent as RawMessageStartEvent, - RawMessageStopEvent, - ContentBlockDeltaEvent as RawContentBlockDeltaEvent, - ContentBlockStartEvent as RawContentBlockStartEvent, - RawContentBlockStopEvent, -) -from ..._models import BaseModel, GenericModel -from .._parse._response import ResponseFormatT -from ..._utils._transform import PropertyInfo -from ...types.parsed_message import ParsedMessage, ParsedContentBlock -from ...types.citations_delta import Citation - - -class TextEvent(BaseModel): - type: Literal["text"] - - text: str - """The text delta""" - - snapshot: str - """The entire accumulated text""" - - def parsed_snapshot(self) -> Dict[str, Any]: - return cast(Dict[str, Any], jiter.from_json(self.snapshot.encode("utf-8"), partial_mode="trailing-strings")) - - -class CitationEvent(BaseModel): - type: Literal["citation"] - - citation: Citation - """The new citation""" - - snapshot: List[Citation] - """All of the accumulated citations""" - - -class ThinkingEvent(BaseModel): - type: Literal["thinking"] - - thinking: str - """The thinking delta""" - - snapshot: str - """The accumulated thinking so far""" - - -class SignatureEvent(BaseModel): - type: Literal["signature"] - - signature: str - """The signature of the thinking block""" - - -class InputJsonEvent(BaseModel): - type: Literal["input_json"] - - partial_json: str - """A partial JSON string delta - - e.g. `'"San Francisco,'` - """ - - snapshot: object - """The currently accumulated parsed object. - - - e.g. `{'location': 'San Francisco, CA'}` - """ - - -class MessageStopEvent(RawMessageStopEvent): - type: Literal["message_stop"] - - message: Message - - -class ContentBlockStopEvent(RawContentBlockStopEvent): - type: Literal["content_block_stop"] - - content_block: ContentBlock - - -MessageStreamEvent = Annotated[ - Union[ - TextEvent, - CitationEvent, - ThinkingEvent, - SignatureEvent, - InputJsonEvent, - RawMessageStartEvent, - RawMessageDeltaEvent, - MessageStopEvent, - RawContentBlockStartEvent, - RawContentBlockDeltaEvent, - ContentBlockStopEvent, - ], - PropertyInfo(discriminator="type"), -] - - -class ParsedMessageStopEvent(RawMessageStopEvent, GenericModel, Generic[ResponseFormatT]): - type: Literal["message_stop"] - - message: ParsedMessage[ResponseFormatT] - - -class ParsedContentBlockStopEvent(RawContentBlockStopEvent, GenericModel, Generic[ResponseFormatT]): - type: Literal["content_block_stop"] - - if TYPE_CHECKING: - content_block: ParsedContentBlock[ResponseFormatT] - else: - content_block: ParsedContentBlock - - -ParsedMessageStreamEvent = Annotated[ - Union[ - TextEvent, - CitationEvent, - ThinkingEvent, - SignatureEvent, - InputJsonEvent, - RawMessageStartEvent, - RawMessageDeltaEvent, - ParsedMessageStopEvent[ResponseFormatT], - RawContentBlockStartEvent, - RawContentBlockDeltaEvent, - ParsedContentBlockStopEvent[ResponseFormatT], - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/lib/tools/__init__.py deleted file mode 100644 index b8593e11..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -from ._beta_runner import BetaToolRunner, BetaAsyncToolRunner, BetaStreamingToolRunner, BetaAsyncStreamingToolRunner -from ._beta_functions import ( - ToolError, - BetaFunctionTool, - BetaAsyncFunctionTool, - BetaBuiltinFunctionTool, - BetaFunctionToolResultType, - BetaAsyncBuiltinFunctionTool, - beta_tool, - beta_async_tool, -) -from ._beta_builtin_memory_tool import BetaAbstractMemoryTool, BetaAsyncAbstractMemoryTool - -__all__ = [ - "beta_tool", - "beta_async_tool", - "BetaFunctionTool", - "BetaAsyncFunctionTool", - "BetaBuiltinFunctionTool", - "BetaAsyncBuiltinFunctionTool", - "BetaToolRunner", - "BetaAsyncStreamingToolRunner", - "BetaStreamingToolRunner", - "BetaAsyncToolRunner", - "BetaFunctionToolResultType", - "BetaAbstractMemoryTool", - "BetaAsyncAbstractMemoryTool", - "ToolError", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_builtin_memory_tool.py b/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_builtin_memory_tool.py deleted file mode 100644 index edb948a2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_builtin_memory_tool.py +++ /dev/null @@ -1,875 +0,0 @@ -from __future__ import annotations - -import os -import uuid -import shutil -from abc import abstractmethod -from typing import TYPE_CHECKING, Any, List, cast -from pathlib import Path -from typing_extensions import override, assert_never - -from anyio import Path as AsyncPath -from anyio.to_thread import run_sync - -from anthropic.types.beta import ( - BetaMemoryTool20250818ViewCommand, - BetaMemoryTool20250818CreateCommand, - BetaMemoryTool20250818DeleteCommand, - BetaMemoryTool20250818InsertCommand, - BetaMemoryTool20250818RenameCommand, - BetaMemoryTool20250818StrReplaceCommand, -) - -from ..._models import construct_type_unchecked -from ...types.beta import ( - BetaMemoryTool20250818Param, - BetaMemoryTool20250818Command, - BetaCacheControlEphemeralParam, - BetaMemoryTool20250818ViewCommand, - BetaMemoryTool20250818CreateCommand, - BetaMemoryTool20250818DeleteCommand, - BetaMemoryTool20250818InsertCommand, - BetaMemoryTool20250818RenameCommand, - BetaMemoryTool20250818StrReplaceCommand, -) -from ._beta_functions import ( - ToolError, - BetaBuiltinFunctionTool, - BetaFunctionToolResultType, - BetaAsyncBuiltinFunctionTool, -) - -MAX_LINES = 999999 -LINE_NUMBER_WIDTH = len(str(MAX_LINES)) - -# Owner read/write only. Avoids 0o666 which, in environments with a permissive -# umask (e.g. Docker where umask is often 0o000), would make memory files -# world-readable or even world-writable. -_FILE_CREATE_MODE = 0o600 -# The default mkdir mode is 0o777, but we want to be more restrictive for memory -# directories to avoid them being world-accessible in environments with permissive umasks -# (eg Docker) -_DIR_CREATE_MODE = 0o700 - - -class BetaAbstractMemoryTool(BetaBuiltinFunctionTool): - """Abstract base class for memory tool implementations. - - This class provides the interface for implementing a custom memory backend for Claude. - - Subclass this to create your own memory storage solution (e.g., database, cloud storage, encrypted files, etc.). - - Example usage: - - ```py - class MyMemoryTool(BetaAbstractMemoryTool): - def view(self, command: BetaMemoryTool20250818ViewCommand) -> BetaFunctionToolResultType: - ... - return "view result" - - def create(self, command: BetaMemoryTool20250818CreateCommand) -> BetaFunctionToolResultType: - ... - return "created successfully" - - # ... implement other abstract methods - - - client = Anthropic() - memory_tool = MyMemoryTool() - message = client.beta.messages.run_tools( - model="claude-sonnet-4-5", - messages=[{"role": "user", "content": "Remember that I like coffee"}], - tools=[memory_tool], - ).until_done() - ``` - """ - - def __init__(self, *, cache_control: BetaCacheControlEphemeralParam | None = None) -> None: - super().__init__() - self._cache_control = cache_control - - @override - def to_dict(self) -> BetaMemoryTool20250818Param: - param: BetaMemoryTool20250818Param = {"type": "memory_20250818", "name": "memory"} - - if self._cache_control is not None: - param["cache_control"] = self._cache_control - - return param - - @override - def call(self, input: object) -> BetaFunctionToolResultType: - command = cast( - BetaMemoryTool20250818Command, - construct_type_unchecked(value=input, type_=cast(Any, BetaMemoryTool20250818Command)), - ) - return self.execute(command) - - def execute(self, command: BetaMemoryTool20250818Command) -> BetaFunctionToolResultType: - """Execute a memory command and return the result. - - This method dispatches to the appropriate handler method based on the - command type (view, create, str_replace, insert, delete, rename). - - You typically don't need to override this method. - """ - if command.command == "view": - return self.view(command) - elif command.command == "create": - return self.create(command) - elif command.command == "str_replace": - return self.str_replace(command) - elif command.command == "insert": - return self.insert(command) - elif command.command == "delete": - return self.delete(command) - elif command.command == "rename": - return self.rename(command) - elif TYPE_CHECKING: # type: ignore[unreachable] - assert_never(command) - else: - raise NotImplementedError(f"Unknown command: {command.command}") - - @abstractmethod - def view(self, command: BetaMemoryTool20250818ViewCommand) -> BetaFunctionToolResultType: - """View the contents of a memory path.""" - pass - - @abstractmethod - def create(self, command: BetaMemoryTool20250818CreateCommand) -> BetaFunctionToolResultType: - """Create a new memory file with the specified content.""" - pass - - @abstractmethod - def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> BetaFunctionToolResultType: - """Replace text in a memory file.""" - pass - - @abstractmethod - def insert(self, command: BetaMemoryTool20250818InsertCommand) -> BetaFunctionToolResultType: - """Insert text at a specific line number in a memory file.""" - pass - - @abstractmethod - def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> BetaFunctionToolResultType: - """Delete a memory file or directory.""" - pass - - @abstractmethod - def rename(self, command: BetaMemoryTool20250818RenameCommand) -> BetaFunctionToolResultType: - """Rename or move a memory file or directory.""" - pass - - def clear_all_memory(self) -> BetaFunctionToolResultType: - """Clear all memory data.""" - raise NotImplementedError("clear_all_memory not implemented") - - -class BetaAsyncAbstractMemoryTool(BetaAsyncBuiltinFunctionTool): - """Abstract base class for memory tool implementations. - - This class provides the interface for implementing a custom memory backend for Claude. - - Subclass this to create your own memory storage solution (e.g., database, cloud storage, encrypted files, etc.). - - Example usage: - - ```py - class MyMemoryTool(BetaAbstractMemoryTool): - def view(self, command: BetaMemoryTool20250818ViewCommand) -> BetaFunctionToolResultType: - ... - return "view result" - - def create(self, command: BetaMemoryTool20250818CreateCommand) -> BetaFunctionToolResultType: - ... - return "created successfully" - - # ... implement other abstract methods - - - client = Anthropic() - memory_tool = MyMemoryTool() - message = client.beta.messages.run_tools( - model="claude-sonnet-4-5", - messages=[{"role": "user", "content": "Remember that I like coffee"}], - tools=[memory_tool], - ).until_done() - ``` - """ - - def __init__(self, *, cache_control: BetaCacheControlEphemeralParam | None = None) -> None: - super().__init__() - self._cache_control = cache_control - - @override - def to_dict(self) -> BetaMemoryTool20250818Param: - param: BetaMemoryTool20250818Param = {"type": "memory_20250818", "name": "memory"} - - if self._cache_control is not None: - param["cache_control"] = self._cache_control - - return param - - @override - async def call(self, input: object) -> BetaFunctionToolResultType: - command = cast( - BetaMemoryTool20250818Command, - construct_type_unchecked(value=input, type_=cast(Any, BetaMemoryTool20250818Command)), - ) - return await self.execute(command) - - async def execute(self, command: BetaMemoryTool20250818Command) -> BetaFunctionToolResultType: - """Execute a memory command and return the result. - - This method dispatches to the appropriate handler method based on the - command type (view, create, str_replace, insert, delete, rename). - - You typically don't need to override this method. - """ - if command.command == "view": - return await self.view(command) - elif command.command == "create": - return await self.create(command) - elif command.command == "str_replace": - return await self.str_replace(command) - elif command.command == "insert": - return await self.insert(command) - elif command.command == "delete": - return await self.delete(command) - elif command.command == "rename": - return await self.rename(command) - elif TYPE_CHECKING: # type: ignore[unreachable] - assert_never(command) - else: - raise NotImplementedError(f"Unknown command: {command.command}") - - @abstractmethod - async def view(self, command: BetaMemoryTool20250818ViewCommand) -> BetaFunctionToolResultType: - """View the contents of a memory path.""" - pass - - @abstractmethod - async def create(self, command: BetaMemoryTool20250818CreateCommand) -> BetaFunctionToolResultType: - """Create a new memory file with the specified content.""" - pass - - @abstractmethod - async def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> BetaFunctionToolResultType: - """Replace text in a memory file.""" - pass - - @abstractmethod - async def insert(self, command: BetaMemoryTool20250818InsertCommand) -> BetaFunctionToolResultType: - """Insert text at a specific line number in a memory file.""" - pass - - @abstractmethod - async def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> BetaFunctionToolResultType: - """Delete a memory file or directory.""" - pass - - @abstractmethod - async def rename(self, command: BetaMemoryTool20250818RenameCommand) -> BetaFunctionToolResultType: - """Rename or move a memory file or directory.""" - pass - - async def clear_all_memory(self) -> BetaFunctionToolResultType: - """Clear all memory data.""" - raise NotImplementedError("clear_all_memory not implemented") - - -def _atomic_write_file(target_path: Path, content: str) -> None: - dir_path = target_path.parent - temp_path = dir_path / f".tmp-{os.getpid()}-{uuid.uuid4()}" - data = content.encode("utf-8") - - try: - fd = os.open(temp_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, _FILE_CREATE_MODE) - try: - offset = 0 - while offset < len(data): - written = os.write(fd, data[offset:]) - if written == 0: - raise OSError("os.write returned 0") - offset += written - - os.fsync(fd) - finally: - os.close(fd) - - os.replace(temp_path, target_path) - except Exception: - temp_path.unlink(missing_ok=True) - raise - - -def _validate_no_symlink_escape(target_path: Path, memory_root: Path) -> None: - resolved_root = memory_root.resolve() - - current = target_path - while True: - try: - resolved = current.resolve() - if resolved != resolved_root and not str(resolved).startswith(str(resolved_root) + os.sep): - raise ToolError("Path would escape /memories directory via symlink") - return - except (FileNotFoundError, OSError): - parent = current.parent - if parent == current or current == memory_root: - return - current = parent - - -def _read_file_content(full_path: Path, memory_path: str) -> str: - try: - return full_path.read_text(encoding="utf-8") - except FileNotFoundError as err: - raise ToolError( - f"The file {memory_path} no longer exists (may have been deleted or renamed concurrently)." - ) from err - - -def _format_file_size(bytes_size: int) -> str: - if bytes_size == 0: - return "0B" - k = 1024 - sizes = ["B", "K", "M", "G"] - i = int(bytes_size.bit_length() - 1) // 10 - i = min(i, len(sizes) - 1) - size = bytes_size / (k**i) - - if size == int(size): - return f"{int(size)}{sizes[i]}" - else: - return f"{size:.1f}{sizes[i]}" - - -class BetaLocalFilesystemMemoryTool(BetaAbstractMemoryTool): - """File-based memory storage implementation for Claude conversations""" - - def __init__(self, base_path: str = "./memory"): - super().__init__() - self.base_path = Path(base_path) - self.memory_root = self.base_path / "memories" - self.memory_root.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) - - def _validate_path(self, path: str) -> Path: - """Validate and resolve memory paths""" - if not path.startswith("/memories"): - raise ToolError(f"Path must start with /memories, got: {path}") - - relative_path = path[len("/memories") :].lstrip("/") - full_path = self.memory_root / relative_path if relative_path else self.memory_root - - resolved_path = full_path.resolve() - resolved_root = self.memory_root.resolve() - if resolved_path != resolved_root and not str(resolved_path).startswith(str(resolved_root) + os.sep): - raise ToolError(f"Path {path} would escape /memories directory") - - _validate_no_symlink_escape(resolved_path, self.memory_root) - - return resolved_path - - @override - def view(self, command: BetaMemoryTool20250818ViewCommand) -> str: - full_path = self._validate_path(command.path) - - if not full_path.exists(): - raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") - - if full_path.is_dir(): - items: List[tuple[str, str]] = [] - - def collect_items(dir_path: Path, relative_path: str, depth: int) -> None: - if depth > 2: - return - - try: - dir_contents = sorted(dir_path.iterdir(), key=lambda x: x.name) - except Exception: - return - - for item in dir_contents: - if item.name.startswith("."): - continue - item_relative_path = f"{relative_path}/{item.name}" if relative_path else item.name - try: - stat = item.stat() - except Exception: - continue - - if item.is_dir(): - items.append((_format_file_size(stat.st_size), f"{item_relative_path}/")) - if depth < 2: - collect_items(item, item_relative_path, depth + 1) - elif item.is_file(): - items.append((_format_file_size(stat.st_size), item_relative_path)) - - collect_items(full_path, "", 1) - - header = f"Here're the files and directories up to 2 levels deep in {command.path}, excluding hidden items:" - dir_stat = full_path.stat() - dir_size = _format_file_size(dir_stat.st_size) - lines = [f"{dir_size}\t{command.path}"] - lines.extend([f"{size}\t{command.path}/{path}" for size, path in items]) - - return f"{header}\n" + "\n".join(lines) - - elif full_path.is_file(): - content = _read_file_content(full_path, command.path) - lines = content.split("\n") - - if len(lines) > MAX_LINES: - raise ToolError(f"File {command.path} exceeds maximum line limit of 999,999 lines.") - - display_lines = lines - start_num = 1 - - if command.view_range and len(command.view_range) == 2: - start_line = max(1, command.view_range[0]) - 1 - end_line = len(lines) if command.view_range[1] == -1 else command.view_range[1] - display_lines = lines[start_line:end_line] - start_num = start_line + 1 - - numbered_lines = [ - f"{str(i + start_num).rjust(LINE_NUMBER_WIDTH)}\t{line}" for i, line in enumerate(display_lines) - ] - - return f"Here's the content of {command.path} with line numbers:\n" + "\n".join(numbered_lines) - else: - raise ToolError(f"Unsupported file type for {command.path}") - - @override - def create(self, command: BetaMemoryTool20250818CreateCommand) -> str: - full_path = self._validate_path(command.path) - - full_path.parent.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) - - try: - fd = os.open(full_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, _FILE_CREATE_MODE) - try: - os.write(fd, command.file_text.encode("utf-8")) - os.fsync(fd) - finally: - os.close(fd) - except FileExistsError as err: - raise ToolError(f"File {command.path} already exists") from err - - return f"File created successfully at: {command.path}" - - @override - def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> str: - full_path = self._validate_path(command.path) - - if not full_path.exists(): - raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") - - if not full_path.is_file(): - raise ToolError(f"The path {command.path} is not a file.") - - content = _read_file_content(full_path, command.path) - - count = content.count(command.old_str) - if count == 0: - raise ToolError( - f"No replacement was performed, old_str `{command.old_str}` did not appear verbatim in {command.path}." - ) - elif count > 1: - matching_lines: List[int] = [] - start = 0 - while True: - pos = content.find(command.old_str, start) - if pos == -1: - break - matching_lines.append(content[:pos].count("\n") + 1) - start = pos + 1 - raise ToolError( - f"No replacement was performed. Multiple occurrences of old_str `{command.old_str}` in lines: {', '.join(map(str, matching_lines))}. Please ensure it is unique" - ) - - pos = content.find(command.old_str) - changed_line_index = content[:pos].count("\n") - new_content = content.replace(command.old_str, command.new_str) - _atomic_write_file(full_path, new_content) - - new_lines = new_content.split("\n") - context_start = max(0, changed_line_index - 2) - context_end = min(len(new_lines), changed_line_index + 3) - snippet = [ - f"{str(line_num).rjust(LINE_NUMBER_WIDTH)}\t{new_lines[line_num - 1]}" - for line_num in range(context_start + 1, context_end + 1) - ] - - return ( - f"The memory file has been edited. Here is the snippet showing the change (with line numbers):\n" - + "\n".join(snippet) - ) - - @override - def insert(self, command: BetaMemoryTool20250818InsertCommand) -> str: - full_path = self._validate_path(command.path) - - if not full_path.exists(): - raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") - - if not full_path.is_file(): - raise ToolError(f"The path {command.path} is not a file.") - - content = _read_file_content(full_path, command.path) - lines = content.splitlines() - - if command.insert_line < 0 or command.insert_line > len(lines): - raise ToolError( - f"Invalid `insert_line` parameter: {command.insert_line}. " - f"It should be within the range [0, {len(lines)}]." - ) - - lines.insert(command.insert_line, command.insert_text.rstrip("\n")) - new_content = "\n".join(lines) - if not new_content.endswith("\n"): - new_content += "\n" - _atomic_write_file(full_path, content=new_content) - return f"The file {command.path} has been edited." - - @override - def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> str: - full_path = self._validate_path(command.path) - - if command.path == "/memories": - raise ToolError("Cannot delete the /memories directory itself") - - try: - if full_path.is_file(): - full_path.unlink() - elif full_path.is_dir(): - shutil.rmtree(full_path) - else: - raise ToolError(f"The path {command.path} does not exist") - except FileNotFoundError as err: - raise ToolError(f"The path {command.path} does not exist") from err - - return f"Successfully deleted {command.path}" - - @override - def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str: - old_full_path = self._validate_path(command.old_path) - new_full_path = self._validate_path(command.new_path) - - if new_full_path.exists(): - raise ToolError(f"The destination {command.new_path} already exists") - - new_full_path.parent.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) - - try: - old_full_path.rename(new_full_path) - except FileNotFoundError as err: - raise ToolError(f"The path {command.old_path} does not exist") from err - - return f"Successfully renamed {command.old_path} to {command.new_path}" - - @override - def clear_all_memory(self) -> str: - """Override the base implementation to provide file system clearing.""" - if self.memory_root.exists(): - shutil.rmtree(self.memory_root) - self.memory_root.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) - return "All memory cleared" - - -async def _async_atomic_write_file(target_path: AsyncPath, content: str) -> None: - temp_path = target_path.parent / f".tmp-{os.getpid()}-{uuid.uuid4()}" - sync_target_path = Path(str(target_path)) - sync_temp_path = Path(str(temp_path)) - data = content.encode("utf-8") - - try: - - def write_replace_and_sync() -> None: - fd = os.open(sync_temp_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, _FILE_CREATE_MODE) - try: - offset = 0 - while offset < len(data): - written = os.write(fd, data[offset:]) - if written == 0: - raise OSError("os.write returned 0") - offset += written - - os.fsync(fd) - finally: - os.close(fd) - - os.replace(sync_temp_path, sync_target_path) - - await run_sync(write_replace_and_sync) - - except Exception: - await temp_path.unlink(missing_ok=True) - raise - - -async def _async_validate_no_symlink_escape(target_path: AsyncPath, memory_root: AsyncPath) -> None: - sync_target = Path(str(target_path)) - sync_root = Path(str(memory_root)) - await run_sync(_validate_no_symlink_escape, sync_target, sync_root) - - -async def _async_read_file_content(full_path: AsyncPath, memory_path: str) -> str: - try: - return await full_path.read_text(encoding="utf-8") - except FileNotFoundError as err: - raise ToolError( - f"The file {memory_path} no longer exists (may have been deleted or renamed concurrently)." - ) from err - - -class BetaAsyncLocalFilesystemMemoryTool(BetaAsyncAbstractMemoryTool): - """Async file-based memory storage implementation for Claude conversations""" - - def __init__(self, base_path: str = "./memory"): - super().__init__() - self.base_path = AsyncPath(base_path) - self.memory_root = self.base_path / "memories" - # Note: Directory creation is deferred to async methods since __init__ can't be async - - async def _ensure_memory_root(self) -> None: - """Ensure the memory root directory exists""" - await self.memory_root.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) - - async def _validate_path(self, path: str) -> AsyncPath: - """Validate and resolve memory paths""" - if not path.startswith("/memories"): - raise ToolError(f"Path must start with /memories, got: {path}") - - relative_path = path[len("/memories") :].lstrip("/") - full_path = self.memory_root / relative_path if relative_path else self.memory_root - - sync_memory_root = Path(str(self.memory_root)) - sync_full_path = Path(str(full_path)) - - resolved_path = sync_full_path.resolve() - resolved_root = sync_memory_root.resolve() - if resolved_path != resolved_root and not str(resolved_path).startswith(str(resolved_root) + os.sep): - raise ToolError(f"Path {path} would escape /memories directory") - - await _async_validate_no_symlink_escape(full_path, self.memory_root) - - return AsyncPath(resolved_path) - - @override - async def view(self, command: BetaMemoryTool20250818ViewCommand) -> str: - await self._ensure_memory_root() - full_path = await self._validate_path(command.path) - - if not await full_path.exists(): - raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") - - if await full_path.is_dir(): - items: List[tuple[str, str]] = [] - - async def collect_items(dir_path: AsyncPath, relative_path: str, depth: int) -> None: - if depth > 2: - return - - try: - dir_items = [item async for item in dir_path.iterdir()] - dir_contents = sorted(dir_items, key=lambda x: x.name) - except Exception: - return - - for item in dir_contents: - if item.name.startswith("."): - continue - item_relative_path = f"{relative_path}/{item.name}" if relative_path else item.name - try: - sync_item = Path(str(item)) - stat = await run_sync(sync_item.stat) - except Exception: - continue - - if await item.is_dir(): - items.append((_format_file_size(stat.st_size), f"{item_relative_path}/")) - if depth < 2: - await collect_items(item, item_relative_path, depth + 1) - elif await item.is_file(): - items.append((_format_file_size(stat.st_size), item_relative_path)) - - await collect_items(full_path, "", 1) - - header = f"Here're the files and directories up to 2 levels deep in {command.path}, excluding hidden items:" - sync_full_path = Path(str(full_path)) - dir_stat = await run_sync(sync_full_path.stat) - dir_size = _format_file_size(dir_stat.st_size) - lines = [f"{dir_size}\t{command.path}"] - lines.extend([f"{size}\t{command.path}/{path}" for size, path in items]) - - return f"{header}\n" + "\n".join(lines) - - elif await full_path.is_file(): - content = await _async_read_file_content(full_path, command.path) - lines = content.split("\n") - - if len(lines) > MAX_LINES: - raise ToolError(f"File {command.path} exceeds maximum line limit of 999,999 lines.") - - display_lines = lines - start_num = 1 - - if command.view_range and len(command.view_range) == 2: - start_line = max(1, command.view_range[0]) - 1 - end_line = len(lines) if command.view_range[1] == -1 else command.view_range[1] - display_lines = lines[start_line:end_line] - start_num = start_line + 1 - - numbered_lines = [ - f"{str(i + start_num).rjust(LINE_NUMBER_WIDTH)}\t{line}" for i, line in enumerate(display_lines) - ] - - return f"Here's the content of {command.path} with line numbers:\n" + "\n".join(numbered_lines) - else: - raise ToolError(f"Unsupported file type for {command.path}") - - @override - async def create(self, command: BetaMemoryTool20250818CreateCommand) -> str: - await self._ensure_memory_root() - full_path = await self._validate_path(command.path) - - await full_path.parent.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) - - try: - sync_full_path = Path(str(full_path)) - - def create_exclusive() -> None: - fd = os.open(sync_full_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, _FILE_CREATE_MODE) - try: - os.write(fd, command.file_text.encode("utf-8")) - os.fsync(fd) - finally: - os.close(fd) - - await run_sync(create_exclusive) - except FileExistsError as err: - raise ToolError(f"File {command.path} already exists") from err - - return f"File created successfully at: {command.path}" - - @override - async def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> str: - await self._ensure_memory_root() - full_path = await self._validate_path(command.path) - - if not await full_path.exists(): - raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") - - if not await full_path.is_file(): - raise ToolError(f"The path {command.path} is not a file.") - - content = await _async_read_file_content(full_path, command.path) - - count = content.count(command.old_str) - if count == 0: - raise ToolError( - f"No replacement was performed, old_str `{command.old_str}` did not appear verbatim in {command.path}." - ) - elif count > 1: - matching_lines: List[int] = [] - start = 0 - while True: - pos = content.find(command.old_str, start) - if pos == -1: - break - matching_lines.append(content[:pos].count("\n") + 1) - start = pos + 1 - raise ToolError( - f"No replacement was performed. Multiple occurrences of old_str `{command.old_str}` in lines: {', '.join(map(str, matching_lines))}. Please ensure it is unique" - ) - - pos = content.find(command.old_str) - changed_line_index = content[:pos].count("\n") - new_content = content.replace(command.old_str, command.new_str) - await _async_atomic_write_file(full_path, new_content) - - new_lines = new_content.split("\n") - context_start = max(0, changed_line_index - 2) - context_end = min(len(new_lines), changed_line_index + 3) - snippet = [ - f"{str(line_num).rjust(LINE_NUMBER_WIDTH)}\t{new_lines[line_num - 1]}" - for line_num in range(context_start + 1, context_end + 1) - ] - - return ( - f"The memory file has been edited. Here is the snippet showing the change (with line numbers):\n" - + "\n".join(snippet) - ) - - @override - async def insert(self, command: BetaMemoryTool20250818InsertCommand) -> str: - await self._ensure_memory_root() - full_path = await self._validate_path(command.path) - - if not await full_path.exists(): - raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") - - if not await full_path.is_file(): - raise ToolError(f"The path {command.path} is not a file.") - - content = await _async_read_file_content(full_path, command.path) - lines = content.splitlines() - - if command.insert_line < 0 or command.insert_line > len(lines): - raise ToolError( - f"Invalid `insert_line` parameter: {command.insert_line}. " - f"It should be within the range [0, {len(lines)}]." - ) - - lines.insert(command.insert_line, command.insert_text.rstrip("\n")) - new_content = "\n".join(lines) - if not new_content.endswith("\n"): - new_content += "\n" - await _async_atomic_write_file(full_path, content=new_content) - return f"The file {command.path} has been edited." - - @override - async def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> str: - await self._ensure_memory_root() - full_path = await self._validate_path(command.path) - - if command.path == "/memories": - raise ToolError("Cannot delete the /memories directory itself") - - try: - if await full_path.is_file(): - await full_path.unlink() - elif await full_path.is_dir(): - await run_sync(shutil.rmtree, str(full_path)) - else: - raise ToolError(f"The path {command.path} does not exist") - except FileNotFoundError as err: - raise ToolError(f"The path {command.path} does not exist") from err - - return f"Successfully deleted {command.path}" - - @override - async def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str: - await self._ensure_memory_root() - old_full_path = await self._validate_path(command.old_path) - new_full_path = await self._validate_path(command.new_path) - - if await new_full_path.exists(): - raise ToolError(f"The destination {command.new_path} already exists") - - await new_full_path.parent.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) - - try: - await old_full_path.rename(new_full_path) - except FileNotFoundError as err: - raise ToolError(f"The path {command.old_path} does not exist") from err - - return f"Successfully renamed {command.old_path} to {command.new_path}" - - @override - async def clear_all_memory(self) -> str: - """Override the base implementation to provide file system clearing.""" - if await self.memory_root.exists(): - await run_sync(shutil.rmtree, str(self.memory_root)) - await self.memory_root.mkdir(parents=True, exist_ok=True, mode=_DIR_CREATE_MODE) - return "All memory cleared" diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_compaction_control.py b/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_compaction_control.py deleted file mode 100644 index e1e07b6a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_compaction_control.py +++ /dev/null @@ -1,55 +0,0 @@ -from typing import TypedDict -from typing_extensions import Required - -DEFAULT_SUMMARY_PROMPT = """You have been working on the task described above but have not yet completed it. Write a continuation summary that will allow you (or another instance of yourself) to resume work efficiently in a future context window where the conversation history will be replaced with this summary. Your summary should be structured, concise, and actionable. Include: -1. Task Overview -The user's core request and success criteria -Any clarifications or constraints they specified -2. Current State -What has been completed so far -Files created, modified, or analyzed (with paths if relevant) -Key outputs or artifacts produced -3. Important Discoveries -Technical constraints or requirements uncovered -Decisions made and their rationale -Errors encountered and how they were resolved -What approaches were tried that didn't work (and why) -4. Next Steps -Specific actions needed to complete the task -Any blockers or open questions to resolve -Priority order if multiple steps remain -5. Context to Preserve -User preferences or style requirements -Domain-specific details that aren't obvious -Any promises made to the user -Be concise but complete—err on the side of including information that would prevent duplicate work or repeated mistakes. Write in a way that enables immediate resumption of the task. -Wrap your summary in

tags.""" - -DEFAULT_THRESHOLD = 100_000 - - -class CompactionControl(TypedDict, total=False): - """Client-side compaction control configuration. - - .. deprecated:: - Use server-side compaction instead by passing - ``edits=[{"type": "compact_20260112"}]`` in the params passed to ``tool_runner()``. - See https://platform.claude.com/docs/en/build-with-claude/compaction - """ - context_token_threshold: int - """The context token threshold at which to trigger compaction. - - When the cumulative token count (input + output) across all messages exceeds this threshold, - the message history will be automatically summarized and compressed. Defaults to 100,000 tokens. - """ - - model: str - """ - The model to use for generating the compaction summary. - If not specified, defaults to the same model used for the tool runner. - """ - - summary_prompt: str - """The prompt used to instruct the model on how to generate the summary.""" - - enabled: Required[bool] diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_functions.py b/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_functions.py deleted file mode 100644 index c5d73b07..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_functions.py +++ /dev/null @@ -1,429 +0,0 @@ -from __future__ import annotations - -import logging -from abc import ABC, abstractmethod -from typing import Any, Union, Generic, TypeVar, Callable, Iterable, Coroutine, cast, overload -from inspect import iscoroutinefunction -from typing_extensions import Literal, TypeAlias, override - -import pydantic -import docstring_parser -from pydantic import BaseModel - -from ... import _compat -from ..._utils import is_dict -from ..._compat import cached_property -from ..._models import TypeAdapter -from ...types.beta import BetaToolParam, BetaToolUnionParam, BetaCacheControlEphemeralParam -from ..._utils._utils import CallableT -from ...types.tool_param import InputSchema -from ...types.beta.beta_tool_result_block_param import Content as BetaContent - -log = logging.getLogger(__name__) - -BetaFunctionToolResultType: TypeAlias = Union[str, Iterable[BetaContent]] - - -class ToolError(Exception): - """Error that can be raised from a tool to return structured content with ``is_error: True``. - - When the tool runner catches this error, it will use the :attr:`content` - property as the tool result instead of ``repr(exc)``. - - Example:: - - raise ToolError( - [ - {"type": "text", "text": "Error details here"}, - {"type": "image", "source": {"type": "base64", "data": "...", "media_type": "image/png"}}, - ] - ) - """ - - content: BetaFunctionToolResultType - - def __init__(self, content: BetaFunctionToolResultType) -> None: - if isinstance(content, str): - message = content - else: - parts: list[str] = [] - for block in content: - text = block.get("text") - if text is not None: - parts.append(str(text)) - else: - parts.append(f"[{block.get('type', 'unknown')}]") - message = " ".join(parts) if parts else "Tool error" - super().__init__(message) - self.content = content - - -Function = Callable[..., BetaFunctionToolResultType] -FunctionT = TypeVar("FunctionT", bound=Function) - -AsyncFunction = Callable[..., Coroutine[Any, Any, BetaFunctionToolResultType]] -AsyncFunctionT = TypeVar("AsyncFunctionT", bound=AsyncFunction) - - -class BetaBuiltinFunctionTool(ABC): - @abstractmethod - def to_dict(self) -> BetaToolUnionParam: ... - - @abstractmethod - def call(self, input: object) -> BetaFunctionToolResultType: ... - - @property - def name(self) -> str: - raw = self.to_dict() - if "mcp_server_name" in raw: - return raw["mcp_server_name"] - return raw["name"] - - -class BetaAsyncBuiltinFunctionTool(ABC): - @abstractmethod - def to_dict(self) -> BetaToolUnionParam: ... - - @abstractmethod - async def call(self, input: object) -> BetaFunctionToolResultType: ... - - @property - def name(self) -> str: - raw = self.to_dict() - if "mcp_server_name" in raw: - return raw["mcp_server_name"] - return raw["name"] - - -class BaseFunctionTool(Generic[CallableT]): - func: CallableT - """The function this tool is wrapping""" - - name: str - """The name of the tool that will be sent to the API""" - - description: str - - input_schema: InputSchema - - def __init__( - self, - func: CallableT, - *, - name: str | None = None, - description: str | None = None, - input_schema: InputSchema | type[BaseModel] | None = None, - defer_loading: bool | None = None, - cache_control: BetaCacheControlEphemeralParam | None = None, - allowed_callers: list[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] | None = None, - eager_input_streaming: bool | None = None, - input_examples: Iterable[dict[str, object]] | None = None, - strict: bool | None = None, - ) -> None: - if _compat.PYDANTIC_V1: - raise RuntimeError("Tool functions are only supported with Pydantic v2") - - self.func = func - self._func_with_validate = pydantic.validate_call(func) - self.name = name or func.__name__ - self._defer_loading = defer_loading - self._cache_control = cache_control - self._allowed_callers = allowed_callers - self._eager_input_streaming = eager_input_streaming - self._input_examples = input_examples - self._strict = strict - - self.description = description or self._get_description_from_docstring() - - if input_schema is not None: - if isinstance(input_schema, type): - self.input_schema: InputSchema = input_schema.model_json_schema() - else: - self.input_schema = input_schema - else: - self.input_schema = self._create_schema_from_function() - - @property - def __call__(self) -> CallableT: - return self.func - - def to_dict(self) -> BetaToolParam: - defn: BetaToolParam = { - "name": self.name, - "description": self.description, - "input_schema": self.input_schema, - } - if self._defer_loading is not None: - defn["defer_loading"] = self._defer_loading - if self._cache_control is not None: - defn["cache_control"] = self._cache_control - if self._allowed_callers is not None: - defn["allowed_callers"] = self._allowed_callers - if self._eager_input_streaming is not None: - defn["eager_input_streaming"] = self._eager_input_streaming - if self._input_examples is not None: - defn["input_examples"] = self._input_examples - if self._strict is not None: - defn["strict"] = self._strict - return defn - - @cached_property - def _parsed_docstring(self) -> docstring_parser.Docstring: - return docstring_parser.parse(self.func.__doc__ or "") - - def _get_description_from_docstring(self) -> str: - """Extract description from parsed docstring.""" - if self._parsed_docstring.short_description: - description = self._parsed_docstring.short_description - if self._parsed_docstring.long_description: - description += f"\n\n{self._parsed_docstring.long_description}" - return description - return "" - - def _create_schema_from_function(self) -> InputSchema: - """Create JSON schema from function signature using pydantic.""" - - from pydantic_core import CoreSchema - from pydantic.json_schema import JsonSchemaValue, GenerateJsonSchema - from pydantic_core.core_schema import ArgumentsParameter - - class CustomGenerateJsonSchema(GenerateJsonSchema): - def __init__(self, *, func: Callable[..., Any], parsed_docstring: Any) -> None: - super().__init__() - self._func = func - self._parsed_docstring = parsed_docstring - - def __call__(self, *_args: Any, **_kwds: Any) -> "CustomGenerateJsonSchema": # noqa: ARG002 - return self - - @override - def kw_arguments_schema( - self, - arguments: "list[ArgumentsParameter]", - var_kwargs_schema: CoreSchema | None, - ) -> JsonSchemaValue: - schema = super().kw_arguments_schema(arguments, var_kwargs_schema) - if schema.get("type") != "object": - return schema - - properties = schema.get("properties") - if not properties or not is_dict(properties): - return schema - - # Add parameter descriptions from docstring - for param in self._parsed_docstring.params: - prop_schema = properties.get(param.arg_name) - if not prop_schema or not is_dict(prop_schema): - continue - - if param.description and "description" not in prop_schema: - prop_schema["description"] = param.description - - return schema - - schema_generator = CustomGenerateJsonSchema(func=self.func, parsed_docstring=self._parsed_docstring) - return self._adapter.json_schema(schema_generator=schema_generator) # type: ignore - - @cached_property - def _adapter(self) -> TypeAdapter[Any]: - return TypeAdapter(self._func_with_validate) - - -class BetaFunctionTool(BaseFunctionTool[FunctionT]): - def call(self, input: object) -> BetaFunctionToolResultType: - if iscoroutinefunction(self.func): - raise RuntimeError("Cannot call a coroutine function synchronously. Use `@async_tool` instead.") - - if not is_dict(input): - raise TypeError(f"Input must be a dictionary, got {type(input).__name__}") - - try: - return self._func_with_validate(**cast(Any, input)) - except pydantic.ValidationError as e: - raise ValueError(f"Invalid arguments for function {self.name}") from e - - -class BetaAsyncFunctionTool(BaseFunctionTool[AsyncFunctionT]): - async def call(self, input: object) -> BetaFunctionToolResultType: - if not iscoroutinefunction(self.func): - raise RuntimeError("Cannot call a synchronous function asynchronously. Use `@tool` instead.") - - if not is_dict(input): - raise TypeError(f"Input must be a dictionary, got {type(input).__name__}") - - try: - return await self._func_with_validate(**cast(Any, input)) - except pydantic.ValidationError as e: - raise ValueError(f"Invalid arguments for function {self.name}") from e - - -@overload -def beta_tool(func: FunctionT) -> BetaFunctionTool[FunctionT]: ... - - -@overload -def beta_tool( - func: FunctionT, - *, - name: str | None = None, - description: str | None = None, - input_schema: InputSchema | type[BaseModel] | None = None, - defer_loading: bool | None = None, - cache_control: BetaCacheControlEphemeralParam | None = None, - allowed_callers: list[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] | None = None, - eager_input_streaming: bool | None = None, - input_examples: Iterable[dict[str, object]] | None = None, - strict: bool | None = None, -) -> BetaFunctionTool[FunctionT]: ... - - -@overload -def beta_tool( - *, - name: str | None = None, - description: str | None = None, - input_schema: InputSchema | type[BaseModel] | None = None, - defer_loading: bool | None = None, - cache_control: BetaCacheControlEphemeralParam | None = None, - allowed_callers: list[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] | None = None, - eager_input_streaming: bool | None = None, - input_examples: Iterable[dict[str, object]] | None = None, - strict: bool | None = None, -) -> Callable[[FunctionT], BetaFunctionTool[FunctionT]]: ... - - -def beta_tool( - func: FunctionT | None = None, - *, - name: str | None = None, - description: str | None = None, - input_schema: InputSchema | type[BaseModel] | None = None, - defer_loading: bool | None = None, - cache_control: BetaCacheControlEphemeralParam | None = None, - allowed_callers: list[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] | None = None, - eager_input_streaming: bool | None = None, - input_examples: Iterable[dict[str, object]] | None = None, - strict: bool | None = None, -) -> BetaFunctionTool[FunctionT] | Callable[[FunctionT], BetaFunctionTool[FunctionT]]: - """Create a FunctionTool from a function with automatic schema inference. - - Can be used as a decorator with or without parentheses: - - @function_tool - def my_func(x: int) -> str: ... - - @function_tool() - def my_func(x: int) -> str: ... - - @function_tool(name="custom_name") - def my_func(x: int) -> str: ... - """ - if _compat.PYDANTIC_V1: - raise RuntimeError("Tool functions are only supported with Pydantic v2") - - def _make(fn: FunctionT) -> BetaFunctionTool[FunctionT]: - return BetaFunctionTool( - fn, - name=name, - description=description, - input_schema=input_schema, - defer_loading=defer_loading, - cache_control=cache_control, - allowed_callers=allowed_callers, - eager_input_streaming=eager_input_streaming, - input_examples=input_examples, - strict=strict, - ) - - if func is not None: - return _make(func) - - return _make - - -@overload -def beta_async_tool(func: AsyncFunctionT) -> BetaAsyncFunctionTool[AsyncFunctionT]: ... - - -@overload -def beta_async_tool( - func: AsyncFunctionT, - *, - name: str | None = None, - description: str | None = None, - input_schema: InputSchema | type[BaseModel] | None = None, - defer_loading: bool | None = None, - cache_control: BetaCacheControlEphemeralParam | None = None, - allowed_callers: list[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] | None = None, - eager_input_streaming: bool | None = None, - input_examples: Iterable[dict[str, object]] | None = None, - strict: bool | None = None, -) -> BetaAsyncFunctionTool[AsyncFunctionT]: ... # noqa: E501 - - -@overload -def beta_async_tool( - *, - name: str | None = None, - description: str | None = None, - input_schema: InputSchema | type[BaseModel] | None = None, - defer_loading: bool | None = None, - cache_control: BetaCacheControlEphemeralParam | None = None, - allowed_callers: list[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] | None = None, - eager_input_streaming: bool | None = None, - input_examples: Iterable[dict[str, object]] | None = None, - strict: bool | None = None, -) -> Callable[[AsyncFunctionT], BetaAsyncFunctionTool[AsyncFunctionT]]: ... - - -def beta_async_tool( - func: AsyncFunctionT | None = None, - *, - name: str | None = None, - description: str | None = None, - input_schema: InputSchema | type[BaseModel] | None = None, - defer_loading: bool | None = None, - cache_control: BetaCacheControlEphemeralParam | None = None, - allowed_callers: list[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] | None = None, - eager_input_streaming: bool | None = None, - input_examples: Iterable[dict[str, object]] | None = None, - strict: bool | None = None, -) -> BetaAsyncFunctionTool[AsyncFunctionT] | Callable[[AsyncFunctionT], BetaAsyncFunctionTool[AsyncFunctionT]]: - """Create an AsyncFunctionTool from a function with automatic schema inference. - - Can be used as a decorator with or without parentheses: - - @async_tool - async def my_func(x: int) -> str: ... - - @async_tool() - async def my_func(x: int) -> str: ... - - @async_tool(name="custom_name") - async def my_func(x: int) -> str: ... - """ - if _compat.PYDANTIC_V1: - raise RuntimeError("Tool functions are only supported with Pydantic v2") - - def _make(fn: AsyncFunctionT) -> BetaAsyncFunctionTool[AsyncFunctionT]: - return BetaAsyncFunctionTool( - fn, - name=name, - description=description, - input_schema=input_schema, - defer_loading=defer_loading, - cache_control=cache_control, - allowed_callers=allowed_callers, - eager_input_streaming=eager_input_streaming, - input_examples=input_examples, - strict=strict, - ) - - if func is not None: - return _make(func) - - return _make - - -BetaRunnableTool = Union[BetaFunctionTool[Any], BetaBuiltinFunctionTool] -BetaAsyncRunnableTool = Union[BetaAsyncFunctionTool[Any], BetaAsyncBuiltinFunctionTool] diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_runner.py b/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_runner.py deleted file mode 100644 index 52e48698..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/_beta_runner.py +++ /dev/null @@ -1,685 +0,0 @@ -from __future__ import annotations - -import logging -import warnings -from abc import ABC, abstractmethod -from typing import ( - TYPE_CHECKING, - Any, - List, - Union, - Generic, - TypeVar, - Callable, - Iterable, - Iterator, - Coroutine, - AsyncIterator, -) -from contextlib import contextmanager, asynccontextmanager -from typing_extensions import TypedDict, override - -import httpx - -from ..._types import Body, Query, Headers, NotGiven -from ..._utils import consume_sync_iterator, consume_async_iterator -from ...types.beta import BetaMessage, BetaMessageParam -from ._beta_functions import ( - ToolError, - BetaFunctionTool, - BetaRunnableTool, - BetaAsyncFunctionTool, - BetaAsyncRunnableTool, - BetaBuiltinFunctionTool, - BetaAsyncBuiltinFunctionTool, -) -from .._stainless_helpers import stainless_helper_header -from ._beta_compaction_control import DEFAULT_THRESHOLD, DEFAULT_SUMMARY_PROMPT, CompactionControl -from ..streaming._beta_messages import BetaMessageStream, BetaAsyncMessageStream -from ...types.beta.parsed_beta_message import ResponseFormatT, ParsedBetaMessage, ParsedBetaContentBlock -from ...types.beta.message_create_params import ParseMessageCreateParamsBase -from ...types.beta.beta_tool_result_block_param import BetaToolResultBlockParam - -if TYPE_CHECKING: - from ..._client import Anthropic, AsyncAnthropic - - -AnyFunctionToolT = TypeVar( - "AnyFunctionToolT", - bound=Union[ - BetaFunctionTool[Any], BetaAsyncFunctionTool[Any], BetaBuiltinFunctionTool, BetaAsyncBuiltinFunctionTool - ], -) -RunnerItemT = TypeVar("RunnerItemT") - -log = logging.getLogger(__name__) - - -class RequestOptions(TypedDict, total=False): - extra_headers: Headers | None - extra_query: Query | None - extra_body: Body | None - timeout: float | httpx.Timeout | None | NotGiven - - -class BaseToolRunner(Generic[AnyFunctionToolT, ResponseFormatT]): - def __init__( - self, - *, - params: ParseMessageCreateParamsBase[ResponseFormatT], - options: RequestOptions, - tools: Iterable[AnyFunctionToolT], - max_iterations: int | None = None, - compaction_control: CompactionControl | None = None, - ) -> None: - self._tools_by_name = {tool.name: tool for tool in tools} - self._params: ParseMessageCreateParamsBase[ResponseFormatT] = { - **params, - "messages": [message for message in params["messages"]], - } - helper_header = stainless_helper_header( - tools=self._tools_by_name.values(), - messages=params.get("messages"), - ) - if helper_header: - merged_headers = {**helper_header, **(options.get("extra_headers") or {})} - options = {**options, "extra_headers": merged_headers} - self._options = options - self._messages_modified = False - self._cached_tool_call_response: BetaMessageParam | None = None - self._max_iterations = max_iterations - self._iteration_count = 0 - self._compaction_control = compaction_control - - def set_messages_params( - self, - params: ParseMessageCreateParamsBase[ResponseFormatT] - | Callable[[ParseMessageCreateParamsBase[ResponseFormatT]], ParseMessageCreateParamsBase[ResponseFormatT]], - ) -> None: - """ - Update the parameters for the next API call. This invalidates any cached tool responses. - - Args: - params (ParsedMessageCreateParamsBase[ResponseFormatT] | Callable): Either new parameters or a function to mutate existing parameters - """ - if callable(params): - params = params(self._params) - self._params = params - - def append_messages(self, *messages: BetaMessageParam | ParsedBetaMessage[ResponseFormatT]) -> None: - """Add one or more messages to the conversation history. - - This invalidates the cached tool response, i.e. if tools were already called, then they will - be called again on the next loop iteration. - """ - message_params: List[BetaMessageParam] = [ - {"role": message.role, "content": message.content} if isinstance(message, BetaMessage) else message - for message in messages - ] - self._messages_modified = True - self.set_messages_params(lambda params: {**params, "messages": [*params["messages"], *message_params]}) - self._cached_tool_call_response = None - - def _should_stop(self) -> bool: - if self._max_iterations is not None and self._iteration_count >= self._max_iterations: - return True - return False - - -class BaseSyncToolRunner(BaseToolRunner[BetaRunnableTool, ResponseFormatT], Generic[RunnerItemT, ResponseFormatT], ABC): - def __init__( - self, - *, - params: ParseMessageCreateParamsBase[ResponseFormatT], - options: RequestOptions, - tools: Iterable[BetaRunnableTool], - client: Anthropic, - max_iterations: int | None = None, - compaction_control: CompactionControl | None = None, - ) -> None: - super().__init__( - params=params, - options=options, - tools=tools, - max_iterations=max_iterations, - compaction_control=compaction_control, - ) - self._client = client - - if compaction_control is not None and compaction_control.get("enabled"): - warnings.warn( - "The 'compaction_control' parameter is deprecated and will be removed in a future version. " - "Use server-side compaction instead by passing `edits=[{'type': 'compact_20260112'}]` in your " - "the params passed to `tool_runner()`. See https://platform.claude.com/docs/en/build-with-claude/compaction", - DeprecationWarning, - stacklevel=3, - ) - - self._iterator = self.__run__() - self._last_message: ( - Callable[[], ParsedBetaMessage[ResponseFormatT]] | ParsedBetaMessage[ResponseFormatT] | None - ) = None - - def __next__(self) -> RunnerItemT: - return self._iterator.__next__() - - def __iter__(self) -> Iterator[RunnerItemT]: - for item in self._iterator: - yield item - - @abstractmethod - @contextmanager - def _handle_request(self) -> Iterator[RunnerItemT]: - raise NotImplementedError() - yield # type: ignore[unreachable] - - def _check_and_compact(self) -> bool: - """ - Check token usage and compact messages if threshold exceeded. - Returns True if compaction was performed, False otherwise. - """ - if self._compaction_control is None or not self._compaction_control["enabled"]: - return False - - message = self._get_last_message() - tokens_used = 0 - if message is not None: - total_input_tokens = ( - message.usage.input_tokens - + (message.usage.cache_creation_input_tokens or 0) - + (message.usage.cache_read_input_tokens or 0) - ) - tokens_used = total_input_tokens + message.usage.output_tokens - - threshold = self._compaction_control.get("context_token_threshold", DEFAULT_THRESHOLD) - - if tokens_used < threshold: - return False - - # Perform compaction - log.info(f"Token usage {tokens_used} has exceeded the threshold of {threshold}. Performing compaction.") - - model = self._compaction_control.get("model", self._params["model"]) - - messages = list(self._params["messages"]) - - if messages[-1]["role"] == "assistant": - # Remove tool_use blocks from the last message to avoid 400 error - # (tool_use requires tool_result, which we don't have yet) - non_tool_blocks = [ - block - for block in messages[-1]["content"] - if isinstance(block, dict) and block.get("type") != "tool_use" - ] - - if non_tool_blocks: - messages[-1]["content"] = non_tool_blocks - else: - messages.pop() - - messages = [ - *messages, - BetaMessageParam( - role="user", - content=self._compaction_control.get("summary_prompt", DEFAULT_SUMMARY_PROMPT), - ), - ] - - response = self._client.beta.messages.create( - model=model, - messages=messages, - max_tokens=self._params["max_tokens"], - extra_headers={"X-Stainless-Helper": "compaction"}, - ) - - log.info(f"Compaction complete. New token usage: {response.usage.output_tokens}") - - first_content = list(response.content)[0] - - if first_content.type != "text": - raise ValueError("Compaction response content is not of type 'text'") - - self.set_messages_params( - lambda params: { - **params, - "messages": [ - { - "role": "user", - "content": [ - { - "type": "text", - "text": first_content.text, - } - ], - } - ], - } - ) - return True - - def __run__(self) -> Iterator[RunnerItemT]: - while not self._should_stop(): - with self._handle_request() as item: - yield item - message = self._get_last_message() - assert message is not None - - # Update container from response for programmatic tool calling support - last_assistant_message = self._get_last_assistant_message() - if last_assistant_message is not None and last_assistant_message.container is not None: - self._params["container"] = last_assistant_message.container.id - - self._iteration_count += 1 - - # If the compaction was performed, skip tool call generation this iteration - if not self._check_and_compact(): - response = self.generate_tool_call_response() - if response is None: - log.debug("Tool call was not requested, exiting from tool runner loop.") - return - - if not self._messages_modified: - self.append_messages(message, response) - - self._messages_modified = False - self._cached_tool_call_response = None - - def until_done(self) -> ParsedBetaMessage[ResponseFormatT]: - """ - Consumes the tool runner stream and returns the last message if it has not been consumed yet. - If it has, it simply returns the last message. - """ - consume_sync_iterator(self) - last_message = self._get_last_message() - assert last_message is not None - return last_message - - def generate_tool_call_response(self) -> BetaMessageParam | None: - """Generate a MessageParam by calling tool functions with any tool use blocks from the last message. - - Note the tool call response is cached, repeated calls to this method will return the same response. - - None can be returned if no tool call was applicable. - """ - if self._cached_tool_call_response is not None: - log.debug("Returning cached tool call response.") - return self._cached_tool_call_response - response = self._generate_tool_call_response() - self._cached_tool_call_response = response - return response - - def _generate_tool_call_response(self) -> BetaMessageParam | None: - content = self._get_last_assistant_message_content() - if not content: - return None - - tool_use_blocks = [block for block in content if block.type == "tool_use"] - if not tool_use_blocks: - return None - - results: list[BetaToolResultBlockParam] = [] - - for tool_use in tool_use_blocks: - tool = self._tools_by_name.get(tool_use.name) - if tool is None: - warnings.warn( - f"Tool '{tool_use.name}' not found in tool runner. " - f"Available tools: {list(self._tools_by_name.keys())}. " - f"If using a raw tool definition, handle the tool call manually and use `append_messages()` to add the result. " - f"Otherwise, pass the tool using `beta_tool(func)` or a `@beta_tool` decorated function.", - UserWarning, - stacklevel=3, - ) - results.append( - { - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": f"Error: Tool '{tool_use.name}' not found", - "is_error": True, - } - ) - continue - - try: - result = tool.call(tool_use.input) - results.append({"type": "tool_result", "tool_use_id": tool_use.id, "content": result}) - except ToolError as exc: - results.append( - { - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": exc.content, - "is_error": True, - } - ) - except Exception as exc: - log.exception(f"Error occurred while calling tool: {tool.name}", exc_info=exc) - results.append( - { - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": repr(exc), - "is_error": True, - } - ) - - return {"role": "user", "content": results} - - def _get_last_message(self) -> ParsedBetaMessage[ResponseFormatT] | None: - if callable(self._last_message): - return self._last_message() - return self._last_message - - def _get_last_assistant_message(self) -> ParsedBetaMessage[ResponseFormatT] | None: - last_message = self._get_last_message() - if last_message is None or last_message.role != "assistant" or not last_message.content: - return None - - return last_message - - def _get_last_assistant_message_content(self) -> list[ParsedBetaContentBlock[ResponseFormatT]] | None: - last_assistant_message = self._get_last_assistant_message() - if last_assistant_message is None: - return None - - return last_assistant_message.content - - -class BetaToolRunner(BaseSyncToolRunner[ParsedBetaMessage[ResponseFormatT], ResponseFormatT]): - @override - @contextmanager - def _handle_request(self) -> Iterator[ParsedBetaMessage[ResponseFormatT]]: - message = self._client.beta.messages.parse(**self._params, **self._options) - self._last_message = message - yield message - - -class BetaStreamingToolRunner(BaseSyncToolRunner[BetaMessageStream[ResponseFormatT], ResponseFormatT]): - @override - @contextmanager - def _handle_request(self) -> Iterator[BetaMessageStream[ResponseFormatT]]: - with self._client.beta.messages.stream(**self._params, **self._options) as stream: - self._last_message = stream.get_final_message - yield stream - - -class BaseAsyncToolRunner( - BaseToolRunner[BetaAsyncRunnableTool, ResponseFormatT], Generic[RunnerItemT, ResponseFormatT], ABC -): - def __init__( - self, - *, - params: ParseMessageCreateParamsBase[ResponseFormatT], - options: RequestOptions, - tools: Iterable[BetaAsyncRunnableTool], - client: AsyncAnthropic, - max_iterations: int | None = None, - compaction_control: CompactionControl | None = None, - ) -> None: - super().__init__( - params=params, - options=options, - tools=tools, - max_iterations=max_iterations, - compaction_control=compaction_control, - ) - self._client = client - - if compaction_control is not None and compaction_control.get("enabled"): - warnings.warn( - "The 'compaction_control' parameter is deprecated and will be removed in a future version. " - "Use server-side compaction instead by passing `edits=[{'type': 'compact_20260112'}]` in your " - "the params passed to `tool_runner()`. See https://platform.claude.com/docs/en/build-with-claude/compaction", - DeprecationWarning, - stacklevel=3, - ) - - self._iterator = self.__run__() - self._last_message: ( - Callable[[], Coroutine[None, None, ParsedBetaMessage[ResponseFormatT]]] - | ParsedBetaMessage[ResponseFormatT] - | None - ) = None - - async def __anext__(self) -> RunnerItemT: - return await self._iterator.__anext__() - - async def __aiter__(self) -> AsyncIterator[RunnerItemT]: - async for item in self._iterator: - yield item - - @abstractmethod - @asynccontextmanager - async def _handle_request(self) -> AsyncIterator[RunnerItemT]: - raise NotImplementedError() - yield # type: ignore[unreachable] - - async def _check_and_compact(self) -> bool: - """ - Check token usage and compact messages if threshold exceeded. - Returns True if compaction was performed, False otherwise. - """ - if self._compaction_control is None or not self._compaction_control["enabled"]: - return False - - message = await self._get_last_message() - tokens_used = 0 - if message is not None: - total_input_tokens = ( - message.usage.input_tokens - + (message.usage.cache_creation_input_tokens or 0) - + (message.usage.cache_read_input_tokens or 0) - ) - tokens_used = total_input_tokens + message.usage.output_tokens - - threshold = self._compaction_control.get("context_token_threshold", DEFAULT_THRESHOLD) - - if tokens_used < threshold: - return False - - # Perform compaction - log.info(f"Token usage {tokens_used} has exceeded the threshold of {threshold}. Performing compaction.") - - model = self._compaction_control.get("model", self._params["model"]) - - messages = list(self._params["messages"]) - - if messages[-1]["role"] == "assistant": - # Remove tool_use blocks from the last message to avoid 400 error - # (tool_use requires tool_result, which we don't have yet) - non_tool_blocks = [ - block - for block in messages[-1]["content"] - if isinstance(block, dict) and block.get("type") != "tool_use" - ] - - if non_tool_blocks: - messages[-1]["content"] = non_tool_blocks - else: - messages.pop() - - messages = [ - *messages, - BetaMessageParam( - role="user", - content=self._compaction_control.get("summary_prompt", DEFAULT_SUMMARY_PROMPT), - ), - ] - - response = await self._client.beta.messages.create( - model=model, - messages=messages, - max_tokens=self._params["max_tokens"], - extra_headers={"X-Stainless-Helper": "compaction"}, - ) - - log.info(f"Compaction complete. New token usage: {response.usage.output_tokens}") - - first_content = list(response.content)[0] - - if first_content.type != "text": - raise ValueError("Compaction response content is not of type 'text'") - - self.set_messages_params( - lambda params: { - **params, - "messages": [ - { - "role": "user", - "content": [ - { - "type": "text", - "text": first_content.text, - } - ], - } - ], - } - ) - return True - - async def __run__(self) -> AsyncIterator[RunnerItemT]: - while not self._should_stop(): - async with self._handle_request() as item: - yield item - message = await self._get_last_message() - assert message is not None - - # Update container from response for programmatic tool calling support - last_assistant_message = await self._get_last_assistant_message() - if last_assistant_message is not None and last_assistant_message.container is not None: - self._params["container"] = last_assistant_message.container.id - - self._iteration_count += 1 - - # If the compaction was performed, skip tool call generation this iteration - if not await self._check_and_compact(): - response = await self.generate_tool_call_response() - if response is None: - log.debug("Tool call was not requested, exiting from tool runner loop.") - return - - if not self._messages_modified: - self.append_messages(message, response) - - self._messages_modified = False - self._cached_tool_call_response = None - - async def until_done(self) -> ParsedBetaMessage[ResponseFormatT]: - """ - Consumes the tool runner stream and returns the last message if it has not been consumed yet. - If it has, it simply returns the last message. - """ - await consume_async_iterator(self) - last_message = await self._get_last_message() - assert last_message is not None - return last_message - - async def generate_tool_call_response(self) -> BetaMessageParam | None: - """Generate a MessageParam by calling tool functions with any tool use blocks from the last message. - - Note the tool call response is cached, repeated calls to this method will return the same response. - - None can be returned if no tool call was applicable. - """ - if self._cached_tool_call_response is not None: - log.debug("Returning cached tool call response.") - return self._cached_tool_call_response - - response = await self._generate_tool_call_response() - self._cached_tool_call_response = response - return response - - async def _get_last_message(self) -> ParsedBetaMessage[ResponseFormatT] | None: - if callable(self._last_message): - return await self._last_message() - return self._last_message - - async def _get_last_assistant_message(self) -> ParsedBetaMessage[ResponseFormatT] | None: - last_message = await self._get_last_message() - if last_message is None or last_message.role != "assistant" or not last_message.content: - return None - - return last_message - - async def _get_last_assistant_message_content(self) -> list[ParsedBetaContentBlock[ResponseFormatT]] | None: - last_assistant_message = await self._get_last_assistant_message() - if last_assistant_message is None: - return None - - return last_assistant_message.content - - async def _generate_tool_call_response(self) -> BetaMessageParam | None: - content = await self._get_last_assistant_message_content() - if not content: - return None - - tool_use_blocks = [block for block in content if block.type == "tool_use"] - if not tool_use_blocks: - return None - - results: list[BetaToolResultBlockParam] = [] - - for tool_use in tool_use_blocks: - tool = self._tools_by_name.get(tool_use.name) - if tool is None: - warnings.warn( - f"Tool '{tool_use.name}' not found in tool runner. " - f"Available tools: {list(self._tools_by_name.keys())}. " - f"If using a raw tool definition, handle the tool call manually and use `append_messages()` to add the result. " - f"Otherwise, pass the tool using `beta_async_tool(func)` or a `@beta_async_tool` decorated function.", - UserWarning, - stacklevel=3, - ) - results.append( - { - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": f"Error: Tool '{tool_use.name}' not found", - "is_error": True, - } - ) - continue - - try: - result = await tool.call(tool_use.input) - results.append({"type": "tool_result", "tool_use_id": tool_use.id, "content": result}) - except ToolError as exc: - results.append( - { - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": exc.content, - "is_error": True, - } - ) - except Exception as exc: - log.exception(f"Error occurred while calling tool: {tool.name}", exc_info=exc) - results.append( - { - "type": "tool_result", - "tool_use_id": tool_use.id, - "content": repr(exc), - "is_error": True, - } - ) - - return {"role": "user", "content": results} - - -class BetaAsyncToolRunner(BaseAsyncToolRunner[ParsedBetaMessage[ResponseFormatT], ResponseFormatT]): - @override - @asynccontextmanager - async def _handle_request(self) -> AsyncIterator[ParsedBetaMessage[ResponseFormatT]]: - message = await self._client.beta.messages.parse(**self._params, **self._options) - self._last_message = message - yield message - - -class BetaAsyncStreamingToolRunner(BaseAsyncToolRunner[BetaAsyncMessageStream[ResponseFormatT], ResponseFormatT]): - @override - @asynccontextmanager - async def _handle_request(self) -> AsyncIterator[BetaAsyncMessageStream[ResponseFormatT]]: - async with self._client.beta.messages.stream(**self._params, **self._options) as stream: - self._last_message = stream.get_final_message - yield stream diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/mcp.py b/.venv/lib/python3.12/site-packages/anthropic/lib/tools/mcp.py deleted file mode 100644 index 219f5cca..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/tools/mcp.py +++ /dev/null @@ -1,442 +0,0 @@ -"""Helpers for integrating MCP (Model Context Protocol) SDK types with the Anthropic SDK. - -These helpers reduce boilerplate when converting between MCP types and Anthropic API types. - -Usage:: - - from anthropic.lib.tools.mcp import mcp_tool, async_mcp_tool, mcp_message - -This module requires the ``mcp`` package to be installed. -""" -# pyright: reportUnknownArgumentType=false, reportUnknownMemberType=false, reportUnknownVariableType=false, reportMissingImports=false, reportUnknownParameterType=false - -from __future__ import annotations - -import json -import base64 -from typing import Any, Iterable -from urllib.parse import urlparse -from typing_extensions import Literal - -try: - from mcp.types import ( # type: ignore[import-not-found] - Tool, - TextContent, - ContentBlock, - ImageContent, - PromptMessage, - CallToolResult, - EmbeddedResource, - ReadResourceResult, - BlobResourceContents, - TextResourceContents, - ) - from mcp.client.session import ClientSession # type: ignore[import-not-found] -except ImportError as _err: - raise ImportError( - "The `mcp` package is required to use MCP helpers. Install it with: pip install anthropic[mcp]. Requires Python 3.10 or higher." - ) from _err - -from ...types.beta import ( - BetaBase64PDFSourceParam, - BetaPlainTextSourceParam, - BetaBase64ImageSourceParam, - BetaCacheControlEphemeralParam, -) -from ._beta_functions import ( - ToolError, - BetaFunctionTool, - BetaAsyncFunctionTool, - BetaFunctionToolResultType, - beta_tool, - beta_async_tool, -) -from .._stainless_helpers import tag_helper -from ...types.beta.beta_tool_result_block_param import Content as BetaContent - -__all__ = [ - "mcp_tool", - "async_mcp_tool", - "mcp_content", - "mcp_message", - "mcp_resource_to_content", - "mcp_resource_to_file", - "UnsupportedMCPValueError", -] - -# ----------------------------------------------------------------------- -# Supported MIME types -# ----------------------------------------------------------------------- - -_SUPPORTED_IMAGE_TYPES = frozenset({"image/jpeg", "image/png", "image/gif", "image/webp"}) - - -class _TaggedDict(dict): # type: ignore[type-arg] - """A dict subclass that can carry a ``_stainless_helper`` attribute. - - Behaves identically to a regular dict for serialization and isinstance checks, - but allows attaching tracking metadata that won't appear in JSON output. - """ - - -class _TaggedTuple(tuple): # type: ignore[type-arg] - """A tuple subclass that can carry a ``_stainless_helper`` attribute.""" - - -def _is_supported_image_type(mime_type: str) -> bool: - return mime_type in _SUPPORTED_IMAGE_TYPES - - -def _is_supported_resource_mime_type(mime_type: str | None) -> bool: - return ( - mime_type is None - or mime_type.startswith("text/") - or mime_type == "application/pdf" - or _is_supported_image_type(mime_type) - ) - - -# ----------------------------------------------------------------------- -# Errors -# ----------------------------------------------------------------------- - - -class UnsupportedMCPValueError(Exception): - """Raised when an MCP value cannot be converted to a format supported by the Claude API.""" - - -# ----------------------------------------------------------------------- -# Content conversion -# ----------------------------------------------------------------------- - - -def mcp_content( - content: ContentBlock, - *, - cache_control: BetaCacheControlEphemeralParam | None = None, -) -> BetaContent: - """Convert a single MCP content block to an Anthropic content block. - - Handles text, image, and embedded resource content types. - Raises :class:`UnsupportedMCPValueError` for audio and resource_link types. - """ - if isinstance(content, TextContent): - block = _TaggedDict({"type": "text", "text": content.text}) - if cache_control is not None: - block["cache_control"] = cache_control - tag_helper(block, "mcp_content") - return block # type: ignore[return-value] - - if isinstance(content, ImageContent): - if not _is_supported_image_type(content.mimeType): - raise UnsupportedMCPValueError(f"Unsupported image MIME type: {content.mimeType}") - image_block = _TaggedDict( - { - "type": "image", - "source": BetaBase64ImageSourceParam( - type="base64", - data=content.data, - media_type=content.mimeType, # type: ignore[typeddict-item] - ), - } - ) - if cache_control is not None: - image_block["cache_control"] = cache_control - tag_helper(image_block, "mcp_content") - return image_block # type: ignore[return-value] - - if isinstance(content, EmbeddedResource): - return _resource_contents_to_block(content.resource, cache_control=cache_control) - - # audio, resource_link, or unknown - content_type = getattr(content, "type", type(content).__name__) - raise UnsupportedMCPValueError(f"Unsupported MCP content type: {content_type}") - - -def _resource_contents_to_block( - resource: TextResourceContents | BlobResourceContents, - *, - cache_control: BetaCacheControlEphemeralParam | None = None, -) -> BetaContent: - """Convert MCP resource contents to an Anthropic content block.""" - mime_type = resource.mimeType - - # Images - if mime_type is not None and _is_supported_image_type(mime_type): - if not isinstance(resource, BlobResourceContents): - raise UnsupportedMCPValueError(f"Image resource must have blob data, not text. URI: {resource.uri}") - image_block = _TaggedDict( - { - "type": "image", - "source": BetaBase64ImageSourceParam( - type="base64", - data=resource.blob, - media_type=mime_type, # type: ignore[typeddict-item] - ), - } - ) - if cache_control is not None: - image_block["cache_control"] = cache_control - tag_helper(image_block, "mcp_resource_to_content") - return image_block # type: ignore[return-value] - - # PDFs - if mime_type == "application/pdf": - if not isinstance(resource, BlobResourceContents): - raise UnsupportedMCPValueError(f"PDF resource must have blob data, not text. URI: {resource.uri}") - pdf_block = _TaggedDict( - { - "type": "document", - "source": BetaBase64PDFSourceParam( - type="base64", - data=resource.blob, - media_type="application/pdf", - ), - } - ) - if cache_control is not None: - pdf_block["cache_control"] = cache_control - tag_helper(pdf_block, "mcp_resource_to_content") - return pdf_block # type: ignore[return-value] - - # Text (text/*, or no MIME type) - if mime_type is None or mime_type.startswith("text/"): - if isinstance(resource, TextResourceContents): - data = resource.text - else: - data = base64.b64decode(resource.blob).decode("utf-8") - text_block = _TaggedDict( - { - "type": "document", - "source": BetaPlainTextSourceParam( - type="text", - data=data, - media_type="text/plain", - ), - } - ) - if cache_control is not None: - text_block["cache_control"] = cache_control - tag_helper(text_block, "mcp_resource_to_content") - return text_block # type: ignore[return-value] - - raise UnsupportedMCPValueError(f'Unsupported MIME type "{mime_type}" for resource: {resource.uri}') - - -# ----------------------------------------------------------------------- -# Message conversion -# ----------------------------------------------------------------------- - - -def mcp_message( - message: PromptMessage, - *, - cache_control: BetaCacheControlEphemeralParam | None = None, -) -> dict[str, Any]: - """Convert an MCP prompt message to an Anthropic ``BetaMessageParam``.""" - result = _TaggedDict( - { - "role": message.role, - "content": [mcp_content(message.content, cache_control=cache_control)], - } - ) - tag_helper(result, "mcp_message") - return result - - -# ----------------------------------------------------------------------- -# Resource conversion -# ----------------------------------------------------------------------- - - -def mcp_resource_to_content( - result: ReadResourceResult, - *, - cache_control: BetaCacheControlEphemeralParam | None = None, -) -> BetaContent: - """Convert MCP resource contents to an Anthropic content block. - - Finds the first resource with a supported MIME type from the result's - ``contents`` list. - """ - if not result.contents: - raise UnsupportedMCPValueError("Resource contents array must contain at least one item") - - supported = next( - (c for c in result.contents if _is_supported_resource_mime_type(c.mimeType)), - None, - ) - if supported is None: - mime_types = [c.mimeType for c in result.contents if c.mimeType is not None] - raise UnsupportedMCPValueError( - f"No supported MIME type found in resource contents. Available: {', '.join(mime_types)}" - ) - - return _resource_contents_to_block(supported, cache_control=cache_control) - - -def mcp_resource_to_file( - result: ReadResourceResult, -) -> tuple[str | None, bytes, str | None]: - """Convert MCP resource contents to a file tuple for ``files.upload()``. - - Returns a ``(filename, content_bytes, mime_type)`` tuple compatible with - the SDK's ``FileTypes``. - """ - if not result.contents: - raise UnsupportedMCPValueError("Resource contents array must contain at least one item") - - resource = result.contents[0] - uri_str = str(resource.uri) - - # Extract filename from URI - path = urlparse(uri_str).path - name = path.rsplit("/", 1)[-1] if path else None - - # Get bytes - if isinstance(resource, BlobResourceContents): - content_bytes = base64.b64decode(resource.blob) - else: - content_bytes = resource.text.encode("utf-8") - - file_tuple = _TaggedTuple((name, content_bytes, resource.mimeType)) - tag_helper(file_tuple, "mcp_resource_to_file") - return file_tuple - - -# ----------------------------------------------------------------------- -# Tool result conversion (used by tool call handlers) -# ----------------------------------------------------------------------- - - -def _convert_tool_result(result: CallToolResult) -> BetaFunctionToolResultType: - """Convert MCP ``CallToolResult`` to a value suitable for returning from ``call()``.""" - if result.isError: - raise ToolError([mcp_content(item) for item in result.content]) - - # If content is empty but structuredContent is present, JSON-encode it - if not result.content and result.structuredContent is not None: - return json.dumps(result.structuredContent) - - return [mcp_content(item) for item in result.content] - - -# ----------------------------------------------------------------------- -# Public factory functions -# ----------------------------------------------------------------------- - - -def mcp_tool( - tool: Tool, - client: ClientSession, - *, - cache_control: BetaCacheControlEphemeralParam | None = None, - defer_loading: bool | None = None, - allowed_callers: list[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] | None = None, - eager_input_streaming: bool | None = None, - input_examples: Iterable[dict[str, object]] | None = None, - strict: bool | None = None, -) -> BetaFunctionTool[Any]: - """Convert an MCP tool to a sync runnable tool for ``tool_runner()``. - - Example:: - - from anthropic.lib.tools.mcp import mcp_tool - - tools_result = await mcp_client.list_tools() - runner = client.beta.messages.tool_runner( - model="claude-sonnet-4-20250514", - max_tokens=1024, - tools=[mcp_tool(t, mcp_client) for t in tools_result.tools], - messages=[{"role": "user", "content": "Use the available tools"}], - ) - - Args: - tool: An MCP tool definition from ``client.list_tools()``. - client: The MCP ``ClientSession`` used to call the tool. - cache_control: Cache control configuration. - defer_loading: If true, tool will not be included in initial system prompt. - allowed_callers: Which callers may use this tool. - eager_input_streaming: Enable eager input streaming for this tool. - input_examples: Example inputs for the tool. - strict: When true, guarantees schema validation on tool names and inputs. - """ - import anyio.from_thread - - tool_name = tool.name - - def call_mcp(**kwargs: Any) -> BetaFunctionToolResultType: - result = anyio.from_thread.run(client.call_tool, tool_name, kwargs) - return _convert_tool_result(result) - - result = beta_tool( - call_mcp, - name=tool_name, - description=tool.description, - input_schema=tool.inputSchema, - cache_control=cache_control, - defer_loading=defer_loading, - allowed_callers=allowed_callers, - eager_input_streaming=eager_input_streaming, - input_examples=input_examples, - strict=strict, - ) - tag_helper(result, "mcp_tool") - return result - - -def async_mcp_tool( - tool: Tool, - client: ClientSession, - *, - cache_control: BetaCacheControlEphemeralParam | None = None, - defer_loading: bool | None = None, - allowed_callers: list[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] | None = None, - eager_input_streaming: bool | None = None, - input_examples: Iterable[dict[str, object]] | None = None, - strict: bool | None = None, -) -> BetaAsyncFunctionTool[Any]: - """Convert an MCP tool to an async runnable tool for ``tool_runner()``. - - Example:: - - from anthropic.lib.tools.mcp import async_mcp_tool - - tools_result = await mcp_client.list_tools() - runner = await client.beta.messages.tool_runner( - model="claude-sonnet-4-20250514", - max_tokens=1024, - tools=[async_mcp_tool(t, mcp_client) for t in tools_result.tools], - messages=[{"role": "user", "content": "Use the available tools"}], - ) - - Args: - tool: An MCP tool definition from ``client.list_tools()``. - client: The MCP ``ClientSession`` used to call the tool. - cache_control: Cache control configuration. - defer_loading: If true, tool will not be included in initial system prompt. - allowed_callers: Which callers may use this tool. - eager_input_streaming: Enable eager input streaming for this tool. - input_examples: Example inputs for the tool. - strict: When true, guarantees schema validation on tool names and inputs. - """ - tool_name = tool.name - - async def call_mcp(**kwargs: Any) -> BetaFunctionToolResultType: - result = await client.call_tool(name=tool_name, arguments=kwargs) - return _convert_tool_result(result) - - result = beta_async_tool( - call_mcp, - name=tool_name, - description=tool.description, - input_schema=tool.inputSchema, - cache_control=cache_control, - defer_loading=defer_loading, - allowed_callers=allowed_callers, - eager_input_streaming=eager_input_streaming, - input_examples=input_examples, - strict=strict, - ) - tag_helper(result, "mcp_tool") - return result diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/__init__.py deleted file mode 100644 index 45b6301e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from ._client import AnthropicVertex as AnthropicVertex, AsyncAnthropicVertex as AsyncAnthropicVertex diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_auth.py b/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_auth.py deleted file mode 100644 index 7502fbf9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_auth.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, cast - -from .._extras import google_auth - -if TYPE_CHECKING: - from google.auth.credentials import Credentials # type: ignore[import-untyped] - -# pyright: reportMissingTypeStubs=false, reportUnknownVariableType=false, reportUnknownMemberType=false, reportUnknownArgumentType=false -# google libraries don't provide types :/ - -# Note: these functions are blocking as they make HTTP requests, the async -# client runs these functions in a separate thread to ensure they do not -# cause synchronous blocking issues. - - -def load_auth(*, project_id: str | None) -> tuple[Credentials, str]: - try: - from google.auth.transport.requests import Request # type: ignore[import-untyped] - except ModuleNotFoundError as err: - raise RuntimeError( - f"Could not import google.auth, you need to install the SDK with `pip install anthropic[vertex]`" - ) from err - - credentials, loaded_project_id = google_auth.default( - scopes=["https://www.googleapis.com/auth/cloud-platform"], - ) - credentials = cast(Any, credentials) - credentials.refresh(Request()) - - if not project_id: - project_id = loaded_project_id - - if not project_id: - raise ValueError("Could not resolve project_id") - - return credentials, project_id - - -def refresh_auth(credentials: Credentials) -> None: - from google.auth.transport.requests import Request # type: ignore[import-untyped] - - credentials.refresh(Request()) diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_beta.py b/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_beta.py deleted file mode 100644 index f2a91b42..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_beta.py +++ /dev/null @@ -1,102 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ._beta_messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, - MessagesWithStreamingResponse, - AsyncMessagesWithStreamingResponse, -) - -__all__ = ["Beta", "AsyncBeta"] - - -class Beta(SyncAPIResource): - @cached_property - def messages(self) -> Messages: - return Messages(self._client) - - @cached_property - def with_raw_response(self) -> BetaWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return the - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return BetaWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> BetaWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return BetaWithStreamingResponse(self) - - -class AsyncBeta(AsyncAPIResource): - @cached_property - def messages(self) -> AsyncMessages: - return AsyncMessages(self._client) - - @cached_property - def with_raw_response(self) -> AsyncBetaWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return the - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncBetaWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncBetaWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncBetaWithStreamingResponse(self) - - -class BetaWithRawResponse: - def __init__(self, beta: Beta) -> None: - self._beta = beta - - @cached_property - def messages(self) -> MessagesWithRawResponse: - return MessagesWithRawResponse(self._beta.messages) - - -class AsyncBetaWithRawResponse: - def __init__(self, beta: AsyncBeta) -> None: - self._beta = beta - - @cached_property - def messages(self) -> AsyncMessagesWithRawResponse: - return AsyncMessagesWithRawResponse(self._beta.messages) - - -class BetaWithStreamingResponse: - def __init__(self, beta: Beta) -> None: - self._beta = beta - - @cached_property - def messages(self) -> MessagesWithStreamingResponse: - return MessagesWithStreamingResponse(self._beta.messages) - - -class AsyncBetaWithStreamingResponse: - def __init__(self, beta: AsyncBeta) -> None: - self._beta = beta - - @cached_property - def messages(self) -> AsyncMessagesWithStreamingResponse: - return AsyncMessagesWithStreamingResponse(self._beta.messages) diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_beta_messages.py b/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_beta_messages.py deleted file mode 100644 index 72b97b04..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_beta_messages.py +++ /dev/null @@ -1,97 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from ... import _legacy_response -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...resources.beta import Messages as FirstPartyMessagesAPI, AsyncMessages as FirstPartyAsyncMessagesAPI - -__all__ = ["Messages", "AsyncMessages"] - - -class Messages(SyncAPIResource): - create = FirstPartyMessagesAPI.create - stream = FirstPartyMessagesAPI.stream - count_tokens = FirstPartyMessagesAPI.count_tokens - - @cached_property - def with_raw_response(self) -> MessagesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return the - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return MessagesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MessagesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return MessagesWithStreamingResponse(self) - - -class AsyncMessages(AsyncAPIResource): - create = FirstPartyAsyncMessagesAPI.create - stream = FirstPartyAsyncMessagesAPI.stream - count_tokens = FirstPartyAsyncMessagesAPI.count_tokens - - @cached_property - def with_raw_response(self) -> AsyncMessagesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return the - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncMessagesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncMessagesWithStreamingResponse(self) - - -class MessagesWithRawResponse: - def __init__(self, messages: Messages) -> None: - self._messages = messages - - self.create = _legacy_response.to_raw_response_wrapper( - messages.create, - ) - - -class AsyncMessagesWithRawResponse: - def __init__(self, messages: AsyncMessages) -> None: - self._messages = messages - - self.create = _legacy_response.async_to_raw_response_wrapper( - messages.create, - ) - - -class MessagesWithStreamingResponse: - def __init__(self, messages: Messages) -> None: - self._messages = messages - - self.create = to_streamed_response_wrapper( - messages.create, - ) - - -class AsyncMessagesWithStreamingResponse: - def __init__(self, messages: AsyncMessages) -> None: - self._messages = messages - - self.create = async_to_streamed_response_wrapper( - messages.create, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_client.py b/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_client.py deleted file mode 100644 index 8918b759..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/lib/vertex/_client.py +++ /dev/null @@ -1,416 +0,0 @@ -from __future__ import annotations - -import os -from typing import TYPE_CHECKING, Any, Union, Mapping, TypeVar -from typing_extensions import Self, override - -import httpx - -from ... import _exceptions -from ._auth import load_auth, refresh_auth -from ._beta import Beta, AsyncBeta -from ..._types import NOT_GIVEN, NotGiven -from ..._utils import is_dict, asyncify, is_given -from ..._compat import model_copy, typed_cached_property -from ..._models import FinalRequestOptions -from ..._version import __version__ -from ..._streaming import Stream, AsyncStream -from ..._exceptions import AnthropicError, APIStatusError -from ..._base_client import ( - DEFAULT_MAX_RETRIES, - BaseClient, - SyncAPIClient, - AsyncAPIClient, -) -from ...resources.messages import Messages, AsyncMessages - -if TYPE_CHECKING: - from google.auth.credentials import Credentials as GoogleCredentials # type: ignore - - -DEFAULT_VERSION = "vertex-2023-10-16" - -_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) -_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) - - -class BaseVertexClient(BaseClient[_HttpxClientT, _DefaultStreamT]): - @typed_cached_property - def region(self) -> str: - raise RuntimeError("region not set") - - @typed_cached_property - def project_id(self) -> str | None: - project_id = os.environ.get("ANTHROPIC_VERTEX_PROJECT_ID") - if project_id: - return project_id - - return None - - @override - def _make_status_error( - self, - err_msg: str, - *, - body: object, - response: httpx.Response, - ) -> APIStatusError: - if response.status_code == 400: - return _exceptions.BadRequestError(err_msg, response=response, body=body) - - if response.status_code == 401: - return _exceptions.AuthenticationError(err_msg, response=response, body=body) - - if response.status_code == 403: - return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) - - if response.status_code == 404: - return _exceptions.NotFoundError(err_msg, response=response, body=body) - - if response.status_code == 409: - return _exceptions.ConflictError(err_msg, response=response, body=body) - - if response.status_code == 422: - return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) - - if response.status_code == 429: - return _exceptions.RateLimitError(err_msg, response=response, body=body) - - if response.status_code == 503: - return _exceptions.ServiceUnavailableError(err_msg, response=response, body=body) - - if response.status_code == 504: - return _exceptions.DeadlineExceededError(err_msg, response=response, body=body) - - if response.status_code >= 500: - return _exceptions.InternalServerError(err_msg, response=response, body=body) - return APIStatusError(err_msg, response=response, body=body) - - -class AnthropicVertex(BaseVertexClient[httpx.Client, Stream[Any]], SyncAPIClient): - messages: Messages - beta: Beta - - def __init__( - self, - *, - region: str | NotGiven = NOT_GIVEN, - project_id: str | NotGiven = NOT_GIVEN, - access_token: str | None = None, - credentials: GoogleCredentials | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. - http_client: httpx.Client | None = None, - _strict_response_validation: bool = False, - ) -> None: - if not is_given(region): - region = os.environ.get("CLOUD_ML_REGION", NOT_GIVEN) - if not is_given(region): - raise ValueError( - "No region was given. The client should be instantiated with the `region` argument or the `CLOUD_ML_REGION` environment variable should be set." - ) - - if base_url is None: - base_url = os.environ.get("ANTHROPIC_VERTEX_BASE_URL") - if base_url is None: - if region == "global": - base_url = "https://aiplatform.googleapis.com/v1" - elif region == "us": - base_url = "https://aiplatform.us.rep.googleapis.com/v1" - elif region == "eu": - base_url = "https://aiplatform.eu.rep.googleapis.com/v1" - else: - base_url = f"https://{region}-aiplatform.googleapis.com/v1" - - super().__init__( - version=__version__, - base_url=base_url, - timeout=timeout, - max_retries=max_retries, - custom_headers=default_headers, - custom_query=default_query, - http_client=http_client, - _strict_response_validation=_strict_response_validation, - ) - - if is_given(project_id): - self.project_id = project_id - - self.region = region - self.access_token = access_token - self.credentials = credentials - - self.messages = Messages(self) - self.beta = Beta(self) - - @override - def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: - return _prepare_options(options, project_id=self.project_id, region=self.region) - - @override - def _prepare_request(self, request: httpx.Request) -> None: - if request.headers.get("Authorization"): - # already authenticated, nothing for us to do - return - - request.headers["Authorization"] = f"Bearer {self._ensure_access_token()}" - - def _ensure_access_token(self) -> str: - if self.access_token is not None: - return self.access_token - - if not self.credentials: - self.credentials, project_id = load_auth(project_id=self.project_id) - if not self.project_id: - self.project_id = project_id - - if self.credentials.expired or not self.credentials.token: - refresh_auth(self.credentials) - - if not self.credentials.token: - raise RuntimeError("Could not resolve API token from the environment") - - assert isinstance(self.credentials.token, str) - return self.credentials.token - - def copy( - self, - *, - region: str | NotGiven = NOT_GIVEN, - project_id: str | NotGiven = NOT_GIVEN, - access_token: str | None = None, - credentials: GoogleCredentials | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - """ - Create a new client instance re-using the same options given to the current client with optional overriding. - """ - if default_headers is not None and set_default_headers is not None: - raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") - - if default_query is not None and set_default_query is not None: - raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - - headers = self._custom_headers - if default_headers is not None: - headers = {**headers, **default_headers} - elif set_default_headers is not None: - headers = set_default_headers - - params = self._custom_query - if default_query is not None: - params = {**params, **default_query} - elif set_default_query is not None: - params = set_default_query - - http_client = http_client or self._client - - return self.__class__( - region=region if is_given(region) else self.region, - project_id=project_id if is_given(project_id) else self.project_id or NOT_GIVEN, - access_token=access_token or self.access_token, - credentials=credentials or self.credentials, - base_url=base_url or self.base_url, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - **_extra_kwargs, - ) - - # Alias for `copy` for nicer inline usage, e.g. - # client.with_options(timeout=10).foo.create(...) - with_options = copy - - -class AsyncAnthropicVertex(BaseVertexClient[httpx.AsyncClient, AsyncStream[Any]], AsyncAPIClient): - messages: AsyncMessages - beta: AsyncBeta - - def __init__( - self, - *, - region: str | NotGiven = NOT_GIVEN, - project_id: str | NotGiven = NOT_GIVEN, - access_token: str | None = None, - credentials: GoogleCredentials | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - max_retries: int = DEFAULT_MAX_RETRIES, - default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. - http_client: httpx.AsyncClient | None = None, - _strict_response_validation: bool = False, - ) -> None: - if not is_given(region): - region = os.environ.get("CLOUD_ML_REGION", NOT_GIVEN) - if not is_given(region): - raise ValueError( - "No region was given. The client should be instantiated with the `region` argument or the `CLOUD_ML_REGION` environment variable should be set." - ) - - if base_url is None: - base_url = os.environ.get("ANTHROPIC_VERTEX_BASE_URL") - if base_url is None: - if region == "global": - base_url = "https://aiplatform.googleapis.com/v1" - else: - base_url = f"https://{region}-aiplatform.googleapis.com/v1" - - super().__init__( - version=__version__, - base_url=base_url, - timeout=timeout, - max_retries=max_retries, - custom_headers=default_headers, - custom_query=default_query, - http_client=http_client, - _strict_response_validation=_strict_response_validation, - ) - - if is_given(project_id): - self.project_id = project_id - - self.region = region - self.access_token = access_token - self.credentials = credentials - - self.messages = AsyncMessages(self) - self.beta = AsyncBeta(self) - - @override - async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: - return _prepare_options(options, project_id=self.project_id, region=self.region) - - @override - async def _prepare_request(self, request: httpx.Request) -> None: - if request.headers.get("Authorization"): - # already authenticated, nothing for us to do - return - - request.headers["Authorization"] = f"Bearer {await self._ensure_access_token()}" - - async def _ensure_access_token(self) -> str: - if self.access_token is not None: - return self.access_token - - if not self.credentials: - self.credentials, project_id = await asyncify(load_auth)(project_id=self.project_id) - if not self.project_id: - self.project_id = project_id - - if self.credentials.expired or not self.credentials.token: - await asyncify(refresh_auth)(self.credentials) - - if not self.credentials.token: - raise RuntimeError("Could not resolve API token from the environment") - - assert isinstance(self.credentials.token, str) - return self.credentials.token - - def copy( - self, - *, - region: str | NotGiven = NOT_GIVEN, - project_id: str | NotGiven = NOT_GIVEN, - access_token: str | None = None, - credentials: GoogleCredentials | None = None, - base_url: str | httpx.URL | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, - default_headers: Mapping[str, str] | None = None, - set_default_headers: Mapping[str, str] | None = None, - default_query: Mapping[str, object] | None = None, - set_default_query: Mapping[str, object] | None = None, - _extra_kwargs: Mapping[str, Any] = {}, - ) -> Self: - """ - Create a new client instance re-using the same options given to the current client with optional overriding. - """ - if default_headers is not None and set_default_headers is not None: - raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") - - if default_query is not None and set_default_query is not None: - raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") - - headers = self._custom_headers - if default_headers is not None: - headers = {**headers, **default_headers} - elif set_default_headers is not None: - headers = set_default_headers - - params = self._custom_query - if default_query is not None: - params = {**params, **default_query} - elif set_default_query is not None: - params = set_default_query - - http_client = http_client or self._client - - return self.__class__( - region=region if is_given(region) else self.region, - project_id=project_id if is_given(project_id) else self.project_id or NOT_GIVEN, - access_token=access_token or self.access_token, - credentials=credentials or self.credentials, - base_url=base_url or self.base_url, - timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, - http_client=http_client, - max_retries=max_retries if is_given(max_retries) else self.max_retries, - default_headers=headers, - default_query=params, - **_extra_kwargs, - ) - - # Alias for `copy` for nicer inline usage, e.g. - # client.with_options(timeout=10).foo.create(...) - with_options = copy - - -def _prepare_options(input_options: FinalRequestOptions, *, project_id: str | None, region: str) -> FinalRequestOptions: - options = model_copy(input_options, deep=True) - - if is_dict(options.json_data): - options.json_data.setdefault("anthropic_version", DEFAULT_VERSION) - - if options.url in {"/v1/messages", "/v1/messages?beta=true"} and options.method == "post": - if project_id is None: - raise RuntimeError( - "No project_id was given and it could not be resolved from credentials. The client should be instantiated with the `project_id` argument or the `ANTHROPIC_VERTEX_PROJECT_ID` environment variable should be set." - ) - - if not is_dict(options.json_data): - raise RuntimeError("Expected json data to be a dictionary for post /v1/messages") - - model = options.json_data.pop("model") - stream = options.json_data.get("stream", False) - specifier = "streamRawPredict" if stream else "rawPredict" - - options.url = f"/projects/{project_id}/locations/{region}/publishers/anthropic/models/{model}:{specifier}" - - if options.url in {"/v1/messages/count_tokens", "/v1/messages/count_tokens?beta=true"} and options.method == "post": - if project_id is None: - raise RuntimeError( - "No project_id was given and it could not be resolved from credentials. The client should be instantiated with the `project_id` argument or the `ANTHROPIC_VERTEX_PROJECT_ID` environment variable should be set." - ) - - options.url = f"/projects/{project_id}/locations/{region}/publishers/anthropic/models/count-tokens:rawPredict" - - if options.url.startswith("/v1/messages/batches"): - raise AnthropicError("The Batch API is not supported in the Vertex client yet") - - return options diff --git a/.venv/lib/python3.12/site-packages/anthropic/pagination.py b/.venv/lib/python3.12/site-packages/anthropic/pagination.py deleted file mode 100644 index 470356b4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/pagination.py +++ /dev/null @@ -1,182 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Generic, TypeVar, Optional -from typing_extensions import override - -from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage - -__all__ = ["SyncPage", "AsyncPage", "SyncTokenPage", "AsyncTokenPage", "SyncPageCursor", "AsyncPageCursor"] - -_T = TypeVar("_T") - - -class SyncPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): - data: List[_T] - has_more: Optional[bool] = None - first_id: Optional[str] = None - last_id: Optional[str] = None - - @override - def _get_page_items(self) -> List[_T]: - data = self.data - if not data: - return [] - return data - - @override - def has_next_page(self) -> bool: - has_more = self.has_more - if has_more is not None and has_more is False: - return False - - return super().has_next_page() - - @override - def next_page_info(self) -> Optional[PageInfo]: - if self._options.params.get("before_id"): - first_id = self.first_id - if not first_id: - return None - - return PageInfo(params={"before_id": first_id}) - - last_id = self.last_id - if not last_id: - return None - - return PageInfo(params={"after_id": last_id}) - - -class AsyncPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): - data: List[_T] - has_more: Optional[bool] = None - first_id: Optional[str] = None - last_id: Optional[str] = None - - @override - def _get_page_items(self) -> List[_T]: - data = self.data - if not data: - return [] - return data - - @override - def has_next_page(self) -> bool: - has_more = self.has_more - if has_more is not None and has_more is False: - return False - - return super().has_next_page() - - @override - def next_page_info(self) -> Optional[PageInfo]: - if self._options.params.get("before_id"): - first_id = self.first_id - if not first_id: - return None - - return PageInfo(params={"before_id": first_id}) - - last_id = self.last_id - if not last_id: - return None - - return PageInfo(params={"after_id": last_id}) - - -class SyncTokenPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): - data: List[_T] - has_more: Optional[bool] = None - next_page: Optional[str] = None - - @override - def _get_page_items(self) -> List[_T]: - data = self.data - if not data: - return [] - return data - - @override - def has_next_page(self) -> bool: - has_more = self.has_more - if has_more is not None and has_more is False: - return False - - return super().has_next_page() - - @override - def next_page_info(self) -> Optional[PageInfo]: - next_page = self.next_page - if not next_page: - return None - - return PageInfo(params={"page_token": next_page}) - - -class AsyncTokenPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): - data: List[_T] - has_more: Optional[bool] = None - next_page: Optional[str] = None - - @override - def _get_page_items(self) -> List[_T]: - data = self.data - if not data: - return [] - return data - - @override - def has_next_page(self) -> bool: - has_more = self.has_more - if has_more is not None and has_more is False: - return False - - return super().has_next_page() - - @override - def next_page_info(self) -> Optional[PageInfo]: - next_page = self.next_page - if not next_page: - return None - - return PageInfo(params={"page_token": next_page}) - - -class SyncPageCursor(BaseSyncPage[_T], BasePage[_T], Generic[_T]): - data: List[_T] - next_page: Optional[str] = None - - @override - def _get_page_items(self) -> List[_T]: - data = self.data - if not data: - return [] - return data - - @override - def next_page_info(self) -> Optional[PageInfo]: - next_page = self.next_page - if not next_page: - return None - - return PageInfo(params={"page": next_page}) - - -class AsyncPageCursor(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): - data: List[_T] - next_page: Optional[str] = None - - @override - def _get_page_items(self) -> List[_T]: - data = self.data - if not data: - return [] - return data - - @override - def next_page_info(self) -> Optional[PageInfo]: - next_page = self.next_page - if not next_page: - return None - - return PageInfo(params={"page": next_page}) diff --git a/.venv/lib/python3.12/site-packages/anthropic/py.typed b/.venv/lib/python3.12/site-packages/anthropic/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/resources/__init__.py deleted file mode 100644 index ffff8855..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/__init__.py +++ /dev/null @@ -1,61 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .beta import ( - Beta, - AsyncBeta, - BetaWithRawResponse, - AsyncBetaWithRawResponse, - BetaWithStreamingResponse, - AsyncBetaWithStreamingResponse, -) -from .models import ( - Models, - AsyncModels, - ModelsWithRawResponse, - AsyncModelsWithRawResponse, - ModelsWithStreamingResponse, - AsyncModelsWithStreamingResponse, -) -from .messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, - MessagesWithStreamingResponse, - AsyncMessagesWithStreamingResponse, -) -from .completions import ( - Completions, - AsyncCompletions, - CompletionsWithRawResponse, - AsyncCompletionsWithRawResponse, - CompletionsWithStreamingResponse, - AsyncCompletionsWithStreamingResponse, -) - -__all__ = [ - "Completions", - "AsyncCompletions", - "CompletionsWithRawResponse", - "AsyncCompletionsWithRawResponse", - "CompletionsWithStreamingResponse", - "AsyncCompletionsWithStreamingResponse", - "Messages", - "AsyncMessages", - "MessagesWithRawResponse", - "AsyncMessagesWithRawResponse", - "MessagesWithStreamingResponse", - "AsyncMessagesWithStreamingResponse", - "Models", - "AsyncModels", - "ModelsWithRawResponse", - "AsyncModelsWithRawResponse", - "ModelsWithStreamingResponse", - "AsyncModelsWithStreamingResponse", - "Beta", - "AsyncBeta", - "BetaWithRawResponse", - "AsyncBetaWithRawResponse", - "BetaWithStreamingResponse", - "AsyncBetaWithStreamingResponse", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/__init__.py deleted file mode 100644 index 9c74f2e5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/__init__.py +++ /dev/null @@ -1,159 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .beta import ( - Beta, - AsyncBeta, - BetaWithRawResponse, - AsyncBetaWithRawResponse, - BetaWithStreamingResponse, - AsyncBetaWithStreamingResponse, -) -from .files import ( - Files, - AsyncFiles, - FilesWithRawResponse, - AsyncFilesWithRawResponse, - FilesWithStreamingResponse, - AsyncFilesWithStreamingResponse, -) -from .agents import ( - Agents, - AsyncAgents, - AgentsWithRawResponse, - AsyncAgentsWithRawResponse, - AgentsWithStreamingResponse, - AsyncAgentsWithStreamingResponse, -) -from .models import ( - Models, - AsyncModels, - ModelsWithRawResponse, - AsyncModelsWithRawResponse, - ModelsWithStreamingResponse, - AsyncModelsWithStreamingResponse, -) -from .skills import ( - Skills, - AsyncSkills, - SkillsWithRawResponse, - AsyncSkillsWithRawResponse, - SkillsWithStreamingResponse, - AsyncSkillsWithStreamingResponse, -) -from .vaults import ( - Vaults, - AsyncVaults, - VaultsWithRawResponse, - AsyncVaultsWithRawResponse, - VaultsWithStreamingResponse, - AsyncVaultsWithStreamingResponse, -) -from .messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, - MessagesWithStreamingResponse, - AsyncMessagesWithStreamingResponse, -) -from .sessions import ( - Sessions, - AsyncSessions, - SessionsWithRawResponse, - AsyncSessionsWithRawResponse, - SessionsWithStreamingResponse, - AsyncSessionsWithStreamingResponse, -) -from .environments import ( - Environments, - AsyncEnvironments, - EnvironmentsWithRawResponse, - AsyncEnvironmentsWithRawResponse, - EnvironmentsWithStreamingResponse, - AsyncEnvironmentsWithStreamingResponse, -) -from .memory_stores import ( - MemoryStores, - AsyncMemoryStores, - MemoryStoresWithRawResponse, - AsyncMemoryStoresWithRawResponse, - MemoryStoresWithStreamingResponse, - AsyncMemoryStoresWithStreamingResponse, -) -from .user_profiles import ( - UserProfiles, - AsyncUserProfiles, - UserProfilesWithRawResponse, - AsyncUserProfilesWithRawResponse, - UserProfilesWithStreamingResponse, - AsyncUserProfilesWithStreamingResponse, -) - -__all__ = [ - "Models", - "AsyncModels", - "ModelsWithRawResponse", - "AsyncModelsWithRawResponse", - "ModelsWithStreamingResponse", - "AsyncModelsWithStreamingResponse", - "Messages", - "AsyncMessages", - "MessagesWithRawResponse", - "AsyncMessagesWithRawResponse", - "MessagesWithStreamingResponse", - "AsyncMessagesWithStreamingResponse", - "Agents", - "AsyncAgents", - "AgentsWithRawResponse", - "AsyncAgentsWithRawResponse", - "AgentsWithStreamingResponse", - "AsyncAgentsWithStreamingResponse", - "Environments", - "AsyncEnvironments", - "EnvironmentsWithRawResponse", - "AsyncEnvironmentsWithRawResponse", - "EnvironmentsWithStreamingResponse", - "AsyncEnvironmentsWithStreamingResponse", - "Sessions", - "AsyncSessions", - "SessionsWithRawResponse", - "AsyncSessionsWithRawResponse", - "SessionsWithStreamingResponse", - "AsyncSessionsWithStreamingResponse", - "Vaults", - "AsyncVaults", - "VaultsWithRawResponse", - "AsyncVaultsWithRawResponse", - "VaultsWithStreamingResponse", - "AsyncVaultsWithStreamingResponse", - "MemoryStores", - "AsyncMemoryStores", - "MemoryStoresWithRawResponse", - "AsyncMemoryStoresWithRawResponse", - "MemoryStoresWithStreamingResponse", - "AsyncMemoryStoresWithStreamingResponse", - "Files", - "AsyncFiles", - "FilesWithRawResponse", - "AsyncFilesWithRawResponse", - "FilesWithStreamingResponse", - "AsyncFilesWithStreamingResponse", - "Skills", - "AsyncSkills", - "SkillsWithRawResponse", - "AsyncSkillsWithRawResponse", - "SkillsWithStreamingResponse", - "AsyncSkillsWithStreamingResponse", - "UserProfiles", - "AsyncUserProfiles", - "UserProfilesWithRawResponse", - "AsyncUserProfilesWithRawResponse", - "UserProfilesWithStreamingResponse", - "AsyncUserProfilesWithStreamingResponse", - "Beta", - "AsyncBeta", - "BetaWithRawResponse", - "AsyncBetaWithRawResponse", - "BetaWithStreamingResponse", - "AsyncBetaWithStreamingResponse", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/agents/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/agents/__init__.py deleted file mode 100644 index 6d56845b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/agents/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .agents import ( - Agents, - AsyncAgents, - AgentsWithRawResponse, - AsyncAgentsWithRawResponse, - AgentsWithStreamingResponse, - AsyncAgentsWithStreamingResponse, -) -from .versions import ( - Versions, - AsyncVersions, - VersionsWithRawResponse, - AsyncVersionsWithRawResponse, - VersionsWithStreamingResponse, - AsyncVersionsWithStreamingResponse, -) - -__all__ = [ - "Versions", - "AsyncVersions", - "VersionsWithRawResponse", - "AsyncVersionsWithRawResponse", - "VersionsWithStreamingResponse", - "AsyncVersionsWithStreamingResponse", - "Agents", - "AsyncAgents", - "AgentsWithRawResponse", - "AsyncAgentsWithRawResponse", - "AgentsWithStreamingResponse", - "AsyncAgentsWithStreamingResponse", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/agents/agents.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/agents/agents.py deleted file mode 100644 index 7ee44853..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/agents/agents.py +++ /dev/null @@ -1,913 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Union, Iterable, Optional -from datetime import datetime -from itertools import chain - -import httpx - -from .... import _legacy_response -from .versions import ( - Versions, - AsyncVersions, - VersionsWithRawResponse, - AsyncVersionsWithRawResponse, - VersionsWithStreamingResponse, - AsyncVersionsWithStreamingResponse, -) -from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ....types.beta import agent_list_params, agent_create_params, agent_update_params, agent_retrieve_params -from ...._base_client import AsyncPaginator, make_request_options -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.beta_managed_agents_agent import BetaManagedAgentsAgent -from ....types.beta.beta_managed_agents_skill_params import BetaManagedAgentsSkillParams -from ....types.beta.beta_managed_agents_url_mcp_server_params import BetaManagedAgentsURLMCPServerParams - -__all__ = ["Agents", "AsyncAgents"] - - -class Agents(SyncAPIResource): - @cached_property - def versions(self) -> Versions: - return Versions(self._client) - - @cached_property - def with_raw_response(self) -> AgentsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AgentsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AgentsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AgentsWithStreamingResponse(self) - - def create( - self, - *, - model: agent_create_params.Model, - name: str, - description: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaManagedAgentsURLMCPServerParams] | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - skills: Iterable[BetaManagedAgentsSkillParams] | Omit = omit, - system: Optional[str] | Omit = omit, - tools: Iterable[agent_create_params.Tool] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsAgent: - """Create Agent - - Args: - model: Model identifier. - - Accepts the - [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), - e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration - control - - name: Human-readable name for the agent. 1-256 characters. - - description: Description of what the agent does. Up to 2048 characters. - - mcp_servers: MCP servers this agent connects to. Maximum 20. Names must be unique within the - array. - - metadata: Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up - to 512 chars. - - skills: Skills available to the agent. Maximum 20. - - system: System prompt for the agent. Up to 100,000 characters. - - tools: Tool configurations available to the agent. Maximum of 128 tools across all - toolsets allowed. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - "/v1/agents?beta=true", - body=maybe_transform( - { - "model": model, - "name": name, - "description": description, - "mcp_servers": mcp_servers, - "metadata": metadata, - "skills": skills, - "system": system, - "tools": tools, - }, - agent_create_params.AgentCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsAgent, - ) - - def retrieve( - self, - agent_id: str, - *, - version: int | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsAgent: - """Get Agent - - Args: - version: Agent version. - - Omit for the most recent version. Must be at least 1 if - specified. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not agent_id: - raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get( - path_template("/v1/agents/{agent_id}?beta=true", agent_id=agent_id), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"version": version}, agent_retrieve_params.AgentRetrieveParams), - ), - cast_to=BetaManagedAgentsAgent, - ) - - def update( - self, - agent_id: str, - *, - version: int, - description: Optional[str] | Omit = omit, - mcp_servers: Optional[Iterable[BetaManagedAgentsURLMCPServerParams]] | Omit = omit, - metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, - model: agent_update_params.Model | Omit = omit, - name: str | Omit = omit, - skills: Optional[Iterable[BetaManagedAgentsSkillParams]] | Omit = omit, - system: Optional[str] | Omit = omit, - tools: Optional[Iterable[agent_update_params.Tool]] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsAgent: - """ - Update Agent - - Args: - version: The agent's current version, used to prevent concurrent overwrites. Obtain this - value from a create or retrieve response. The request fails if this does not - match the server's current version. - - description: Description. Up to 2048 characters. Omit to preserve; send empty string or null - to clear. - - mcp_servers: MCP servers. Full replacement. Omit to preserve; send empty array or null to - clear. Names must be unique. Maximum 20. - - metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. - Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars - each) with values up to 512 chars. - - model: Model identifier. Accepts the - [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), - e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration - control. Omit to preserve. Cannot be cleared. - - name: Human-readable name. 1-256 characters. Omit to preserve. Cannot be cleared. - - skills: Skills. Full replacement. Omit to preserve; send empty array or null to clear. - Maximum 20. - - system: System prompt. Up to 100,000 characters. Omit to preserve; send empty string or - null to clear. - - tools: Tool configurations available to the agent. Full replacement. Omit to preserve; - send empty array or null to clear. Maximum of 128 tools across all toolsets - allowed. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not agent_id: - raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/agents/{agent_id}?beta=true", agent_id=agent_id), - body=maybe_transform( - { - "version": version, - "description": description, - "mcp_servers": mcp_servers, - "metadata": metadata, - "model": model, - "name": name, - "skills": skills, - "system": system, - "tools": tools, - }, - agent_update_params.AgentUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsAgent, - ) - - def list( - self, - *, - created_at_gte: Union[str, datetime] | Omit = omit, - created_at_lte: Union[str, datetime] | Omit = omit, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaManagedAgentsAgent]: - """ - List Agents - - Args: - created_at_gte: Return agents created at or after this time (inclusive). - - created_at_lte: Return agents created at or before this time (inclusive). - - include_archived: Include archived agents in results. Defaults to false. - - limit: Maximum results per page. Default 20, maximum 100. - - page: Opaque pagination cursor from a previous response. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - "/v1/agents?beta=true", - page=SyncPageCursor[BetaManagedAgentsAgent], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "created_at_gte": created_at_gte, - "created_at_lte": created_at_lte, - "include_archived": include_archived, - "limit": limit, - "page": page, - }, - agent_list_params.AgentListParams, - ), - ), - model=BetaManagedAgentsAgent, - ) - - def archive( - self, - agent_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsAgent: - """ - Archive Agent - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not agent_id: - raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/agents/{agent_id}/archive?beta=true", agent_id=agent_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsAgent, - ) - - -class AsyncAgents(AsyncAPIResource): - @cached_property - def versions(self) -> AsyncVersions: - return AsyncVersions(self._client) - - @cached_property - def with_raw_response(self) -> AsyncAgentsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncAgentsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncAgentsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncAgentsWithStreamingResponse(self) - - async def create( - self, - *, - model: agent_create_params.Model, - name: str, - description: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaManagedAgentsURLMCPServerParams] | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - skills: Iterable[BetaManagedAgentsSkillParams] | Omit = omit, - system: Optional[str] | Omit = omit, - tools: Iterable[agent_create_params.Tool] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsAgent: - """Create Agent - - Args: - model: Model identifier. - - Accepts the - [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), - e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration - control - - name: Human-readable name for the agent. 1-256 characters. - - description: Description of what the agent does. Up to 2048 characters. - - mcp_servers: MCP servers this agent connects to. Maximum 20. Names must be unique within the - array. - - metadata: Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up - to 512 chars. - - skills: Skills available to the agent. Maximum 20. - - system: System prompt for the agent. Up to 100,000 characters. - - tools: Tool configurations available to the agent. Maximum of 128 tools across all - toolsets allowed. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - "/v1/agents?beta=true", - body=await async_maybe_transform( - { - "model": model, - "name": name, - "description": description, - "mcp_servers": mcp_servers, - "metadata": metadata, - "skills": skills, - "system": system, - "tools": tools, - }, - agent_create_params.AgentCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsAgent, - ) - - async def retrieve( - self, - agent_id: str, - *, - version: int | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsAgent: - """Get Agent - - Args: - version: Agent version. - - Omit for the most recent version. Must be at least 1 if - specified. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not agent_id: - raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._get( - path_template("/v1/agents/{agent_id}?beta=true", agent_id=agent_id), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"version": version}, agent_retrieve_params.AgentRetrieveParams), - ), - cast_to=BetaManagedAgentsAgent, - ) - - async def update( - self, - agent_id: str, - *, - version: int, - description: Optional[str] | Omit = omit, - mcp_servers: Optional[Iterable[BetaManagedAgentsURLMCPServerParams]] | Omit = omit, - metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, - model: agent_update_params.Model | Omit = omit, - name: str | Omit = omit, - skills: Optional[Iterable[BetaManagedAgentsSkillParams]] | Omit = omit, - system: Optional[str] | Omit = omit, - tools: Optional[Iterable[agent_update_params.Tool]] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsAgent: - """ - Update Agent - - Args: - version: The agent's current version, used to prevent concurrent overwrites. Obtain this - value from a create or retrieve response. The request fails if this does not - match the server's current version. - - description: Description. Up to 2048 characters. Omit to preserve; send empty string or null - to clear. - - mcp_servers: MCP servers. Full replacement. Omit to preserve; send empty array or null to - clear. Names must be unique. Maximum 20. - - metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. - Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars - each) with values up to 512 chars. - - model: Model identifier. Accepts the - [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), - e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration - control. Omit to preserve. Cannot be cleared. - - name: Human-readable name. 1-256 characters. Omit to preserve. Cannot be cleared. - - skills: Skills. Full replacement. Omit to preserve; send empty array or null to clear. - Maximum 20. - - system: System prompt. Up to 100,000 characters. Omit to preserve; send empty string or - null to clear. - - tools: Tool configurations available to the agent. Full replacement. Omit to preserve; - send empty array or null to clear. Maximum of 128 tools across all toolsets - allowed. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not agent_id: - raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/agents/{agent_id}?beta=true", agent_id=agent_id), - body=await async_maybe_transform( - { - "version": version, - "description": description, - "mcp_servers": mcp_servers, - "metadata": metadata, - "model": model, - "name": name, - "skills": skills, - "system": system, - "tools": tools, - }, - agent_update_params.AgentUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsAgent, - ) - - def list( - self, - *, - created_at_gte: Union[str, datetime] | Omit = omit, - created_at_lte: Union[str, datetime] | Omit = omit, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaManagedAgentsAgent, AsyncPageCursor[BetaManagedAgentsAgent]]: - """ - List Agents - - Args: - created_at_gte: Return agents created at or after this time (inclusive). - - created_at_lte: Return agents created at or before this time (inclusive). - - include_archived: Include archived agents in results. Defaults to false. - - limit: Maximum results per page. Default 20, maximum 100. - - page: Opaque pagination cursor from a previous response. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - "/v1/agents?beta=true", - page=AsyncPageCursor[BetaManagedAgentsAgent], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "created_at_gte": created_at_gte, - "created_at_lte": created_at_lte, - "include_archived": include_archived, - "limit": limit, - "page": page, - }, - agent_list_params.AgentListParams, - ), - ), - model=BetaManagedAgentsAgent, - ) - - async def archive( - self, - agent_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsAgent: - """ - Archive Agent - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not agent_id: - raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/agents/{agent_id}/archive?beta=true", agent_id=agent_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsAgent, - ) - - -class AgentsWithRawResponse: - def __init__(self, agents: Agents) -> None: - self._agents = agents - - self.create = _legacy_response.to_raw_response_wrapper( - agents.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - agents.retrieve, - ) - self.update = _legacy_response.to_raw_response_wrapper( - agents.update, - ) - self.list = _legacy_response.to_raw_response_wrapper( - agents.list, - ) - self.archive = _legacy_response.to_raw_response_wrapper( - agents.archive, - ) - - @cached_property - def versions(self) -> VersionsWithRawResponse: - return VersionsWithRawResponse(self._agents.versions) - - -class AsyncAgentsWithRawResponse: - def __init__(self, agents: AsyncAgents) -> None: - self._agents = agents - - self.create = _legacy_response.async_to_raw_response_wrapper( - agents.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - agents.retrieve, - ) - self.update = _legacy_response.async_to_raw_response_wrapper( - agents.update, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - agents.list, - ) - self.archive = _legacy_response.async_to_raw_response_wrapper( - agents.archive, - ) - - @cached_property - def versions(self) -> AsyncVersionsWithRawResponse: - return AsyncVersionsWithRawResponse(self._agents.versions) - - -class AgentsWithStreamingResponse: - def __init__(self, agents: Agents) -> None: - self._agents = agents - - self.create = to_streamed_response_wrapper( - agents.create, - ) - self.retrieve = to_streamed_response_wrapper( - agents.retrieve, - ) - self.update = to_streamed_response_wrapper( - agents.update, - ) - self.list = to_streamed_response_wrapper( - agents.list, - ) - self.archive = to_streamed_response_wrapper( - agents.archive, - ) - - @cached_property - def versions(self) -> VersionsWithStreamingResponse: - return VersionsWithStreamingResponse(self._agents.versions) - - -class AsyncAgentsWithStreamingResponse: - def __init__(self, agents: AsyncAgents) -> None: - self._agents = agents - - self.create = async_to_streamed_response_wrapper( - agents.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - agents.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - agents.update, - ) - self.list = async_to_streamed_response_wrapper( - agents.list, - ) - self.archive = async_to_streamed_response_wrapper( - agents.archive, - ) - - @cached_property - def versions(self) -> AsyncVersionsWithStreamingResponse: - return AsyncVersionsWithStreamingResponse(self._agents.versions) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/agents/versions.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/agents/versions.py deleted file mode 100644 index a1f883f9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/agents/versions.py +++ /dev/null @@ -1,230 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from itertools import chain - -import httpx - -from .... import _legacy_response -from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ...._base_client import AsyncPaginator, make_request_options -from ....types.beta.agents import version_list_params -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.beta_managed_agents_agent import BetaManagedAgentsAgent - -__all__ = ["Versions", "AsyncVersions"] - - -class Versions(SyncAPIResource): - @cached_property - def with_raw_response(self) -> VersionsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return VersionsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> VersionsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return VersionsWithStreamingResponse(self) - - def list( - self, - agent_id: str, - *, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaManagedAgentsAgent]: - """List Agent Versions - - Args: - limit: Maximum results per page. - - Default 20, maximum 100. - - page: Opaque pagination cursor. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not agent_id: - raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/agents/{agent_id}/versions?beta=true", agent_id=agent_id), - page=SyncPageCursor[BetaManagedAgentsAgent], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "page": page, - }, - version_list_params.VersionListParams, - ), - ), - model=BetaManagedAgentsAgent, - ) - - -class AsyncVersions(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncVersionsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncVersionsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncVersionsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncVersionsWithStreamingResponse(self) - - def list( - self, - agent_id: str, - *, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaManagedAgentsAgent, AsyncPageCursor[BetaManagedAgentsAgent]]: - """List Agent Versions - - Args: - limit: Maximum results per page. - - Default 20, maximum 100. - - page: Opaque pagination cursor. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not agent_id: - raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/agents/{agent_id}/versions?beta=true", agent_id=agent_id), - page=AsyncPageCursor[BetaManagedAgentsAgent], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "page": page, - }, - version_list_params.VersionListParams, - ), - ), - model=BetaManagedAgentsAgent, - ) - - -class VersionsWithRawResponse: - def __init__(self, versions: Versions) -> None: - self._versions = versions - - self.list = _legacy_response.to_raw_response_wrapper( - versions.list, - ) - - -class AsyncVersionsWithRawResponse: - def __init__(self, versions: AsyncVersions) -> None: - self._versions = versions - - self.list = _legacy_response.async_to_raw_response_wrapper( - versions.list, - ) - - -class VersionsWithStreamingResponse: - def __init__(self, versions: Versions) -> None: - self._versions = versions - - self.list = to_streamed_response_wrapper( - versions.list, - ) - - -class AsyncVersionsWithStreamingResponse: - def __init__(self, versions: AsyncVersions) -> None: - self._versions = versions - - self.list = async_to_streamed_response_wrapper( - versions.list, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/beta.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/beta.py deleted file mode 100644 index a0ef6387..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/beta.py +++ /dev/null @@ -1,390 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .files import ( - Files, - AsyncFiles, - FilesWithRawResponse, - AsyncFilesWithRawResponse, - FilesWithStreamingResponse, - AsyncFilesWithStreamingResponse, -) -from .models import ( - Models, - AsyncModels, - ModelsWithRawResponse, - AsyncModelsWithRawResponse, - ModelsWithStreamingResponse, - AsyncModelsWithStreamingResponse, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from .environments import ( - Environments, - AsyncEnvironments, - EnvironmentsWithRawResponse, - AsyncEnvironmentsWithRawResponse, - EnvironmentsWithStreamingResponse, - AsyncEnvironmentsWithStreamingResponse, -) -from .agents.agents import ( - Agents, - AsyncAgents, - AgentsWithRawResponse, - AsyncAgentsWithRawResponse, - AgentsWithStreamingResponse, - AsyncAgentsWithStreamingResponse, -) -from .skills.skills import ( - Skills, - AsyncSkills, - SkillsWithRawResponse, - AsyncSkillsWithRawResponse, - SkillsWithStreamingResponse, - AsyncSkillsWithStreamingResponse, -) -from .user_profiles import ( - UserProfiles, - AsyncUserProfiles, - UserProfilesWithRawResponse, - AsyncUserProfilesWithRawResponse, - UserProfilesWithStreamingResponse, - AsyncUserProfilesWithStreamingResponse, -) -from .vaults.vaults import ( - Vaults, - AsyncVaults, - VaultsWithRawResponse, - AsyncVaultsWithRawResponse, - VaultsWithStreamingResponse, - AsyncVaultsWithStreamingResponse, -) -from .messages.messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, - MessagesWithStreamingResponse, - AsyncMessagesWithStreamingResponse, -) -from .sessions.sessions import ( - Sessions, - AsyncSessions, - SessionsWithRawResponse, - AsyncSessionsWithRawResponse, - SessionsWithStreamingResponse, - AsyncSessionsWithStreamingResponse, -) -from .memory_stores.memory_stores import ( - MemoryStores, - AsyncMemoryStores, - MemoryStoresWithRawResponse, - AsyncMemoryStoresWithRawResponse, - MemoryStoresWithStreamingResponse, - AsyncMemoryStoresWithStreamingResponse, -) - -__all__ = ["Beta", "AsyncBeta"] - - -class Beta(SyncAPIResource): - @cached_property - def models(self) -> Models: - return Models(self._client) - - @cached_property - def messages(self) -> Messages: - return Messages(self._client) - - @cached_property - def agents(self) -> Agents: - return Agents(self._client) - - @cached_property - def environments(self) -> Environments: - return Environments(self._client) - - @cached_property - def sessions(self) -> Sessions: - return Sessions(self._client) - - @cached_property - def vaults(self) -> Vaults: - return Vaults(self._client) - - @cached_property - def memory_stores(self) -> MemoryStores: - return MemoryStores(self._client) - - @cached_property - def files(self) -> Files: - return Files(self._client) - - @cached_property - def skills(self) -> Skills: - return Skills(self._client) - - @cached_property - def user_profiles(self) -> UserProfiles: - return UserProfiles(self._client) - - @cached_property - def with_raw_response(self) -> BetaWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return BetaWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> BetaWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return BetaWithStreamingResponse(self) - - -class AsyncBeta(AsyncAPIResource): - @cached_property - def models(self) -> AsyncModels: - return AsyncModels(self._client) - - @cached_property - def messages(self) -> AsyncMessages: - return AsyncMessages(self._client) - - @cached_property - def agents(self) -> AsyncAgents: - return AsyncAgents(self._client) - - @cached_property - def environments(self) -> AsyncEnvironments: - return AsyncEnvironments(self._client) - - @cached_property - def sessions(self) -> AsyncSessions: - return AsyncSessions(self._client) - - @cached_property - def vaults(self) -> AsyncVaults: - return AsyncVaults(self._client) - - @cached_property - def memory_stores(self) -> AsyncMemoryStores: - return AsyncMemoryStores(self._client) - - @cached_property - def files(self) -> AsyncFiles: - return AsyncFiles(self._client) - - @cached_property - def skills(self) -> AsyncSkills: - return AsyncSkills(self._client) - - @cached_property - def user_profiles(self) -> AsyncUserProfiles: - return AsyncUserProfiles(self._client) - - @cached_property - def with_raw_response(self) -> AsyncBetaWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncBetaWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncBetaWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncBetaWithStreamingResponse(self) - - -class BetaWithRawResponse: - def __init__(self, beta: Beta) -> None: - self._beta = beta - - @cached_property - def models(self) -> ModelsWithRawResponse: - return ModelsWithRawResponse(self._beta.models) - - @cached_property - def messages(self) -> MessagesWithRawResponse: - return MessagesWithRawResponse(self._beta.messages) - - @cached_property - def agents(self) -> AgentsWithRawResponse: - return AgentsWithRawResponse(self._beta.agents) - - @cached_property - def environments(self) -> EnvironmentsWithRawResponse: - return EnvironmentsWithRawResponse(self._beta.environments) - - @cached_property - def sessions(self) -> SessionsWithRawResponse: - return SessionsWithRawResponse(self._beta.sessions) - - @cached_property - def vaults(self) -> VaultsWithRawResponse: - return VaultsWithRawResponse(self._beta.vaults) - - @cached_property - def memory_stores(self) -> MemoryStoresWithRawResponse: - return MemoryStoresWithRawResponse(self._beta.memory_stores) - - @cached_property - def files(self) -> FilesWithRawResponse: - return FilesWithRawResponse(self._beta.files) - - @cached_property - def skills(self) -> SkillsWithRawResponse: - return SkillsWithRawResponse(self._beta.skills) - - @cached_property - def user_profiles(self) -> UserProfilesWithRawResponse: - return UserProfilesWithRawResponse(self._beta.user_profiles) - - -class AsyncBetaWithRawResponse: - def __init__(self, beta: AsyncBeta) -> None: - self._beta = beta - - @cached_property - def models(self) -> AsyncModelsWithRawResponse: - return AsyncModelsWithRawResponse(self._beta.models) - - @cached_property - def messages(self) -> AsyncMessagesWithRawResponse: - return AsyncMessagesWithRawResponse(self._beta.messages) - - @cached_property - def agents(self) -> AsyncAgentsWithRawResponse: - return AsyncAgentsWithRawResponse(self._beta.agents) - - @cached_property - def environments(self) -> AsyncEnvironmentsWithRawResponse: - return AsyncEnvironmentsWithRawResponse(self._beta.environments) - - @cached_property - def sessions(self) -> AsyncSessionsWithRawResponse: - return AsyncSessionsWithRawResponse(self._beta.sessions) - - @cached_property - def vaults(self) -> AsyncVaultsWithRawResponse: - return AsyncVaultsWithRawResponse(self._beta.vaults) - - @cached_property - def memory_stores(self) -> AsyncMemoryStoresWithRawResponse: - return AsyncMemoryStoresWithRawResponse(self._beta.memory_stores) - - @cached_property - def files(self) -> AsyncFilesWithRawResponse: - return AsyncFilesWithRawResponse(self._beta.files) - - @cached_property - def skills(self) -> AsyncSkillsWithRawResponse: - return AsyncSkillsWithRawResponse(self._beta.skills) - - @cached_property - def user_profiles(self) -> AsyncUserProfilesWithRawResponse: - return AsyncUserProfilesWithRawResponse(self._beta.user_profiles) - - -class BetaWithStreamingResponse: - def __init__(self, beta: Beta) -> None: - self._beta = beta - - @cached_property - def models(self) -> ModelsWithStreamingResponse: - return ModelsWithStreamingResponse(self._beta.models) - - @cached_property - def messages(self) -> MessagesWithStreamingResponse: - return MessagesWithStreamingResponse(self._beta.messages) - - @cached_property - def agents(self) -> AgentsWithStreamingResponse: - return AgentsWithStreamingResponse(self._beta.agents) - - @cached_property - def environments(self) -> EnvironmentsWithStreamingResponse: - return EnvironmentsWithStreamingResponse(self._beta.environments) - - @cached_property - def sessions(self) -> SessionsWithStreamingResponse: - return SessionsWithStreamingResponse(self._beta.sessions) - - @cached_property - def vaults(self) -> VaultsWithStreamingResponse: - return VaultsWithStreamingResponse(self._beta.vaults) - - @cached_property - def memory_stores(self) -> MemoryStoresWithStreamingResponse: - return MemoryStoresWithStreamingResponse(self._beta.memory_stores) - - @cached_property - def files(self) -> FilesWithStreamingResponse: - return FilesWithStreamingResponse(self._beta.files) - - @cached_property - def skills(self) -> SkillsWithStreamingResponse: - return SkillsWithStreamingResponse(self._beta.skills) - - @cached_property - def user_profiles(self) -> UserProfilesWithStreamingResponse: - return UserProfilesWithStreamingResponse(self._beta.user_profiles) - - -class AsyncBetaWithStreamingResponse: - def __init__(self, beta: AsyncBeta) -> None: - self._beta = beta - - @cached_property - def models(self) -> AsyncModelsWithStreamingResponse: - return AsyncModelsWithStreamingResponse(self._beta.models) - - @cached_property - def messages(self) -> AsyncMessagesWithStreamingResponse: - return AsyncMessagesWithStreamingResponse(self._beta.messages) - - @cached_property - def agents(self) -> AsyncAgentsWithStreamingResponse: - return AsyncAgentsWithStreamingResponse(self._beta.agents) - - @cached_property - def environments(self) -> AsyncEnvironmentsWithStreamingResponse: - return AsyncEnvironmentsWithStreamingResponse(self._beta.environments) - - @cached_property - def sessions(self) -> AsyncSessionsWithStreamingResponse: - return AsyncSessionsWithStreamingResponse(self._beta.sessions) - - @cached_property - def vaults(self) -> AsyncVaultsWithStreamingResponse: - return AsyncVaultsWithStreamingResponse(self._beta.vaults) - - @cached_property - def memory_stores(self) -> AsyncMemoryStoresWithStreamingResponse: - return AsyncMemoryStoresWithStreamingResponse(self._beta.memory_stores) - - @cached_property - def files(self) -> AsyncFilesWithStreamingResponse: - return AsyncFilesWithStreamingResponse(self._beta.files) - - @cached_property - def skills(self) -> AsyncSkillsWithStreamingResponse: - return AsyncSkillsWithStreamingResponse(self._beta.skills) - - @cached_property - def user_profiles(self) -> AsyncUserProfilesWithStreamingResponse: - return AsyncUserProfilesWithStreamingResponse(self._beta.user_profiles) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/environments.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/environments.py deleted file mode 100644 index 7064f10a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/environments.py +++ /dev/null @@ -1,863 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from itertools import chain - -import httpx - -from ... import _legacy_response -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...pagination import SyncPageCursor, AsyncPageCursor -from ...types.beta import ( - BetaCloudConfigParams, - environment_list_params, - environment_create_params, - environment_update_params, -) -from ..._base_client import AsyncPaginator, make_request_options -from ...types.anthropic_beta_param import AnthropicBetaParam -from ...types.beta.beta_environment import BetaEnvironment -from ...types.beta.beta_cloud_config_params import BetaCloudConfigParams -from ...types.beta.beta_environment_delete_response import BetaEnvironmentDeleteResponse - -__all__ = ["Environments", "AsyncEnvironments"] - - -class Environments(SyncAPIResource): - @cached_property - def with_raw_response(self) -> EnvironmentsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return EnvironmentsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> EnvironmentsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return EnvironmentsWithStreamingResponse(self) - - def create( - self, - *, - name: str, - config: Optional[BetaCloudConfigParams] | Omit = omit, - description: Optional[str] | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaEnvironment: - """ - Create a new environment with the specified configuration. - - Args: - name: Human-readable name for the environment - - config: Request params for `cloud` environment configuration. - - Fields default to null; on update, omitted fields preserve the existing value. - - description: Optional description of the environment - - metadata: User-provided metadata key-value pairs - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - "/v1/environments?beta=true", - body=maybe_transform( - { - "name": name, - "config": config, - "description": description, - "metadata": metadata, - }, - environment_create_params.EnvironmentCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaEnvironment, - ) - - def retrieve( - self, - environment_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaEnvironment: - """ - Retrieve a specific environment by ID. - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not environment_id: - raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get( - path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaEnvironment, - ) - - def update( - self, - environment_id: str, - *, - config: Optional[BetaCloudConfigParams] | Omit = omit, - description: Optional[str] | Omit = omit, - metadata: Dict[str, Optional[str]] | Omit = omit, - name: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaEnvironment: - """ - Update an existing environment's configuration. - - Args: - config: Request params for `cloud` environment configuration. - - Fields default to null; on update, omitted fields preserve the existing value. - - description: Updated description of the environment - - metadata: User-provided metadata key-value pairs. Set a value to null or empty string to - delete the key. - - name: Updated name for the environment - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not environment_id: - raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), - body=maybe_transform( - { - "config": config, - "description": description, - "metadata": metadata, - "name": name, - }, - environment_update_params.EnvironmentUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaEnvironment, - ) - - def list( - self, - *, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - page: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaEnvironment]: - """ - List environments with pagination support. - - Args: - include_archived: Include archived environments in the response - - limit: Maximum number of environments to return - - page: Opaque cursor from previous response for pagination. Pass the `next_page` value - from the previous response. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - "/v1/environments?beta=true", - page=SyncPageCursor[BetaEnvironment], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "include_archived": include_archived, - "limit": limit, - "page": page, - }, - environment_list_params.EnvironmentListParams, - ), - ), - model=BetaEnvironment, - ) - - def delete( - self, - environment_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaEnvironmentDeleteResponse: - """Delete an environment by ID. - - Returns a confirmation of the deletion. - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not environment_id: - raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._delete( - path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaEnvironmentDeleteResponse, - ) - - def archive( - self, - environment_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaEnvironment: - """Archive an environment by ID. - - Archived environments cannot be used to create new - sessions. - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not environment_id: - raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/environments/{environment_id}/archive?beta=true", environment_id=environment_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaEnvironment, - ) - - -class AsyncEnvironments(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncEnvironmentsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncEnvironmentsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncEnvironmentsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncEnvironmentsWithStreamingResponse(self) - - async def create( - self, - *, - name: str, - config: Optional[BetaCloudConfigParams] | Omit = omit, - description: Optional[str] | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaEnvironment: - """ - Create a new environment with the specified configuration. - - Args: - name: Human-readable name for the environment - - config: Request params for `cloud` environment configuration. - - Fields default to null; on update, omitted fields preserve the existing value. - - description: Optional description of the environment - - metadata: User-provided metadata key-value pairs - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - "/v1/environments?beta=true", - body=await async_maybe_transform( - { - "name": name, - "config": config, - "description": description, - "metadata": metadata, - }, - environment_create_params.EnvironmentCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaEnvironment, - ) - - async def retrieve( - self, - environment_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaEnvironment: - """ - Retrieve a specific environment by ID. - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not environment_id: - raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._get( - path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaEnvironment, - ) - - async def update( - self, - environment_id: str, - *, - config: Optional[BetaCloudConfigParams] | Omit = omit, - description: Optional[str] | Omit = omit, - metadata: Dict[str, Optional[str]] | Omit = omit, - name: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaEnvironment: - """ - Update an existing environment's configuration. - - Args: - config: Request params for `cloud` environment configuration. - - Fields default to null; on update, omitted fields preserve the existing value. - - description: Updated description of the environment - - metadata: User-provided metadata key-value pairs. Set a value to null or empty string to - delete the key. - - name: Updated name for the environment - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not environment_id: - raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), - body=await async_maybe_transform( - { - "config": config, - "description": description, - "metadata": metadata, - "name": name, - }, - environment_update_params.EnvironmentUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaEnvironment, - ) - - def list( - self, - *, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - page: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaEnvironment, AsyncPageCursor[BetaEnvironment]]: - """ - List environments with pagination support. - - Args: - include_archived: Include archived environments in the response - - limit: Maximum number of environments to return - - page: Opaque cursor from previous response for pagination. Pass the `next_page` value - from the previous response. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - "/v1/environments?beta=true", - page=AsyncPageCursor[BetaEnvironment], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "include_archived": include_archived, - "limit": limit, - "page": page, - }, - environment_list_params.EnvironmentListParams, - ), - ), - model=BetaEnvironment, - ) - - async def delete( - self, - environment_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaEnvironmentDeleteResponse: - """Delete an environment by ID. - - Returns a confirmation of the deletion. - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not environment_id: - raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._delete( - path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaEnvironmentDeleteResponse, - ) - - async def archive( - self, - environment_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaEnvironment: - """Archive an environment by ID. - - Archived environments cannot be used to create new - sessions. - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not environment_id: - raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/environments/{environment_id}/archive?beta=true", environment_id=environment_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaEnvironment, - ) - - -class EnvironmentsWithRawResponse: - def __init__(self, environments: Environments) -> None: - self._environments = environments - - self.create = _legacy_response.to_raw_response_wrapper( - environments.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - environments.retrieve, - ) - self.update = _legacy_response.to_raw_response_wrapper( - environments.update, - ) - self.list = _legacy_response.to_raw_response_wrapper( - environments.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - environments.delete, - ) - self.archive = _legacy_response.to_raw_response_wrapper( - environments.archive, - ) - - -class AsyncEnvironmentsWithRawResponse: - def __init__(self, environments: AsyncEnvironments) -> None: - self._environments = environments - - self.create = _legacy_response.async_to_raw_response_wrapper( - environments.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - environments.retrieve, - ) - self.update = _legacy_response.async_to_raw_response_wrapper( - environments.update, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - environments.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - environments.delete, - ) - self.archive = _legacy_response.async_to_raw_response_wrapper( - environments.archive, - ) - - -class EnvironmentsWithStreamingResponse: - def __init__(self, environments: Environments) -> None: - self._environments = environments - - self.create = to_streamed_response_wrapper( - environments.create, - ) - self.retrieve = to_streamed_response_wrapper( - environments.retrieve, - ) - self.update = to_streamed_response_wrapper( - environments.update, - ) - self.list = to_streamed_response_wrapper( - environments.list, - ) - self.delete = to_streamed_response_wrapper( - environments.delete, - ) - self.archive = to_streamed_response_wrapper( - environments.archive, - ) - - -class AsyncEnvironmentsWithStreamingResponse: - def __init__(self, environments: AsyncEnvironments) -> None: - self._environments = environments - - self.create = async_to_streamed_response_wrapper( - environments.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - environments.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - environments.update, - ) - self.list = async_to_streamed_response_wrapper( - environments.list, - ) - self.delete = async_to_streamed_response_wrapper( - environments.delete, - ) - self.archive = async_to_streamed_response_wrapper( - environments.archive, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/files.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/files.py deleted file mode 100644 index 0bc03801..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/files.py +++ /dev/null @@ -1,724 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Mapping, cast -from itertools import chain - -import httpx - -from ... import _legacy_response -from ..._files import deepcopy_with_paths -from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given -from ..._utils import is_given, extract_files, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - BinaryAPIResponse, - AsyncBinaryAPIResponse, - StreamedBinaryAPIResponse, - AsyncStreamedBinaryAPIResponse, - to_streamed_response_wrapper, - to_custom_raw_response_wrapper, - async_to_streamed_response_wrapper, - to_custom_streamed_response_wrapper, - async_to_custom_raw_response_wrapper, - async_to_custom_streamed_response_wrapper, -) -from ...pagination import SyncPage, AsyncPage -from ...types.beta import file_list_params, file_upload_params -from ..._base_client import AsyncPaginator, make_request_options -from ...lib._stainless_helpers import stainless_helper_header_from_file as _stainless_helper_header_from_file -from ...types.beta.deleted_file import DeletedFile -from ...types.beta.file_metadata import FileMetadata -from ...types.anthropic_beta_param import AnthropicBetaParam - -__all__ = ["Files", "AsyncFiles"] - - -class Files(SyncAPIResource): - @cached_property - def with_raw_response(self) -> FilesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return FilesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> FilesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return FilesWithStreamingResponse(self) - - def list( - self, - *, - after_id: str | Omit = omit, - before_id: str | Omit = omit, - limit: int | Omit = omit, - scope_id: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPage[FileMetadata]: - """List Files - - Args: - after_id: ID of the object to use as a cursor for pagination. - - When provided, returns the - page of results immediately after this object. - - before_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately before this object. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - scope_id: Filter by scope ID. Only returns files associated with the specified scope - (e.g., a session ID). - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} - return self._get_api_list( - "/v1/files?beta=true", - page=SyncPage[FileMetadata], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after_id": after_id, - "before_id": before_id, - "limit": limit, - "scope_id": scope_id, - }, - file_list_params.FileListParams, - ), - ), - model=FileMetadata, - ) - - def delete( - self, - file_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DeletedFile: - """ - Delete File - - Args: - file_id: ID of the File. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} - return self._delete( - path_template("/v1/files/{file_id}?beta=true", file_id=file_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=DeletedFile, - ) - - def download( - self, - file_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BinaryAPIResponse: - """ - Download File - - Args: - file_id: ID of the File. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"Accept": "application/binary", **(extra_headers or {})} - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} - return self._get( - path_template("/v1/files/{file_id}/content?beta=true", file_id=file_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BinaryAPIResponse, - ) - - def retrieve_metadata( - self, - file_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> FileMetadata: - """ - Get File Metadata - - Args: - file_id: ID of the File. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} - return self._get( - path_template("/v1/files/{file_id}?beta=true", file_id=file_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=FileMetadata, - ) - - def upload( - self, - *, - file: FileTypes, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> FileMetadata: - """ - Upload File - - Args: - file: The file to upload - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} - extra_headers = {**_stainless_helper_header_from_file(file), **extra_headers} - body = deepcopy_with_paths({"file": file}, [["file"]]) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers["Content-Type"] = "multipart/form-data" - return self._post( - "/v1/files?beta=true", - body=maybe_transform(body, file_upload_params.FileUploadParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=FileMetadata, - ) - - -class AsyncFiles(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncFilesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncFilesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncFilesWithStreamingResponse(self) - - def list( - self, - *, - after_id: str | Omit = omit, - before_id: str | Omit = omit, - limit: int | Omit = omit, - scope_id: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[FileMetadata, AsyncPage[FileMetadata]]: - """List Files - - Args: - after_id: ID of the object to use as a cursor for pagination. - - When provided, returns the - page of results immediately after this object. - - before_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately before this object. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - scope_id: Filter by scope ID. Only returns files associated with the specified scope - (e.g., a session ID). - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} - return self._get_api_list( - "/v1/files?beta=true", - page=AsyncPage[FileMetadata], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after_id": after_id, - "before_id": before_id, - "limit": limit, - "scope_id": scope_id, - }, - file_list_params.FileListParams, - ), - ), - model=FileMetadata, - ) - - async def delete( - self, - file_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DeletedFile: - """ - Delete File - - Args: - file_id: ID of the File. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} - return await self._delete( - path_template("/v1/files/{file_id}?beta=true", file_id=file_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=DeletedFile, - ) - - async def download( - self, - file_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncBinaryAPIResponse: - """ - Download File - - Args: - file_id: ID of the File. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = {"Accept": "application/binary", **(extra_headers or {})} - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} - return await self._get( - path_template("/v1/files/{file_id}/content?beta=true", file_id=file_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AsyncBinaryAPIResponse, - ) - - async def retrieve_metadata( - self, - file_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> FileMetadata: - """ - Get File Metadata - - Args: - file_id: ID of the File. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not file_id: - raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} - return await self._get( - path_template("/v1/files/{file_id}?beta=true", file_id=file_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=FileMetadata, - ) - - async def upload( - self, - *, - file: FileTypes, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> FileMetadata: - """ - Upload File - - Args: - file: The file to upload - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} - extra_headers = {**_stainless_helper_header_from_file(file), **extra_headers} - body = deepcopy_with_paths({"file": file}, [["file"]]) - files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers["Content-Type"] = "multipart/form-data" - return await self._post( - "/v1/files?beta=true", - body=await async_maybe_transform(body, file_upload_params.FileUploadParams), - files=files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=FileMetadata, - ) - - -class FilesWithRawResponse: - def __init__(self, files: Files) -> None: - self._files = files - - self.list = _legacy_response.to_raw_response_wrapper( - files.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - files.delete, - ) - self.download = to_custom_raw_response_wrapper( - files.download, - BinaryAPIResponse, - ) - self.retrieve_metadata = _legacy_response.to_raw_response_wrapper( - files.retrieve_metadata, - ) - self.upload = _legacy_response.to_raw_response_wrapper( - files.upload, - ) - - -class AsyncFilesWithRawResponse: - def __init__(self, files: AsyncFiles) -> None: - self._files = files - - self.list = _legacy_response.async_to_raw_response_wrapper( - files.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - files.delete, - ) - self.download = async_to_custom_raw_response_wrapper( - files.download, - AsyncBinaryAPIResponse, - ) - self.retrieve_metadata = _legacy_response.async_to_raw_response_wrapper( - files.retrieve_metadata, - ) - self.upload = _legacy_response.async_to_raw_response_wrapper( - files.upload, - ) - - -class FilesWithStreamingResponse: - def __init__(self, files: Files) -> None: - self._files = files - - self.list = to_streamed_response_wrapper( - files.list, - ) - self.delete = to_streamed_response_wrapper( - files.delete, - ) - self.download = to_custom_streamed_response_wrapper( - files.download, - StreamedBinaryAPIResponse, - ) - self.retrieve_metadata = to_streamed_response_wrapper( - files.retrieve_metadata, - ) - self.upload = to_streamed_response_wrapper( - files.upload, - ) - - -class AsyncFilesWithStreamingResponse: - def __init__(self, files: AsyncFiles) -> None: - self._files = files - - self.list = async_to_streamed_response_wrapper( - files.list, - ) - self.delete = async_to_streamed_response_wrapper( - files.delete, - ) - self.download = async_to_custom_streamed_response_wrapper( - files.download, - AsyncStreamedBinaryAPIResponse, - ) - self.retrieve_metadata = async_to_streamed_response_wrapper( - files.retrieve_metadata, - ) - self.upload = async_to_streamed_response_wrapper( - files.upload, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/__init__.py deleted file mode 100644 index 497fbfab..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .memories import ( - Memories, - AsyncMemories, - MemoriesWithRawResponse, - AsyncMemoriesWithRawResponse, - MemoriesWithStreamingResponse, - AsyncMemoriesWithStreamingResponse, -) -from .memory_stores import ( - MemoryStores, - AsyncMemoryStores, - MemoryStoresWithRawResponse, - AsyncMemoryStoresWithRawResponse, - MemoryStoresWithStreamingResponse, - AsyncMemoryStoresWithStreamingResponse, -) -from .memory_versions import ( - MemoryVersions, - AsyncMemoryVersions, - MemoryVersionsWithRawResponse, - AsyncMemoryVersionsWithRawResponse, - MemoryVersionsWithStreamingResponse, - AsyncMemoryVersionsWithStreamingResponse, -) - -__all__ = [ - "Memories", - "AsyncMemories", - "MemoriesWithRawResponse", - "AsyncMemoriesWithRawResponse", - "MemoriesWithStreamingResponse", - "AsyncMemoriesWithStreamingResponse", - "MemoryVersions", - "AsyncMemoryVersions", - "MemoryVersionsWithRawResponse", - "AsyncMemoryVersionsWithRawResponse", - "MemoryVersionsWithStreamingResponse", - "AsyncMemoryVersionsWithStreamingResponse", - "MemoryStores", - "AsyncMemoryStores", - "MemoryStoresWithRawResponse", - "AsyncMemoryStoresWithRawResponse", - "MemoryStoresWithStreamingResponse", - "AsyncMemoryStoresWithStreamingResponse", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/memories.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/memories.py deleted file mode 100644 index d9c9a82f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/memories.py +++ /dev/null @@ -1,854 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Any, List, Optional, cast -from itertools import chain -from typing_extensions import Literal - -import httpx - -from .... import _legacy_response -from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ...._base_client import AsyncPaginator, make_request_options -from ....types.beta.memory_stores import ( - BetaManagedAgentsMemoryView, - memory_list_params, - memory_create_params, - memory_delete_params, - memory_update_params, - memory_retrieve_params, -) -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.memory_stores.beta_managed_agents_memory import BetaManagedAgentsMemory -from ....types.beta.memory_stores.beta_managed_agents_memory_view import BetaManagedAgentsMemoryView -from ....types.beta.memory_stores.beta_managed_agents_deleted_memory import BetaManagedAgentsDeletedMemory -from ....types.beta.memory_stores.beta_managed_agents_memory_list_item import BetaManagedAgentsMemoryListItem -from ....types.beta.memory_stores.beta_managed_agents_precondition_param import BetaManagedAgentsPreconditionParam - -__all__ = ["Memories", "AsyncMemories"] - - -class Memories(SyncAPIResource): - @cached_property - def with_raw_response(self) -> MemoriesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return MemoriesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MemoriesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return MemoriesWithStreamingResponse(self) - - def create( - self, - memory_store_id: str, - *, - content: Optional[str], - path: str, - view: BetaManagedAgentsMemoryView | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemory: - """ - CreateMemory - - Args: - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/memory_stores/{memory_store_id}/memories?beta=true", memory_store_id=memory_store_id), - body=maybe_transform( - { - "content": content, - "path": path, - }, - memory_create_params.MemoryCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"view": view}, memory_create_params.MemoryCreateParams), - ), - cast_to=BetaManagedAgentsMemory, - ) - - def retrieve( - self, - memory_id: str, - *, - memory_store_id: str, - view: BetaManagedAgentsMemoryView | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemory: - """ - GetMemory - - Args: - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - if not memory_id: - raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get( - path_template( - "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", - memory_store_id=memory_store_id, - memory_id=memory_id, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"view": view}, memory_retrieve_params.MemoryRetrieveParams), - ), - cast_to=BetaManagedAgentsMemory, - ) - - def update( - self, - memory_id: str, - *, - memory_store_id: str, - view: BetaManagedAgentsMemoryView | Omit = omit, - content: Optional[str] | Omit = omit, - path: Optional[str] | Omit = omit, - precondition: BetaManagedAgentsPreconditionParam | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemory: - """ - UpdateMemory - - Args: - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - if not memory_id: - raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template( - "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", - memory_store_id=memory_store_id, - memory_id=memory_id, - ), - body=maybe_transform( - { - "content": content, - "path": path, - "precondition": precondition, - }, - memory_update_params.MemoryUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"view": view}, memory_update_params.MemoryUpdateParams), - ), - cast_to=BetaManagedAgentsMemory, - ) - - def list( - self, - memory_store_id: str, - *, - depth: int | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - order_by: str | Omit = omit, - page: str | Omit = omit, - path_prefix: str | Omit = omit, - view: BetaManagedAgentsMemoryView | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaManagedAgentsMemoryListItem]: - """ - ListMemories - - Args: - depth: Query parameter for depth - - limit: Query parameter for limit - - order: Query parameter for order - - order_by: Query parameter for order_by - - page: Query parameter for page - - path_prefix: Optional path prefix filter (raw string-prefix match; include a trailing slash - for directory-scoped lists). This value appears in request URLs. Do not include - secrets or personally identifiable information. - - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/memory_stores/{memory_store_id}/memories?beta=true", memory_store_id=memory_store_id), - page=SyncPageCursor[BetaManagedAgentsMemoryListItem], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "depth": depth, - "limit": limit, - "order": order, - "order_by": order_by, - "page": page, - "path_prefix": path_prefix, - "view": view, - }, - memory_list_params.MemoryListParams, - ), - ), - model=cast( - Any, BetaManagedAgentsMemoryListItem - ), # Union types cannot be passed in as arguments in the type system - ) - - def delete( - self, - memory_id: str, - *, - memory_store_id: str, - expected_content_sha256: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeletedMemory: - """ - DeleteMemory - - Args: - expected_content_sha256: Query parameter for expected_content_sha256 - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - if not memory_id: - raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._delete( - path_template( - "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", - memory_store_id=memory_store_id, - memory_id=memory_id, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"expected_content_sha256": expected_content_sha256}, memory_delete_params.MemoryDeleteParams - ), - ), - cast_to=BetaManagedAgentsDeletedMemory, - ) - - -class AsyncMemories(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncMemoriesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncMemoriesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMemoriesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncMemoriesWithStreamingResponse(self) - - async def create( - self, - memory_store_id: str, - *, - content: Optional[str], - path: str, - view: BetaManagedAgentsMemoryView | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemory: - """ - CreateMemory - - Args: - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/memory_stores/{memory_store_id}/memories?beta=true", memory_store_id=memory_store_id), - body=await async_maybe_transform( - { - "content": content, - "path": path, - }, - memory_create_params.MemoryCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"view": view}, memory_create_params.MemoryCreateParams), - ), - cast_to=BetaManagedAgentsMemory, - ) - - async def retrieve( - self, - memory_id: str, - *, - memory_store_id: str, - view: BetaManagedAgentsMemoryView | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemory: - """ - GetMemory - - Args: - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - if not memory_id: - raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._get( - path_template( - "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", - memory_store_id=memory_store_id, - memory_id=memory_id, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"view": view}, memory_retrieve_params.MemoryRetrieveParams), - ), - cast_to=BetaManagedAgentsMemory, - ) - - async def update( - self, - memory_id: str, - *, - memory_store_id: str, - view: BetaManagedAgentsMemoryView | Omit = omit, - content: Optional[str] | Omit = omit, - path: Optional[str] | Omit = omit, - precondition: BetaManagedAgentsPreconditionParam | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemory: - """ - UpdateMemory - - Args: - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - if not memory_id: - raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template( - "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", - memory_store_id=memory_store_id, - memory_id=memory_id, - ), - body=await async_maybe_transform( - { - "content": content, - "path": path, - "precondition": precondition, - }, - memory_update_params.MemoryUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"view": view}, memory_update_params.MemoryUpdateParams), - ), - cast_to=BetaManagedAgentsMemory, - ) - - def list( - self, - memory_store_id: str, - *, - depth: int | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - order_by: str | Omit = omit, - page: str | Omit = omit, - path_prefix: str | Omit = omit, - view: BetaManagedAgentsMemoryView | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaManagedAgentsMemoryListItem, AsyncPageCursor[BetaManagedAgentsMemoryListItem]]: - """ - ListMemories - - Args: - depth: Query parameter for depth - - limit: Query parameter for limit - - order: Query parameter for order - - order_by: Query parameter for order_by - - page: Query parameter for page - - path_prefix: Optional path prefix filter (raw string-prefix match; include a trailing slash - for directory-scoped lists). This value appears in request URLs. Do not include - secrets or personally identifiable information. - - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/memory_stores/{memory_store_id}/memories?beta=true", memory_store_id=memory_store_id), - page=AsyncPageCursor[BetaManagedAgentsMemoryListItem], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "depth": depth, - "limit": limit, - "order": order, - "order_by": order_by, - "page": page, - "path_prefix": path_prefix, - "view": view, - }, - memory_list_params.MemoryListParams, - ), - ), - model=cast( - Any, BetaManagedAgentsMemoryListItem - ), # Union types cannot be passed in as arguments in the type system - ) - - async def delete( - self, - memory_id: str, - *, - memory_store_id: str, - expected_content_sha256: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeletedMemory: - """ - DeleteMemory - - Args: - expected_content_sha256: Query parameter for expected_content_sha256 - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - if not memory_id: - raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._delete( - path_template( - "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", - memory_store_id=memory_store_id, - memory_id=memory_id, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"expected_content_sha256": expected_content_sha256}, memory_delete_params.MemoryDeleteParams - ), - ), - cast_to=BetaManagedAgentsDeletedMemory, - ) - - -class MemoriesWithRawResponse: - def __init__(self, memories: Memories) -> None: - self._memories = memories - - self.create = _legacy_response.to_raw_response_wrapper( - memories.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - memories.retrieve, - ) - self.update = _legacy_response.to_raw_response_wrapper( - memories.update, - ) - self.list = _legacy_response.to_raw_response_wrapper( - memories.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - memories.delete, - ) - - -class AsyncMemoriesWithRawResponse: - def __init__(self, memories: AsyncMemories) -> None: - self._memories = memories - - self.create = _legacy_response.async_to_raw_response_wrapper( - memories.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - memories.retrieve, - ) - self.update = _legacy_response.async_to_raw_response_wrapper( - memories.update, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - memories.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - memories.delete, - ) - - -class MemoriesWithStreamingResponse: - def __init__(self, memories: Memories) -> None: - self._memories = memories - - self.create = to_streamed_response_wrapper( - memories.create, - ) - self.retrieve = to_streamed_response_wrapper( - memories.retrieve, - ) - self.update = to_streamed_response_wrapper( - memories.update, - ) - self.list = to_streamed_response_wrapper( - memories.list, - ) - self.delete = to_streamed_response_wrapper( - memories.delete, - ) - - -class AsyncMemoriesWithStreamingResponse: - def __init__(self, memories: AsyncMemories) -> None: - self._memories = memories - - self.create = async_to_streamed_response_wrapper( - memories.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - memories.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - memories.update, - ) - self.list = async_to_streamed_response_wrapper( - memories.list, - ) - self.delete = async_to_streamed_response_wrapper( - memories.delete, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/memory_stores.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/memory_stores.py deleted file mode 100644 index 707584af..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/memory_stores.py +++ /dev/null @@ -1,890 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Union, Optional -from datetime import datetime -from itertools import chain - -import httpx - -from .... import _legacy_response -from .memories import ( - Memories, - AsyncMemories, - MemoriesWithRawResponse, - AsyncMemoriesWithRawResponse, - MemoriesWithStreamingResponse, - AsyncMemoriesWithStreamingResponse, -) -from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ....types.beta import memory_store_list_params, memory_store_create_params, memory_store_update_params -from ...._base_client import AsyncPaginator, make_request_options -from .memory_versions import ( - MemoryVersions, - AsyncMemoryVersions, - MemoryVersionsWithRawResponse, - AsyncMemoryVersionsWithRawResponse, - MemoryVersionsWithStreamingResponse, - AsyncMemoryVersionsWithStreamingResponse, -) -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.beta_managed_agents_memory_store import BetaManagedAgentsMemoryStore -from ....types.beta.beta_managed_agents_deleted_memory_store import BetaManagedAgentsDeletedMemoryStore - -__all__ = ["MemoryStores", "AsyncMemoryStores"] - - -class MemoryStores(SyncAPIResource): - @cached_property - def memories(self) -> Memories: - return Memories(self._client) - - @cached_property - def memory_versions(self) -> MemoryVersions: - return MemoryVersions(self._client) - - @cached_property - def with_raw_response(self) -> MemoryStoresWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return MemoryStoresWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MemoryStoresWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return MemoryStoresWithStreamingResponse(self) - - def create( - self, - *, - name: str, - description: str | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryStore: - """ - CreateMemoryStore - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - "/v1/memory_stores?beta=true", - body=maybe_transform( - { - "name": name, - "description": description, - "metadata": metadata, - }, - memory_store_create_params.MemoryStoreCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsMemoryStore, - ) - - def retrieve( - self, - memory_store_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryStore: - """ - GetMemoryStore - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get( - path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsMemoryStore, - ) - - def update( - self, - memory_store_id: str, - *, - description: Optional[str] | Omit = omit, - metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, - name: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryStore: - """UpdateMemoryStore - - Args: - metadata: Metadata patch. - - Set a key to a string to upsert it, or to null to delete it. - Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars - each) with values up to 512 chars. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), - body=maybe_transform( - { - "description": description, - "metadata": metadata, - "name": name, - }, - memory_store_update_params.MemoryStoreUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsMemoryStore, - ) - - def list( - self, - *, - created_at_gte: Union[str, datetime] | Omit = omit, - created_at_lte: Union[str, datetime] | Omit = omit, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaManagedAgentsMemoryStore]: - """ - ListMemoryStores - - Args: - created_at_gte: Return stores created at or after this time (inclusive). - - created_at_lte: Return stores created at or before this time (inclusive). - - include_archived: Query parameter for include_archived - - limit: Query parameter for limit - - page: Query parameter for page - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - "/v1/memory_stores?beta=true", - page=SyncPageCursor[BetaManagedAgentsMemoryStore], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "created_at_gte": created_at_gte, - "created_at_lte": created_at_lte, - "include_archived": include_archived, - "limit": limit, - "page": page, - }, - memory_store_list_params.MemoryStoreListParams, - ), - ), - model=BetaManagedAgentsMemoryStore, - ) - - def delete( - self, - memory_store_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeletedMemoryStore: - """ - DeleteMemoryStore - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._delete( - path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsDeletedMemoryStore, - ) - - def archive( - self, - memory_store_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryStore: - """ - ArchiveMemoryStore - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/memory_stores/{memory_store_id}/archive?beta=true", memory_store_id=memory_store_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsMemoryStore, - ) - - -class AsyncMemoryStores(AsyncAPIResource): - @cached_property - def memories(self) -> AsyncMemories: - return AsyncMemories(self._client) - - @cached_property - def memory_versions(self) -> AsyncMemoryVersions: - return AsyncMemoryVersions(self._client) - - @cached_property - def with_raw_response(self) -> AsyncMemoryStoresWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncMemoryStoresWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMemoryStoresWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncMemoryStoresWithStreamingResponse(self) - - async def create( - self, - *, - name: str, - description: str | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryStore: - """ - CreateMemoryStore - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - "/v1/memory_stores?beta=true", - body=await async_maybe_transform( - { - "name": name, - "description": description, - "metadata": metadata, - }, - memory_store_create_params.MemoryStoreCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsMemoryStore, - ) - - async def retrieve( - self, - memory_store_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryStore: - """ - GetMemoryStore - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._get( - path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsMemoryStore, - ) - - async def update( - self, - memory_store_id: str, - *, - description: Optional[str] | Omit = omit, - metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, - name: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryStore: - """UpdateMemoryStore - - Args: - metadata: Metadata patch. - - Set a key to a string to upsert it, or to null to delete it. - Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars - each) with values up to 512 chars. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), - body=await async_maybe_transform( - { - "description": description, - "metadata": metadata, - "name": name, - }, - memory_store_update_params.MemoryStoreUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsMemoryStore, - ) - - def list( - self, - *, - created_at_gte: Union[str, datetime] | Omit = omit, - created_at_lte: Union[str, datetime] | Omit = omit, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaManagedAgentsMemoryStore, AsyncPageCursor[BetaManagedAgentsMemoryStore]]: - """ - ListMemoryStores - - Args: - created_at_gte: Return stores created at or after this time (inclusive). - - created_at_lte: Return stores created at or before this time (inclusive). - - include_archived: Query parameter for include_archived - - limit: Query parameter for limit - - page: Query parameter for page - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - "/v1/memory_stores?beta=true", - page=AsyncPageCursor[BetaManagedAgentsMemoryStore], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "created_at_gte": created_at_gte, - "created_at_lte": created_at_lte, - "include_archived": include_archived, - "limit": limit, - "page": page, - }, - memory_store_list_params.MemoryStoreListParams, - ), - ), - model=BetaManagedAgentsMemoryStore, - ) - - async def delete( - self, - memory_store_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeletedMemoryStore: - """ - DeleteMemoryStore - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._delete( - path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsDeletedMemoryStore, - ) - - async def archive( - self, - memory_store_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryStore: - """ - ArchiveMemoryStore - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/memory_stores/{memory_store_id}/archive?beta=true", memory_store_id=memory_store_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsMemoryStore, - ) - - -class MemoryStoresWithRawResponse: - def __init__(self, memory_stores: MemoryStores) -> None: - self._memory_stores = memory_stores - - self.create = _legacy_response.to_raw_response_wrapper( - memory_stores.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - memory_stores.retrieve, - ) - self.update = _legacy_response.to_raw_response_wrapper( - memory_stores.update, - ) - self.list = _legacy_response.to_raw_response_wrapper( - memory_stores.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - memory_stores.delete, - ) - self.archive = _legacy_response.to_raw_response_wrapper( - memory_stores.archive, - ) - - @cached_property - def memories(self) -> MemoriesWithRawResponse: - return MemoriesWithRawResponse(self._memory_stores.memories) - - @cached_property - def memory_versions(self) -> MemoryVersionsWithRawResponse: - return MemoryVersionsWithRawResponse(self._memory_stores.memory_versions) - - -class AsyncMemoryStoresWithRawResponse: - def __init__(self, memory_stores: AsyncMemoryStores) -> None: - self._memory_stores = memory_stores - - self.create = _legacy_response.async_to_raw_response_wrapper( - memory_stores.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - memory_stores.retrieve, - ) - self.update = _legacy_response.async_to_raw_response_wrapper( - memory_stores.update, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - memory_stores.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - memory_stores.delete, - ) - self.archive = _legacy_response.async_to_raw_response_wrapper( - memory_stores.archive, - ) - - @cached_property - def memories(self) -> AsyncMemoriesWithRawResponse: - return AsyncMemoriesWithRawResponse(self._memory_stores.memories) - - @cached_property - def memory_versions(self) -> AsyncMemoryVersionsWithRawResponse: - return AsyncMemoryVersionsWithRawResponse(self._memory_stores.memory_versions) - - -class MemoryStoresWithStreamingResponse: - def __init__(self, memory_stores: MemoryStores) -> None: - self._memory_stores = memory_stores - - self.create = to_streamed_response_wrapper( - memory_stores.create, - ) - self.retrieve = to_streamed_response_wrapper( - memory_stores.retrieve, - ) - self.update = to_streamed_response_wrapper( - memory_stores.update, - ) - self.list = to_streamed_response_wrapper( - memory_stores.list, - ) - self.delete = to_streamed_response_wrapper( - memory_stores.delete, - ) - self.archive = to_streamed_response_wrapper( - memory_stores.archive, - ) - - @cached_property - def memories(self) -> MemoriesWithStreamingResponse: - return MemoriesWithStreamingResponse(self._memory_stores.memories) - - @cached_property - def memory_versions(self) -> MemoryVersionsWithStreamingResponse: - return MemoryVersionsWithStreamingResponse(self._memory_stores.memory_versions) - - -class AsyncMemoryStoresWithStreamingResponse: - def __init__(self, memory_stores: AsyncMemoryStores) -> None: - self._memory_stores = memory_stores - - self.create = async_to_streamed_response_wrapper( - memory_stores.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - memory_stores.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - memory_stores.update, - ) - self.list = async_to_streamed_response_wrapper( - memory_stores.list, - ) - self.delete = async_to_streamed_response_wrapper( - memory_stores.delete, - ) - self.archive = async_to_streamed_response_wrapper( - memory_stores.archive, - ) - - @cached_property - def memories(self) -> AsyncMemoriesWithStreamingResponse: - return AsyncMemoriesWithStreamingResponse(self._memory_stores.memories) - - @cached_property - def memory_versions(self) -> AsyncMemoryVersionsWithStreamingResponse: - return AsyncMemoryVersionsWithStreamingResponse(self._memory_stores.memory_versions) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/memory_versions.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/memory_versions.py deleted file mode 100644 index 36cb5a23..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/memory_stores/memory_versions.py +++ /dev/null @@ -1,554 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Union -from datetime import datetime -from itertools import chain - -import httpx - -from .... import _legacy_response -from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ...._base_client import AsyncPaginator, make_request_options -from ....types.beta.memory_stores import ( - BetaManagedAgentsMemoryView, - BetaManagedAgentsMemoryVersionOperation, - memory_version_list_params, - memory_version_retrieve_params, -) -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.memory_stores.beta_managed_agents_memory_view import BetaManagedAgentsMemoryView -from ....types.beta.memory_stores.beta_managed_agents_memory_version import BetaManagedAgentsMemoryVersion -from ....types.beta.memory_stores.beta_managed_agents_memory_version_operation import ( - BetaManagedAgentsMemoryVersionOperation, -) - -__all__ = ["MemoryVersions", "AsyncMemoryVersions"] - - -class MemoryVersions(SyncAPIResource): - @cached_property - def with_raw_response(self) -> MemoryVersionsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return MemoryVersionsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MemoryVersionsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return MemoryVersionsWithStreamingResponse(self) - - def retrieve( - self, - memory_version_id: str, - *, - memory_store_id: str, - view: BetaManagedAgentsMemoryView | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryVersion: - """ - GetMemoryVersion - - Args: - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - if not memory_version_id: - raise ValueError(f"Expected a non-empty value for `memory_version_id` but received {memory_version_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get( - path_template( - "/v1/memory_stores/{memory_store_id}/memory_versions/{memory_version_id}?beta=true", - memory_store_id=memory_store_id, - memory_version_id=memory_version_id, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"view": view}, memory_version_retrieve_params.MemoryVersionRetrieveParams), - ), - cast_to=BetaManagedAgentsMemoryVersion, - ) - - def list( - self, - memory_store_id: str, - *, - api_key_id: str | Omit = omit, - created_at_gte: Union[str, datetime] | Omit = omit, - created_at_lte: Union[str, datetime] | Omit = omit, - limit: int | Omit = omit, - memory_id: str | Omit = omit, - operation: BetaManagedAgentsMemoryVersionOperation | Omit = omit, - page: str | Omit = omit, - session_id: str | Omit = omit, - view: BetaManagedAgentsMemoryView | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaManagedAgentsMemoryVersion]: - """ - ListMemoryVersions - - Args: - api_key_id: Query parameter for api_key_id - - created_at_gte: Return versions created at or after this time (inclusive). - - created_at_lte: Return versions created at or before this time (inclusive). - - limit: Query parameter for limit - - memory_id: Query parameter for memory_id - - operation: Query parameter for operation - - page: Query parameter for page - - session_id: Query parameter for session_id - - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template( - "/v1/memory_stores/{memory_store_id}/memory_versions?beta=true", memory_store_id=memory_store_id - ), - page=SyncPageCursor[BetaManagedAgentsMemoryVersion], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "api_key_id": api_key_id, - "created_at_gte": created_at_gte, - "created_at_lte": created_at_lte, - "limit": limit, - "memory_id": memory_id, - "operation": operation, - "page": page, - "session_id": session_id, - "view": view, - }, - memory_version_list_params.MemoryVersionListParams, - ), - ), - model=BetaManagedAgentsMemoryVersion, - ) - - def redact( - self, - memory_version_id: str, - *, - memory_store_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryVersion: - """ - RedactMemoryVersion - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - if not memory_version_id: - raise ValueError(f"Expected a non-empty value for `memory_version_id` but received {memory_version_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template( - "/v1/memory_stores/{memory_store_id}/memory_versions/{memory_version_id}/redact?beta=true", - memory_store_id=memory_store_id, - memory_version_id=memory_version_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsMemoryVersion, - ) - - -class AsyncMemoryVersions(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncMemoryVersionsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncMemoryVersionsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMemoryVersionsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncMemoryVersionsWithStreamingResponse(self) - - async def retrieve( - self, - memory_version_id: str, - *, - memory_store_id: str, - view: BetaManagedAgentsMemoryView | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryVersion: - """ - GetMemoryVersion - - Args: - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - if not memory_version_id: - raise ValueError(f"Expected a non-empty value for `memory_version_id` but received {memory_version_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._get( - path_template( - "/v1/memory_stores/{memory_store_id}/memory_versions/{memory_version_id}?beta=true", - memory_store_id=memory_store_id, - memory_version_id=memory_version_id, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"view": view}, memory_version_retrieve_params.MemoryVersionRetrieveParams - ), - ), - cast_to=BetaManagedAgentsMemoryVersion, - ) - - def list( - self, - memory_store_id: str, - *, - api_key_id: str | Omit = omit, - created_at_gte: Union[str, datetime] | Omit = omit, - created_at_lte: Union[str, datetime] | Omit = omit, - limit: int | Omit = omit, - memory_id: str | Omit = omit, - operation: BetaManagedAgentsMemoryVersionOperation | Omit = omit, - page: str | Omit = omit, - session_id: str | Omit = omit, - view: BetaManagedAgentsMemoryView | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaManagedAgentsMemoryVersion, AsyncPageCursor[BetaManagedAgentsMemoryVersion]]: - """ - ListMemoryVersions - - Args: - api_key_id: Query parameter for api_key_id - - created_at_gte: Return versions created at or after this time (inclusive). - - created_at_lte: Return versions created at or before this time (inclusive). - - limit: Query parameter for limit - - memory_id: Query parameter for memory_id - - operation: Query parameter for operation - - page: Query parameter for page - - session_id: Query parameter for session_id - - view: Query parameter for view - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template( - "/v1/memory_stores/{memory_store_id}/memory_versions?beta=true", memory_store_id=memory_store_id - ), - page=AsyncPageCursor[BetaManagedAgentsMemoryVersion], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "api_key_id": api_key_id, - "created_at_gte": created_at_gte, - "created_at_lte": created_at_lte, - "limit": limit, - "memory_id": memory_id, - "operation": operation, - "page": page, - "session_id": session_id, - "view": view, - }, - memory_version_list_params.MemoryVersionListParams, - ), - ), - model=BetaManagedAgentsMemoryVersion, - ) - - async def redact( - self, - memory_version_id: str, - *, - memory_store_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsMemoryVersion: - """ - RedactMemoryVersion - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not memory_store_id: - raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") - if not memory_version_id: - raise ValueError(f"Expected a non-empty value for `memory_version_id` but received {memory_version_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template( - "/v1/memory_stores/{memory_store_id}/memory_versions/{memory_version_id}/redact?beta=true", - memory_store_id=memory_store_id, - memory_version_id=memory_version_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsMemoryVersion, - ) - - -class MemoryVersionsWithRawResponse: - def __init__(self, memory_versions: MemoryVersions) -> None: - self._memory_versions = memory_versions - - self.retrieve = _legacy_response.to_raw_response_wrapper( - memory_versions.retrieve, - ) - self.list = _legacy_response.to_raw_response_wrapper( - memory_versions.list, - ) - self.redact = _legacy_response.to_raw_response_wrapper( - memory_versions.redact, - ) - - -class AsyncMemoryVersionsWithRawResponse: - def __init__(self, memory_versions: AsyncMemoryVersions) -> None: - self._memory_versions = memory_versions - - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - memory_versions.retrieve, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - memory_versions.list, - ) - self.redact = _legacy_response.async_to_raw_response_wrapper( - memory_versions.redact, - ) - - -class MemoryVersionsWithStreamingResponse: - def __init__(self, memory_versions: MemoryVersions) -> None: - self._memory_versions = memory_versions - - self.retrieve = to_streamed_response_wrapper( - memory_versions.retrieve, - ) - self.list = to_streamed_response_wrapper( - memory_versions.list, - ) - self.redact = to_streamed_response_wrapper( - memory_versions.redact, - ) - - -class AsyncMemoryVersionsWithStreamingResponse: - def __init__(self, memory_versions: AsyncMemoryVersions) -> None: - self._memory_versions = memory_versions - - self.retrieve = async_to_streamed_response_wrapper( - memory_versions.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - memory_versions.list, - ) - self.redact = async_to_streamed_response_wrapper( - memory_versions.redact, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/messages/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/messages/__init__.py deleted file mode 100644 index 34b0a923..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/messages/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .batches import ( - Batches, - AsyncBatches, - BatchesWithRawResponse, - AsyncBatchesWithRawResponse, - BatchesWithStreamingResponse, - AsyncBatchesWithStreamingResponse, -) -from .messages import ( - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, - MessagesWithStreamingResponse, - AsyncMessagesWithStreamingResponse, -) - -__all__ = [ - "Batches", - "AsyncBatches", - "BatchesWithRawResponse", - "AsyncBatchesWithRawResponse", - "BatchesWithStreamingResponse", - "AsyncBatchesWithStreamingResponse", - "Messages", - "AsyncMessages", - "MessagesWithRawResponse", - "AsyncMessagesWithRawResponse", - "MessagesWithStreamingResponse", - "AsyncMessagesWithStreamingResponse", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/messages/batches.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/messages/batches.py deleted file mode 100644 index 5c2ad54d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/messages/batches.py +++ /dev/null @@ -1,900 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Iterable -from itertools import chain - -import httpx - -from .... import _legacy_response -from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPage, AsyncPage -from ...._exceptions import AnthropicError -from ...._base_client import AsyncPaginator, make_request_options -from ...._decoders.jsonl import JSONLDecoder, AsyncJSONLDecoder -from ....types.beta.messages import batch_list_params, batch_create_params -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.messages.beta_message_batch import BetaMessageBatch -from ....types.beta.messages.beta_deleted_message_batch import BetaDeletedMessageBatch -from ....types.beta.messages.beta_message_batch_individual_response import BetaMessageBatchIndividualResponse - -__all__ = ["Batches", "AsyncBatches"] - - -class Batches(SyncAPIResource): - @cached_property - def with_raw_response(self) -> BatchesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return BatchesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> BatchesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return BatchesWithStreamingResponse(self) - - def create( - self, - *, - requests: Iterable[batch_create_params.Request], - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessageBatch: - """ - Send a batch of Message creation requests. - - The Message Batches API can be used to process multiple Messages API requests at - once. Once a Message Batch is created, it begins processing immediately. Batches - can take up to 24 hours to complete. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - requests: List of requests for prompt completion. Each is an individual request to create - a Message. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return self._post( - "/v1/messages/batches?beta=true", - body=maybe_transform({"requests": requests}, batch_create_params.BatchCreateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessageBatch, - ) - - def retrieve( - self, - message_batch_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessageBatch: - """This endpoint is idempotent and can be used to poll for Message Batch - completion. - - To access the results of a Message Batch, make a request to the - `results_url` field in the response. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return self._get( - path_template("/v1/messages/batches/{message_batch_id}?beta=true", message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessageBatch, - ) - - def list( - self, - *, - after_id: str | Omit = omit, - before_id: str | Omit = omit, - limit: int | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPage[BetaMessageBatch]: - """List all Message Batches within a Workspace. - - Most recently created batches are - returned first. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - after_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately after this object. - - before_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately before this object. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return self._get_api_list( - "/v1/messages/batches?beta=true", - page=SyncPage[BetaMessageBatch], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after_id": after_id, - "before_id": before_id, - "limit": limit, - }, - batch_list_params.BatchListParams, - ), - ), - model=BetaMessageBatch, - ) - - def delete( - self, - message_batch_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaDeletedMessageBatch: - """ - Delete a Message Batch. - - Message Batches can only be deleted once they've finished processing. If you'd - like to delete an in-progress batch, you must first cancel it. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return self._delete( - path_template("/v1/messages/batches/{message_batch_id}?beta=true", message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaDeletedMessageBatch, - ) - - def cancel( - self, - message_batch_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessageBatch: - """Batches may be canceled any time before processing ends. - - Once cancellation is - initiated, the batch enters a `canceling` state, at which time the system may - complete any in-progress, non-interruptible requests before finalizing - cancellation. - - The number of canceled requests is specified in `request_counts`. To determine - which requests were canceled, check the individual results within the batch. - Note that cancellation may not result in any canceled requests if they were - non-interruptible. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return self._post( - path_template( - "/v1/messages/batches/{message_batch_id}/cancel?beta=true", message_batch_id=message_batch_id - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessageBatch, - ) - - def results( - self, - message_batch_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> JSONLDecoder[BetaMessageBatchIndividualResponse]: - """ - Streams the results of a Message Batch as a `.jsonl` file. - - Each line in the file is a JSON object containing the result of a single request - in the Message Batch. Results are not guaranteed to be in the same order as - requests. Use the `custom_id` field to match results to requests. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - - batch = self.retrieve(message_batch_id=message_batch_id) - if not batch.results_url: - raise AnthropicError( - f"No `results_url` for the given batch; Has it finished processing? {batch.processing_status}" - ) - - extra_headers = {"Accept": "application/binary", **(extra_headers or {})} - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return self._get( - path_template(batch.results_url, message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=JSONLDecoder[BetaMessageBatchIndividualResponse], - stream=True, - ) - - -class AsyncBatches(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncBatchesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncBatchesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncBatchesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncBatchesWithStreamingResponse(self) - - async def create( - self, - *, - requests: Iterable[batch_create_params.Request], - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessageBatch: - """ - Send a batch of Message creation requests. - - The Message Batches API can be used to process multiple Messages API requests at - once. Once a Message Batch is created, it begins processing immediately. Batches - can take up to 24 hours to complete. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - requests: List of requests for prompt completion. Each is an individual request to create - a Message. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return await self._post( - "/v1/messages/batches?beta=true", - body=await async_maybe_transform({"requests": requests}, batch_create_params.BatchCreateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessageBatch, - ) - - async def retrieve( - self, - message_batch_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessageBatch: - """This endpoint is idempotent and can be used to poll for Message Batch - completion. - - To access the results of a Message Batch, make a request to the - `results_url` field in the response. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return await self._get( - path_template("/v1/messages/batches/{message_batch_id}?beta=true", message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessageBatch, - ) - - def list( - self, - *, - after_id: str | Omit = omit, - before_id: str | Omit = omit, - limit: int | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaMessageBatch, AsyncPage[BetaMessageBatch]]: - """List all Message Batches within a Workspace. - - Most recently created batches are - returned first. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - after_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately after this object. - - before_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately before this object. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return self._get_api_list( - "/v1/messages/batches?beta=true", - page=AsyncPage[BetaMessageBatch], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after_id": after_id, - "before_id": before_id, - "limit": limit, - }, - batch_list_params.BatchListParams, - ), - ), - model=BetaMessageBatch, - ) - - async def delete( - self, - message_batch_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaDeletedMessageBatch: - """ - Delete a Message Batch. - - Message Batches can only be deleted once they've finished processing. If you'd - like to delete an in-progress batch, you must first cancel it. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return await self._delete( - path_template("/v1/messages/batches/{message_batch_id}?beta=true", message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaDeletedMessageBatch, - ) - - async def cancel( - self, - message_batch_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessageBatch: - """Batches may be canceled any time before processing ends. - - Once cancellation is - initiated, the batch enters a `canceling` state, at which time the system may - complete any in-progress, non-interruptible requests before finalizing - cancellation. - - The number of canceled requests is specified in `request_counts`. To determine - which requests were canceled, check the individual results within the batch. - Note that cancellation may not result in any canceled requests if they were - non-interruptible. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return await self._post( - path_template( - "/v1/messages/batches/{message_batch_id}/cancel?beta=true", message_batch_id=message_batch_id - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessageBatch, - ) - - async def results( - self, - message_batch_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncJSONLDecoder[BetaMessageBatchIndividualResponse]: - """ - Streams the results of a Message Batch as a `.jsonl` file. - - Each line in the file is a JSON object containing the result of a single request - in the Message Batch. Results are not guaranteed to be in the same order as - requests. Use the `custom_id` field to match results to requests. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - - batch = await self.retrieve(message_batch_id=message_batch_id) - if not batch.results_url: - raise AnthropicError( - f"No `results_url` for the given batch; Has it finished processing? {batch.processing_status}" - ) - - extra_headers = {"Accept": "application/binary", **(extra_headers or {})} - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} - return await self._get( - path_template(batch.results_url, message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AsyncJSONLDecoder[BetaMessageBatchIndividualResponse], - stream=True, - ) - - -class BatchesWithRawResponse: - def __init__(self, batches: Batches) -> None: - self._batches = batches - - self.create = _legacy_response.to_raw_response_wrapper( - batches.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - batches.retrieve, - ) - self.list = _legacy_response.to_raw_response_wrapper( - batches.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - batches.delete, - ) - self.cancel = _legacy_response.to_raw_response_wrapper( - batches.cancel, - ) - self.results = _legacy_response.to_raw_response_wrapper( - batches.results, - ) - - -class AsyncBatchesWithRawResponse: - def __init__(self, batches: AsyncBatches) -> None: - self._batches = batches - - self.create = _legacy_response.async_to_raw_response_wrapper( - batches.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - batches.retrieve, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - batches.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - batches.delete, - ) - self.cancel = _legacy_response.async_to_raw_response_wrapper( - batches.cancel, - ) - self.results = _legacy_response.async_to_raw_response_wrapper( - batches.results, - ) - - -class BatchesWithStreamingResponse: - def __init__(self, batches: Batches) -> None: - self._batches = batches - - self.create = to_streamed_response_wrapper( - batches.create, - ) - self.retrieve = to_streamed_response_wrapper( - batches.retrieve, - ) - self.list = to_streamed_response_wrapper( - batches.list, - ) - self.delete = to_streamed_response_wrapper( - batches.delete, - ) - self.cancel = to_streamed_response_wrapper( - batches.cancel, - ) - self.results = to_streamed_response_wrapper( - batches.results, - ) - - -class AsyncBatchesWithStreamingResponse: - def __init__(self, batches: AsyncBatches) -> None: - self._batches = batches - - self.create = async_to_streamed_response_wrapper( - batches.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - batches.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - batches.list, - ) - self.delete = async_to_streamed_response_wrapper( - batches.delete, - ) - self.cancel = async_to_streamed_response_wrapper( - batches.cancel, - ) - self.results = async_to_streamed_response_wrapper( - batches.results, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/messages/messages.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/messages/messages.py deleted file mode 100644 index e3e10e09..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/messages/messages.py +++ /dev/null @@ -1,3839 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import inspect -import warnings -from typing import TYPE_CHECKING, List, Type, Union, Iterable, Optional, cast -from functools import partial -from itertools import chain -from typing_extensions import Literal, overload - -import httpx -import pydantic - -from .... import _legacy_response -from .batches import ( - Batches, - AsyncBatches, - BatchesWithRawResponse, - AsyncBatchesWithRawResponse, - BatchesWithStreamingResponse, - AsyncBatchesWithStreamingResponse, -) -from ...._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ...._utils import is_given, required_args, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._models import TypeAdapter -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....lib.tools import ( - BetaToolRunner, - BetaAsyncToolRunner, - BetaStreamingToolRunner, - BetaAsyncStreamingToolRunner, -) -from ...._constants import DEFAULT_TIMEOUT, MODEL_NONSTREAMING_TOKENS -from ...._streaming import Stream, AsyncStream -from ....types.beta import ( - BetaThinkingConfigParam, - message_create_params, - message_count_tokens_params, -) -from ...._exceptions import AnthropicError -from ...._base_client import make_request_options -from ...._utils._utils import is_dict -from ....lib.streaming import BetaMessageStreamManager, BetaAsyncMessageStreamManager -from ...messages.messages import DEPRECATED_MODELS, MODELS_TO_WARN_WITH_THINKING_ENABLED -from ....types.model_param import ModelParam -from ....lib._parse._response import ResponseFormatT, parse_beta_response -from ....lib._parse._transform import transform_schema -from ....lib._stainless_helpers import stainless_helper_header as _stainless_helper_header -from ....types.beta.beta_message import BetaMessage -from ....lib.tools._beta_functions import ( - BetaFunctionTool, - BetaRunnableTool, - BetaAsyncFunctionTool, - BetaAsyncRunnableTool, - BetaBuiltinFunctionTool, - BetaAsyncBuiltinFunctionTool, -) -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.beta_message_param import BetaMessageParam -from ....types.beta.beta_metadata_param import BetaMetadataParam -from ....types.beta.parsed_beta_message import ParsedBetaMessage -from ....types.beta.beta_text_block_param import BetaTextBlockParam -from ....types.beta.beta_tool_union_param import BetaToolUnionParam -from ....types.beta.beta_tool_choice_param import BetaToolChoiceParam -from ....lib.tools._beta_compaction_control import CompactionControl -from ....types.beta.beta_output_config_param import BetaOutputConfigParam -from ....types.beta.beta_message_tokens_count import BetaMessageTokensCount -from ....types.beta.beta_thinking_config_param import BetaThinkingConfigParam -from ....types.beta.beta_json_output_format_param import BetaJSONOutputFormatParam -from ....types.beta.beta_raw_message_stream_event import BetaRawMessageStreamEvent -from ....types.beta.beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from ....types.beta.beta_context_management_config_param import BetaContextManagementConfigParam -from ....types.beta.beta_request_mcp_server_url_definition_param import BetaRequestMCPServerURLDefinitionParam - -if TYPE_CHECKING: - from ...._client import Anthropic, AsyncAnthropic - -__all__ = ["Messages", "AsyncMessages"] - - -class Messages(SyncAPIResource): - @cached_property - def batches(self) -> Batches: - return Batches(self._client) - - @cached_property - def with_raw_response(self) -> MessagesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return MessagesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MessagesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return MessagesWithStreamingResponse(self) - - @overload - def create( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessage: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - context_management: Context management configuration. - - This allows you to control how Claude manages context across multiple requests, - such as whether to clear function results or not. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - mcp_servers: MCP servers to be utilized in this request - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - output_format: Deprecated: Use `output_config.format` instead. See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - - A schema to specify Claude's output format in responses. This parameter will be - removed in a future release. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - speed: The inference speed mode for this request. `"fast"` enables high - output-tokens-per-second inference. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - def create( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - stream: Literal[True], - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Stream[BetaRawMessageStreamEvent]: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - context_management: Context management configuration. - - This allows you to control how Claude manages context across multiple requests, - such as whether to clear function results or not. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - mcp_servers: MCP servers to be utilized in this request - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - output_format: Deprecated: Use `output_config.format` instead. See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - - A schema to specify Claude's output format in responses. This parameter will be - removed in a future release. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - speed: The inference speed mode for this request. `"fast"` enables high - output-tokens-per-second inference. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - def create( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - stream: bool, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessage | Stream[BetaRawMessageStreamEvent]: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - context_management: Context management configuration. - - This allows you to control how Claude manages context across multiple requests, - such as whether to clear function results or not. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - mcp_servers: MCP servers to be utilized in this request - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - output_format: Deprecated: Use `output_config.format` instead. See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - - A schema to specify Claude's output format in responses. This parameter will be - removed in a future release. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - speed: The inference speed mode for this request. `"fast"` enables high - output-tokens-per-second inference. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @required_args(["max_tokens", "messages", "model"], ["max_tokens", "messages", "model", "stream"]) - def create( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Literal[True] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessage | Stream[BetaRawMessageStreamEvent]: - validate_output_format(output_format) - _validate_output_config_conflict(output_config, output_format) - _warn_output_format_deprecated(output_format) - - if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: - timeout = self._client._calculate_nonstreaming_timeout( - max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) - ) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - merged_output_config = _merge_output_configs(output_config, output_format) - - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **_stainless_helper_header(tools, messages), - **(extra_headers or {}), - } - return self._post( - "/v1/messages?beta=true", - body=maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "container": container, - "context_management": context_management, - "inference_geo": inference_geo, - "mcp_servers": mcp_servers, - "metadata": metadata, - "output_config": merged_output_config, - "output_format": omit, - "service_tier": service_tier, - "speed": speed, - "stop_sequences": stop_sequences, - "stream": stream, - "system": system, - "temperature": temperature, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - "top_k": top_k, - "top_p": top_p, - "user_profile_id": user_profile_id, - }, - message_create_params.MessageCreateParamsStreaming - if stream - else message_create_params.MessageCreateParamsNonStreaming, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessage, - stream=stream or False, - stream_cls=Stream[BetaRawMessageStreamEvent], - ) - - def parse( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Literal[True] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ParsedBetaMessage[ResponseFormatT]: - _validate_output_config_conflict(output_config, output_format) - _warn_output_format_deprecated(output_format) - - if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: - timeout = self._client._calculate_nonstreaming_timeout( - max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) - ) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - betas = [beta for beta in betas] if is_given(betas) else [] - - if "structured-outputs-2025-12-15" not in betas: - # Ensure structured outputs beta is included for parse method - betas.append("structured-outputs-2025-12-15") - - extra_headers = { - "X-Stainless-Helper": "beta.messages.parse", - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), - **_stainless_helper_header(tools, messages), - **(extra_headers or {}), - } - - if is_given(output_format) and output_format is not None: - adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) - - try: - schema = adapted_type.json_schema() - transformed_output_format = BetaJSONOutputFormatParam( - schema=transform_schema(schema), type="json_schema" - ) - except pydantic.errors.PydanticSchemaGenerationError as e: - raise TypeError( - ( - "Could not generate JSON schema for the given `output_format` type. " - "Use a type that works with `pydantic.TypeAdapter`" - ) - ) from e - - merged_output_config = _merge_output_configs(output_config, transformed_output_format) - else: - merged_output_config = output_config - - def parser(response: BetaMessage) -> ParsedBetaMessage[ResponseFormatT]: - return parse_beta_response( - response=response, - output_format=cast( - ResponseFormatT, - output_format if is_given(output_format) and output_format is not None else NOT_GIVEN, - ), - ) - - return self._post( - "/v1/messages?beta=true", - body=maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "container": container, - "context_management": context_management, - "inference_geo": inference_geo, - "mcp_servers": mcp_servers, - "metadata": metadata, - "output_config": merged_output_config, - "output_format": omit, - "service_tier": service_tier, - "speed": speed, - "stop_sequences": stop_sequences, - "stream": stream, - "system": system, - "temperature": temperature, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - "top_k": top_k, - "top_p": top_p, - "user_profile_id": user_profile_id, - }, - message_create_params.MessageCreateParamsNonStreaming, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - post_parser=parser, - ), - cast_to=cast(Type[ParsedBetaMessage[ResponseFormatT]], BetaMessage), - stream=False, - ) - - @overload - def tool_runner( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - tools: Iterable[BetaRunnableTool | BetaToolUnionParam], - compaction_control: CompactionControl | Omit = omit, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - max_iterations: int | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BetaToolRunner[ResponseFormatT]: ... - - @overload - def tool_runner( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - tools: Iterable[BetaRunnableTool | BetaToolUnionParam], - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - compaction_control: CompactionControl | Omit = omit, - stream: Literal[True], - max_iterations: int | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BetaStreamingToolRunner[ResponseFormatT]: ... - - @overload - def tool_runner( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - tools: Iterable[BetaRunnableTool | BetaToolUnionParam], - compaction_control: CompactionControl | Omit = omit, - stream: bool, - max_iterations: int | Omit = omit, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BetaStreamingToolRunner[ResponseFormatT] | BetaToolRunner[ResponseFormatT]: ... - - def tool_runner( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - tools: Iterable[BetaRunnableTool | BetaToolUnionParam], - compaction_control: CompactionControl | Omit = omit, - max_iterations: int | Omit = omit, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: bool | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BetaStreamingToolRunner[ResponseFormatT] | BetaToolRunner[ResponseFormatT]: - """Create a Message stream""" - _validate_output_config_conflict(output_config, output_format) - _warn_output_format_deprecated(output_format) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - extra_headers = { - "X-Stainless-Helper": "BetaToolRunner", - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), - **_stainless_helper_header(tools, messages), - **(extra_headers or {}), - } - - runnable_tools: list[BetaRunnableTool] = [] - raw_tools: list[BetaToolUnionParam] = [] - - for tool in tools: - if isinstance(tool, (BetaFunctionTool, BetaBuiltinFunctionTool)): - runnable_tools.append(tool) - else: - raw_tools.append(tool) - - params = cast( - message_create_params.ParseMessageCreateParamsBase[ResponseFormatT], - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "container": container, - "context_management": context_management, - "inference_geo": inference_geo, - "mcp_servers": mcp_servers, - "metadata": metadata, - "output_config": output_config, - "output_format": output_format, - "service_tier": service_tier, - "speed": speed, - "stop_sequences": stop_sequences, - "system": system, - "temperature": temperature, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": [*[tool.to_dict() for tool in runnable_tools], *raw_tools], - "top_k": top_k, - "top_p": top_p, - "user_profile_id": user_profile_id, - }, - ) - - if stream: - return BetaStreamingToolRunner[ResponseFormatT]( - tools=runnable_tools, - params=params, - options={ - "extra_headers": extra_headers, - "extra_query": extra_query, - "extra_body": extra_body, - "timeout": timeout, - }, - client=cast("Anthropic", self._client), - max_iterations=max_iterations if is_given(max_iterations) else None, - compaction_control=compaction_control if is_given(compaction_control) else None, - ) - return BetaToolRunner[ResponseFormatT]( - tools=runnable_tools, - params=params, - options={ - "extra_headers": extra_headers, - "extra_query": extra_query, - "extra_body": extra_body, - "timeout": timeout, - }, - client=cast("Anthropic", self._client), - max_iterations=max_iterations if is_given(max_iterations) else None, - compaction_control=compaction_control if is_given(compaction_control) else None, - ) - - def stream( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: None | BetaJSONOutputFormatParam | type[ResponseFormatT] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BetaMessageStreamManager[ResponseFormatT]: - _validate_output_config_conflict(output_config, output_format) - _warn_output_format_deprecated(output_format) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - """Create a Message stream""" - extra_headers = { - "X-Stainless-Helper-Method": "stream", - "X-Stainless-Stream-Helper": "beta.messages", - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), - **_stainless_helper_header(tools, messages), - **(extra_headers or {}), - } - - transformed_output_format: BetaJSONOutputFormatParam | Omit = omit - - if is_dict(output_format): - transformed_output_format = cast(BetaJSONOutputFormatParam, output_format) - elif is_given(output_format) and output_format is not None: - adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) - - try: - schema = adapted_type.json_schema() - transformed_output_format = BetaJSONOutputFormatParam( - schema=transform_schema(schema), type="json_schema" - ) - except pydantic.errors.PydanticSchemaGenerationError as e: - raise TypeError( - ( - "Could not generate JSON schema for the given `output_format` type. " - "Use a type that works with `pydantic.TypeAdapter`" - ) - ) from e - - merged_output_config = _merge_output_configs(output_config, transformed_output_format) - - make_request = partial( - self._post, - "/v1/messages?beta=true", - body=maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "metadata": metadata, - "output_config": merged_output_config, - "output_format": omit, - "container": container, - "context_management": context_management, - "inference_geo": inference_geo, - "mcp_servers": mcp_servers, - "service_tier": service_tier, - "speed": speed, - "stop_sequences": stop_sequences, - "system": system, - "temperature": temperature, - "thinking": thinking, - "top_k": top_k, - "top_p": top_p, - "tools": tools, - "tool_choice": tool_choice, - "user_profile_id": user_profile_id, - "stream": True, - }, - message_create_params.MessageCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessage, - stream=True, - stream_cls=Stream[BetaRawMessageStreamEvent], - ) - return BetaMessageStreamManager( - make_request, - output_format=NOT_GIVEN if is_dict(output_format) else cast(ResponseFormatT, output_format), - ) - - def count_tokens( - self, - *, - messages: Iterable[BetaMessageParam], - model: ModelParam, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[message_count_tokens_params.Tool] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessageTokensCount: - """ - Count the number of tokens in a Message. - - The Token Count API can be used to count the number of tokens in a Message, - including tools, images, and documents, without creating it. - - Learn more about token counting in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/token-counting) - - Args: - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - context_management: Context management configuration. - - This allows you to control how Claude manages context across multiple requests, - such as whether to clear function results or not. - - mcp_servers: MCP servers to be utilized in this request - - output_config: Configuration options for the model's output, such as the output format. - - output_format: Deprecated: Use `output_config.format` instead. See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - - A schema to specify Claude's output format in responses. This parameter will be - removed in a future release. - - speed: The inference speed mode for this request. `"fast"` enables high - output-tokens-per-second inference. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - _validate_output_config_conflict(output_config, output_format) - _warn_output_format_deprecated(output_format) - - merged_output_config = _merge_output_configs(output_config, output_format) - - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["token-counting-2024-11-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "token-counting-2024-11-01", **(extra_headers or {})} - return self._post( - "/v1/messages/count_tokens?beta=true", - body=maybe_transform( - { - "messages": messages, - "model": model, - "cache_control": cache_control, - "context_management": context_management, - "mcp_servers": mcp_servers, - "output_config": merged_output_config, - "output_format": omit, - "speed": speed, - "system": system, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - }, - message_count_tokens_params.MessageCountTokensParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessageTokensCount, - ) - - -class AsyncMessages(AsyncAPIResource): - @cached_property - def batches(self) -> AsyncBatches: - return AsyncBatches(self._client) - - @cached_property - def with_raw_response(self) -> AsyncMessagesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncMessagesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncMessagesWithStreamingResponse(self) - - @overload - async def create( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessage: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - context_management: Context management configuration. - - This allows you to control how Claude manages context across multiple requests, - such as whether to clear function results or not. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - mcp_servers: MCP servers to be utilized in this request - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - output_format: Deprecated: Use `output_config.format` instead. See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - - A schema to specify Claude's output format in responses. This parameter will be - removed in a future release. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - speed: The inference speed mode for this request. `"fast"` enables high - output-tokens-per-second inference. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - async def create( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - stream: Literal[True], - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncStream[BetaRawMessageStreamEvent]: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - context_management: Context management configuration. - - This allows you to control how Claude manages context across multiple requests, - such as whether to clear function results or not. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - mcp_servers: MCP servers to be utilized in this request - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - output_format: Deprecated: Use `output_config.format` instead. See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - - A schema to specify Claude's output format in responses. This parameter will be - removed in a future release. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - speed: The inference speed mode for this request. `"fast"` enables high - output-tokens-per-second inference. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - async def create( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - stream: bool, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessage | AsyncStream[BetaRawMessageStreamEvent]: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - context_management: Context management configuration. - - This allows you to control how Claude manages context across multiple requests, - such as whether to clear function results or not. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - mcp_servers: MCP servers to be utilized in this request - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - output_format: Deprecated: Use `output_config.format` instead. See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - - A schema to specify Claude's output format in responses. This parameter will be - removed in a future release. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - speed: The inference speed mode for this request. `"fast"` enables high - output-tokens-per-second inference. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a - party other than your organization. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @required_args(["max_tokens", "messages", "model"], ["max_tokens", "messages", "model", "stream"]) - async def create( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Literal[True] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessage | AsyncStream[BetaRawMessageStreamEvent]: - validate_output_format(output_format) - _validate_output_config_conflict(output_config, output_format) - _warn_output_format_deprecated(output_format) - - if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: - timeout = self._client._calculate_nonstreaming_timeout( - max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) - ) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - merged_output_config = _merge_output_configs(output_config, output_format) - - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **_stainless_helper_header(tools, messages), - **(extra_headers or {}), - } - return await self._post( - "/v1/messages?beta=true", - body=await async_maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "container": container, - "context_management": context_management, - "inference_geo": inference_geo, - "mcp_servers": mcp_servers, - "metadata": metadata, - "output_config": merged_output_config, - "output_format": omit, - "service_tier": service_tier, - "speed": speed, - "stop_sequences": stop_sequences, - "stream": stream, - "system": system, - "temperature": temperature, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - "top_k": top_k, - "top_p": top_p, - "user_profile_id": user_profile_id, - }, - message_create_params.MessageCreateParamsStreaming - if stream - else message_create_params.MessageCreateParamsNonStreaming, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessage, - stream=stream or False, - stream_cls=AsyncStream[BetaRawMessageStreamEvent], - ) - - async def parse( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Literal[True] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ParsedBetaMessage[ResponseFormatT]: - _validate_output_config_conflict(output_config, output_format) - _warn_output_format_deprecated(output_format) - - if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: - timeout = self._client._calculate_nonstreaming_timeout( - max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) - ) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - betas = [beta for beta in betas] if is_given(betas) else [] - - if "structured-outputs-2025-12-15" not in betas: - # Ensure structured outputs beta is included for parse method - betas.append("structured-outputs-2025-12-15") - - extra_headers = { - "X-Stainless-Helper": "beta.messages.parse", - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), - **_stainless_helper_header(tools, messages), - **(extra_headers or {}), - } - - if is_given(output_format) and output_format is not None: - adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) - - try: - schema = adapted_type.json_schema() - transformed_output_format = BetaJSONOutputFormatParam( - schema=transform_schema(schema), type="json_schema" - ) - except pydantic.errors.PydanticSchemaGenerationError as e: - raise TypeError( - ( - "Could not generate JSON schema for the given `output_format` type. " - "Use a type that works with `pydantic.TypeAdapter`" - ) - ) from e - - merged_output_config = _merge_output_configs(output_config, transformed_output_format) - else: - merged_output_config = output_config - - def parser(response: BetaMessage) -> ParsedBetaMessage[ResponseFormatT]: - return parse_beta_response( - response=response, - output_format=cast( - ResponseFormatT, - output_format if is_given(output_format) and output_format is not None else NOT_GIVEN, - ), - ) - - return await self._post( - "/v1/messages?beta=true", - body=maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "container": container, - "context_management": context_management, - "inference_geo": inference_geo, - "mcp_servers": mcp_servers, - "output_config": merged_output_config, - "metadata": metadata, - "output_format": omit, - "service_tier": service_tier, - "speed": speed, - "stop_sequences": stop_sequences, - "stream": stream, - "system": system, - "temperature": temperature, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - "top_k": top_k, - "top_p": top_p, - "user_profile_id": user_profile_id, - }, - message_create_params.MessageCreateParamsNonStreaming, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - post_parser=parser, - ), - cast_to=cast(Type[ParsedBetaMessage[ResponseFormatT]], BetaMessage), - stream=False, - ) - - @overload - def tool_runner( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - tools: Iterable[BetaAsyncRunnableTool | BetaToolUnionParam], - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - compaction_control: CompactionControl | Omit = omit, - max_iterations: int | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BetaAsyncToolRunner[ResponseFormatT]: ... - - @overload - def tool_runner( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - tools: Iterable[BetaAsyncRunnableTool | BetaToolUnionParam], - compaction_control: CompactionControl | Omit = omit, - stream: Literal[True], - max_iterations: int | Omit = omit, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BetaAsyncStreamingToolRunner[ResponseFormatT]: ... - - @overload - def tool_runner( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - tools: Iterable[BetaAsyncRunnableTool | BetaToolUnionParam], - compaction_control: CompactionControl | Omit = omit, - stream: bool, - max_iterations: int | Omit = omit, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BetaAsyncStreamingToolRunner[ResponseFormatT] | BetaAsyncToolRunner[ResponseFormatT]: ... - - def tool_runner( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - tools: Iterable[BetaAsyncRunnableTool | BetaToolUnionParam], - compaction_control: CompactionControl | Omit = omit, - max_iterations: int | Omit = omit, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[True] | Literal[False] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BetaAsyncToolRunner[ResponseFormatT] | BetaAsyncStreamingToolRunner[ResponseFormatT]: - """Create a Message stream""" - _validate_output_config_conflict(output_config, output_format) - _warn_output_format_deprecated(output_format) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - extra_headers = { - "X-Stainless-Helper": "BetaToolRunner", - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), - **_stainless_helper_header(tools, messages), - **(extra_headers or {}), - } - - runnable_tools: list[BetaAsyncRunnableTool] = [] - raw_tools: list[BetaToolUnionParam] = [] - - for tool in tools: - if isinstance(tool, (BetaAsyncFunctionTool, BetaAsyncBuiltinFunctionTool)): - runnable_tools.append(tool) - else: - raw_tools.append(tool) - - params = cast( - message_create_params.ParseMessageCreateParamsBase[ResponseFormatT], - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "container": container, - "context_management": context_management, - "inference_geo": inference_geo, - "mcp_servers": mcp_servers, - "metadata": metadata, - "output_config": output_config, - "output_format": output_format, - "service_tier": service_tier, - "speed": speed, - "stop_sequences": stop_sequences, - "system": system, - "temperature": temperature, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": [*[tool.to_dict() for tool in runnable_tools], *raw_tools], - "top_k": top_k, - "top_p": top_p, - "user_profile_id": user_profile_id, - }, - ) - - if stream: - return BetaAsyncStreamingToolRunner[ResponseFormatT]( - tools=runnable_tools, - params=params, - options={ - "extra_headers": extra_headers, - "extra_query": extra_query, - "extra_body": extra_body, - "timeout": timeout, - }, - client=cast("AsyncAnthropic", self._client), - max_iterations=max_iterations if is_given(max_iterations) else None, - compaction_control=compaction_control if is_given(compaction_control) else None, - ) - return BetaAsyncToolRunner[ResponseFormatT]( - tools=runnable_tools, - params=params, - options={ - "extra_headers": extra_headers, - "extra_query": extra_query, - "extra_body": extra_body, - "timeout": timeout, - }, - client=cast("AsyncAnthropic", self._client), - max_iterations=max_iterations if is_given(max_iterations) else None, - compaction_control=compaction_control if is_given(compaction_control) else None, - ) - - def stream( - self, - *, - max_tokens: int, - messages: Iterable[BetaMessageParam], - model: ModelParam, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - metadata: BetaMetadataParam | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: None | type[ResponseFormatT] | BetaJSONOutputFormatParam | Omit = omit, - container: Optional[message_create_params.Container] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[BetaToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - user_profile_id: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> BetaAsyncMessageStreamManager[ResponseFormatT]: - _validate_output_config_conflict(output_config, output_format) - _warn_output_format_deprecated(output_format) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - extra_headers = { - "X-Stainless-Helper-Method": "stream", - "X-Stainless-Stream-Helper": "beta.messages", - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN}), - **_stainless_helper_header(tools, messages), - **(extra_headers or {}), - } - - transformed_output_format: BetaJSONOutputFormatParam | Omit = omit - - if is_dict(output_format): - transformed_output_format = cast(BetaJSONOutputFormatParam, output_format) - elif is_given(output_format) and output_format is not None: - adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) - - try: - schema = adapted_type.json_schema() - transformed_output_format = BetaJSONOutputFormatParam( - schema=transform_schema(schema), type="json_schema" - ) - except pydantic.errors.PydanticSchemaGenerationError as e: - raise TypeError( - ( - "Could not generate JSON schema for the given `output_format` type. " - "Use a type that works with `pydantic.TypeAdapter`" - ) - ) from e - - merged_output_config = _merge_output_configs(output_config, transformed_output_format) - - request = self._post( - "/v1/messages?beta=true", - body=maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "metadata": metadata, - "output_config": merged_output_config, - "output_format": omit, - "container": container, - "context_management": context_management, - "inference_geo": inference_geo, - "mcp_servers": mcp_servers, - "service_tier": service_tier, - "speed": speed, - "stop_sequences": stop_sequences, - "system": system, - "temperature": temperature, - "thinking": thinking, - "top_k": top_k, - "top_p": top_p, - "tools": tools, - "tool_choice": tool_choice, - "user_profile_id": user_profile_id, - "stream": True, - }, - message_create_params.MessageCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessage, - stream=True, - stream_cls=AsyncStream[BetaRawMessageStreamEvent], - ) - return BetaAsyncMessageStreamManager( - request, - output_format=NOT_GIVEN if is_dict(output_format) else cast(ResponseFormatT, output_format), - ) - - async def count_tokens( - self, - *, - messages: Iterable[BetaMessageParam], - model: ModelParam, - cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, - context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, - output_config: BetaOutputConfigParam | Omit = omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, - speed: Optional[Literal["standard", "fast"]] | Omit = omit, - system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, - thinking: BetaThinkingConfigParam | Omit = omit, - tool_choice: BetaToolChoiceParam | Omit = omit, - tools: Iterable[message_count_tokens_params.Tool] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaMessageTokensCount: - """ - Count the number of tokens in a Message. - - The Token Count API can be used to count the number of tokens in a Message, - including tools, images, and documents, without creating it. - - Learn more about token counting in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/token-counting) - - Args: - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - context_management: Context management configuration. - - This allows you to control how Claude manages context across multiple requests, - such as whether to clear function results or not. - - mcp_servers: MCP servers to be utilized in this request - - output_config: Configuration options for the model's output, such as the output format. - - output_format: Deprecated: Use `output_config.format` instead. See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - - A schema to specify Claude's output format in responses. This parameter will be - removed in a future release. - - speed: The inference speed mode for this request. `"fast"` enables high - output-tokens-per-second inference. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - _validate_output_config_conflict(output_config, output_format) - _warn_output_format_deprecated(output_format) - - merged_output_config = _merge_output_configs(output_config, output_format) - - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["token-counting-2024-11-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "token-counting-2024-11-01", **(extra_headers or {})} - return await self._post( - "/v1/messages/count_tokens?beta=true", - body=await async_maybe_transform( - { - "messages": messages, - "model": model, - "cache_control": cache_control, - "context_management": context_management, - "mcp_servers": mcp_servers, - "mcp_servers": mcp_servers, - "output_config": merged_output_config, - "output_format": omit, - "speed": speed, - "system": system, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - }, - message_count_tokens_params.MessageCountTokensParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaMessageTokensCount, - ) - - -class MessagesWithRawResponse: - def __init__(self, messages: Messages) -> None: - self._messages = messages - - self.create = _legacy_response.to_raw_response_wrapper( - messages.create, - ) - self.parse = _legacy_response.to_raw_response_wrapper( - messages.parse, - ) - self.count_tokens = _legacy_response.to_raw_response_wrapper( - messages.count_tokens, - ) - - @cached_property - def batches(self) -> BatchesWithRawResponse: - return BatchesWithRawResponse(self._messages.batches) - - -class AsyncMessagesWithRawResponse: - def __init__(self, messages: AsyncMessages) -> None: - self._messages = messages - - self.create = _legacy_response.async_to_raw_response_wrapper( - messages.create, - ) - self.parse = _legacy_response.async_to_raw_response_wrapper( - messages.parse, - ) - self.count_tokens = _legacy_response.async_to_raw_response_wrapper( - messages.count_tokens, - ) - - @cached_property - def batches(self) -> AsyncBatchesWithRawResponse: - return AsyncBatchesWithRawResponse(self._messages.batches) - - -class MessagesWithStreamingResponse: - def __init__(self, messages: Messages) -> None: - self._messages = messages - - self.create = to_streamed_response_wrapper( - messages.create, - ) - self.count_tokens = to_streamed_response_wrapper( - messages.count_tokens, - ) - - @cached_property - def batches(self) -> BatchesWithStreamingResponse: - return BatchesWithStreamingResponse(self._messages.batches) - - -class AsyncMessagesWithStreamingResponse: - def __init__(self, messages: AsyncMessages) -> None: - self._messages = messages - - self.create = async_to_streamed_response_wrapper( - messages.create, - ) - self.count_tokens = async_to_streamed_response_wrapper( - messages.count_tokens, - ) - - @cached_property - def batches(self) -> AsyncBatchesWithStreamingResponse: - return AsyncBatchesWithStreamingResponse(self._messages.batches) - - -def validate_output_format(output_format: object) -> None: - if inspect.isclass(output_format) and issubclass(output_format, pydantic.BaseModel): - raise TypeError( - "You tried to pass a `BaseModel` class to `beta.messages.create()`; You must use `beta.messages.parse()` instead" - ) - - -def _validate_output_config_conflict( - output_config: BetaOutputConfigParam | Omit, - output_format: object, -) -> None: - if is_given(output_format) and output_format is not None and is_given(output_config): - if "format" in output_config and output_config["format"] is not None: - raise AnthropicError( - "Both output_format and output_config.format were provided. " - "Please use only output_config.format (output_format is deprecated).", - ) - - -def _merge_output_configs( - output_config: BetaOutputConfigParam | Omit, - output_format: Optional[BetaJSONOutputFormatParam] | Omit, -) -> BetaOutputConfigParam | Omit: - if is_given(output_format): - if is_given(output_config): - return {**output_config, "format": output_format} - else: - return {"format": output_format} - return output_config - - -def _warn_output_format_deprecated(output_format: object) -> None: - """Emit deprecation warning if output_format is provided.""" - if is_given(output_format) and output_format is not None: - warnings.warn( - "The 'output_format' parameter is deprecated. Please use 'output_config.format' instead.", - DeprecationWarning, - stacklevel=4, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/models.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/models.py deleted file mode 100644 index 716c7c42..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/models.py +++ /dev/null @@ -1,331 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List - -import httpx - -from ... import _legacy_response -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import is_given, path_template, maybe_transform, strip_not_given -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...pagination import SyncPage, AsyncPage -from ...types.beta import model_list_params -from ..._base_client import AsyncPaginator, make_request_options -from ...types.anthropic_beta_param import AnthropicBetaParam -from ...types.beta.beta_model_info import BetaModelInfo - -__all__ = ["Models", "AsyncModels"] - - -class Models(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ModelsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return ModelsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ModelsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return ModelsWithStreamingResponse(self) - - def retrieve( - self, - model_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaModelInfo: - """ - Get a specific model. - - The Models API response can be used to determine information about a specific - model or resolve a model alias to a model ID. - - Args: - model_id: Model identifier or alias. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model_id: - raise ValueError(f"Expected a non-empty value for `model_id` but received {model_id!r}") - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **(extra_headers or {}), - } - return self._get( - path_template("/v1/models/{model_id}?beta=true", model_id=model_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaModelInfo, - ) - - def list( - self, - *, - after_id: str | Omit = omit, - before_id: str | Omit = omit, - limit: int | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPage[BetaModelInfo]: - """ - List available models. - - The Models API response can be used to determine which models are available for - use in the API. More recently released models are listed first. - - Args: - after_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately after this object. - - before_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately before this object. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **(extra_headers or {}), - } - return self._get_api_list( - "/v1/models?beta=true", - page=SyncPage[BetaModelInfo], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after_id": after_id, - "before_id": before_id, - "limit": limit, - }, - model_list_params.ModelListParams, - ), - ), - model=BetaModelInfo, - ) - - -class AsyncModels(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncModelsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncModelsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncModelsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncModelsWithStreamingResponse(self) - - async def retrieve( - self, - model_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaModelInfo: - """ - Get a specific model. - - The Models API response can be used to determine information about a specific - model or resolve a model alias to a model ID. - - Args: - model_id: Model identifier or alias. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model_id: - raise ValueError(f"Expected a non-empty value for `model_id` but received {model_id!r}") - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **(extra_headers or {}), - } - return await self._get( - path_template("/v1/models/{model_id}?beta=true", model_id=model_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaModelInfo, - ) - - def list( - self, - *, - after_id: str | Omit = omit, - before_id: str | Omit = omit, - limit: int | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaModelInfo, AsyncPage[BetaModelInfo]]: - """ - List available models. - - The Models API response can be used to determine which models are available for - use in the API. More recently released models are listed first. - - Args: - after_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately after this object. - - before_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately before this object. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **(extra_headers or {}), - } - return self._get_api_list( - "/v1/models?beta=true", - page=AsyncPage[BetaModelInfo], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after_id": after_id, - "before_id": before_id, - "limit": limit, - }, - model_list_params.ModelListParams, - ), - ), - model=BetaModelInfo, - ) - - -class ModelsWithRawResponse: - def __init__(self, models: Models) -> None: - self._models = models - - self.retrieve = _legacy_response.to_raw_response_wrapper( - models.retrieve, - ) - self.list = _legacy_response.to_raw_response_wrapper( - models.list, - ) - - -class AsyncModelsWithRawResponse: - def __init__(self, models: AsyncModels) -> None: - self._models = models - - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - models.retrieve, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - models.list, - ) - - -class ModelsWithStreamingResponse: - def __init__(self, models: Models) -> None: - self._models = models - - self.retrieve = to_streamed_response_wrapper( - models.retrieve, - ) - self.list = to_streamed_response_wrapper( - models.list, - ) - - -class AsyncModelsWithStreamingResponse: - def __init__(self, models: AsyncModels) -> None: - self._models = models - - self.retrieve = async_to_streamed_response_wrapper( - models.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - models.list, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/__init__.py deleted file mode 100644 index b683e027..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .events import ( - Events, - AsyncEvents, - EventsWithRawResponse, - AsyncEventsWithRawResponse, - EventsWithStreamingResponse, - AsyncEventsWithStreamingResponse, -) -from .sessions import ( - Sessions, - AsyncSessions, - SessionsWithRawResponse, - AsyncSessionsWithRawResponse, - SessionsWithStreamingResponse, - AsyncSessionsWithStreamingResponse, -) -from .resources import ( - Resources, - AsyncResources, - ResourcesWithRawResponse, - AsyncResourcesWithRawResponse, - ResourcesWithStreamingResponse, - AsyncResourcesWithStreamingResponse, -) - -__all__ = [ - "Events", - "AsyncEvents", - "EventsWithRawResponse", - "AsyncEventsWithRawResponse", - "EventsWithStreamingResponse", - "AsyncEventsWithStreamingResponse", - "Resources", - "AsyncResources", - "ResourcesWithRawResponse", - "AsyncResourcesWithRawResponse", - "ResourcesWithStreamingResponse", - "AsyncResourcesWithStreamingResponse", - "Sessions", - "AsyncSessions", - "SessionsWithRawResponse", - "AsyncSessionsWithRawResponse", - "SessionsWithStreamingResponse", - "AsyncSessionsWithStreamingResponse", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/events.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/events.py deleted file mode 100644 index bafd1e93..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/events.py +++ /dev/null @@ -1,475 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Any, List, Iterable, cast -from itertools import chain -from typing_extensions import Literal - -import httpx - -from .... import _legacy_response -from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...._streaming import Stream, AsyncStream -from ....pagination import SyncPageCursor, AsyncPageCursor -from ...._base_client import AsyncPaginator, make_request_options -from ....types.beta.sessions import event_list_params, event_send_params -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.sessions.beta_managed_agents_event_params import BetaManagedAgentsEventParams -from ....types.beta.sessions.beta_managed_agents_session_event import BetaManagedAgentsSessionEvent -from ....types.beta.sessions.beta_managed_agents_send_session_events import BetaManagedAgentsSendSessionEvents -from ....types.beta.sessions.beta_managed_agents_stream_session_events import BetaManagedAgentsStreamSessionEvents - -__all__ = ["Events", "AsyncEvents"] - - -class Events(SyncAPIResource): - @cached_property - def with_raw_response(self) -> EventsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return EventsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> EventsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return EventsWithStreamingResponse(self) - - def list( - self, - session_id: str, - *, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaManagedAgentsSessionEvent]: - """ - List Events - - Args: - limit: Query parameter for limit - - order: Sort direction for results, ordered by created_at. Defaults to asc - (chronological). - - page: Opaque pagination cursor from a previous response's next_page. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/sessions/{session_id}/events?beta=true", session_id=session_id), - page=SyncPageCursor[BetaManagedAgentsSessionEvent], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "order": order, - "page": page, - }, - event_list_params.EventListParams, - ), - ), - model=cast( - Any, BetaManagedAgentsSessionEvent - ), # Union types cannot be passed in as arguments in the type system - ) - - def send( - self, - session_id: str, - *, - events: Iterable[BetaManagedAgentsEventParams], - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsSendSessionEvents: - """ - Send Events - - Args: - events: Events to send to the `session`. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/sessions/{session_id}/events?beta=true", session_id=session_id), - body=maybe_transform({"events": events}, event_send_params.EventSendParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsSendSessionEvents, - ) - - def stream( - self, - session_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Stream[BetaManagedAgentsStreamSessionEvents]: - """ - Stream Events - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get( - path_template("/v1/sessions/{session_id}/events/stream?beta=true", session_id=session_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=cast( - Any, BetaManagedAgentsStreamSessionEvents - ), # Union types cannot be passed in as arguments in the type system - stream=True, - stream_cls=Stream[BetaManagedAgentsStreamSessionEvents], - ) - - -class AsyncEvents(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncEventsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncEventsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncEventsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncEventsWithStreamingResponse(self) - - def list( - self, - session_id: str, - *, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaManagedAgentsSessionEvent, AsyncPageCursor[BetaManagedAgentsSessionEvent]]: - """ - List Events - - Args: - limit: Query parameter for limit - - order: Sort direction for results, ordered by created_at. Defaults to asc - (chronological). - - page: Opaque pagination cursor from a previous response's next_page. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/sessions/{session_id}/events?beta=true", session_id=session_id), - page=AsyncPageCursor[BetaManagedAgentsSessionEvent], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "order": order, - "page": page, - }, - event_list_params.EventListParams, - ), - ), - model=cast( - Any, BetaManagedAgentsSessionEvent - ), # Union types cannot be passed in as arguments in the type system - ) - - async def send( - self, - session_id: str, - *, - events: Iterable[BetaManagedAgentsEventParams], - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsSendSessionEvents: - """ - Send Events - - Args: - events: Events to send to the `session`. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/sessions/{session_id}/events?beta=true", session_id=session_id), - body=await async_maybe_transform({"events": events}, event_send_params.EventSendParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsSendSessionEvents, - ) - - async def stream( - self, - session_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncStream[BetaManagedAgentsStreamSessionEvents]: - """ - Stream Events - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._get( - path_template("/v1/sessions/{session_id}/events/stream?beta=true", session_id=session_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=cast( - Any, BetaManagedAgentsStreamSessionEvents - ), # Union types cannot be passed in as arguments in the type system - stream=True, - stream_cls=AsyncStream[BetaManagedAgentsStreamSessionEvents], - ) - - -class EventsWithRawResponse: - def __init__(self, events: Events) -> None: - self._events = events - - self.list = _legacy_response.to_raw_response_wrapper( - events.list, - ) - self.send = _legacy_response.to_raw_response_wrapper( - events.send, - ) - self.stream = _legacy_response.to_raw_response_wrapper( - events.stream, - ) - - -class AsyncEventsWithRawResponse: - def __init__(self, events: AsyncEvents) -> None: - self._events = events - - self.list = _legacy_response.async_to_raw_response_wrapper( - events.list, - ) - self.send = _legacy_response.async_to_raw_response_wrapper( - events.send, - ) - self.stream = _legacy_response.async_to_raw_response_wrapper( - events.stream, - ) - - -class EventsWithStreamingResponse: - def __init__(self, events: Events) -> None: - self._events = events - - self.list = to_streamed_response_wrapper( - events.list, - ) - self.send = to_streamed_response_wrapper( - events.send, - ) - self.stream = to_streamed_response_wrapper( - events.stream, - ) - - -class AsyncEventsWithStreamingResponse: - def __init__(self, events: AsyncEvents) -> None: - self._events = events - - self.list = async_to_streamed_response_wrapper( - events.list, - ) - self.send = async_to_streamed_response_wrapper( - events.send, - ) - self.stream = async_to_streamed_response_wrapper( - events.stream, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/resources.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/resources.py deleted file mode 100644 index 34fcf1be..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/resources.py +++ /dev/null @@ -1,769 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Any, List, Optional, cast -from itertools import chain -from typing_extensions import Literal - -import httpx - -from .... import _legacy_response -from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ...._base_client import AsyncPaginator, make_request_options -from ....types.beta.sessions import resource_add_params, resource_list_params, resource_update_params -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.sessions.resource_update_response import ResourceUpdateResponse -from ....types.beta.sessions.resource_retrieve_response import ResourceRetrieveResponse -from ....types.beta.sessions.beta_managed_agents_file_resource import BetaManagedAgentsFileResource -from ....types.beta.sessions.beta_managed_agents_session_resource import BetaManagedAgentsSessionResource -from ....types.beta.sessions.beta_managed_agents_delete_session_resource import BetaManagedAgentsDeleteSessionResource - -__all__ = ["Resources", "AsyncResources"] - - -class Resources(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ResourcesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return ResourcesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ResourcesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return ResourcesWithStreamingResponse(self) - - def retrieve( - self, - resource_id: str, - *, - session_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ResourceRetrieveResponse: - """ - Get Session Resource - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - if not resource_id: - raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return cast( - ResourceRetrieveResponse, - self._get( - path_template( - "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", - session_id=session_id, - resource_id=resource_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=cast( - Any, ResourceRetrieveResponse - ), # Union types cannot be passed in as arguments in the type system - ), - ) - - def update( - self, - resource_id: str, - *, - session_id: str, - authorization_token: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ResourceUpdateResponse: - """ - Update Session Resource - - Args: - authorization_token: New authorization token for the resource. Currently only `github_repository` - resources support token rotation. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - if not resource_id: - raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return cast( - ResourceUpdateResponse, - self._post( - path_template( - "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", - session_id=session_id, - resource_id=resource_id, - ), - body=maybe_transform( - {"authorization_token": authorization_token}, resource_update_params.ResourceUpdateParams - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=cast( - Any, ResourceUpdateResponse - ), # Union types cannot be passed in as arguments in the type system - ), - ) - - def list( - self, - session_id: str, - *, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaManagedAgentsSessionResource]: - """ - List Session Resources - - Args: - limit: Maximum number of resources to return per page (max 1000). If omitted, returns - all resources. - - page: Opaque cursor from a previous response's next_page field. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/sessions/{session_id}/resources?beta=true", session_id=session_id), - page=SyncPageCursor[BetaManagedAgentsSessionResource], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "page": page, - }, - resource_list_params.ResourceListParams, - ), - ), - model=cast( - Any, BetaManagedAgentsSessionResource - ), # Union types cannot be passed in as arguments in the type system - ) - - def delete( - self, - resource_id: str, - *, - session_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeleteSessionResource: - """ - Delete Session Resource - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - if not resource_id: - raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._delete( - path_template( - "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", - session_id=session_id, - resource_id=resource_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsDeleteSessionResource, - ) - - def add( - self, - session_id: str, - *, - file_id: str, - type: Literal["file"], - mount_path: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsFileResource: - """ - Add Session Resource - - Args: - file_id: ID of a previously uploaded file. - - mount_path: Mount path in the container. Defaults to `/mnt/session/uploads/`. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/sessions/{session_id}/resources?beta=true", session_id=session_id), - body=maybe_transform( - { - "file_id": file_id, - "type": type, - "mount_path": mount_path, - }, - resource_add_params.ResourceAddParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsFileResource, - ) - - -class AsyncResources(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncResourcesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncResourcesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncResourcesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncResourcesWithStreamingResponse(self) - - async def retrieve( - self, - resource_id: str, - *, - session_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ResourceRetrieveResponse: - """ - Get Session Resource - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - if not resource_id: - raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return cast( - ResourceRetrieveResponse, - await self._get( - path_template( - "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", - session_id=session_id, - resource_id=resource_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=cast( - Any, ResourceRetrieveResponse - ), # Union types cannot be passed in as arguments in the type system - ), - ) - - async def update( - self, - resource_id: str, - *, - session_id: str, - authorization_token: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ResourceUpdateResponse: - """ - Update Session Resource - - Args: - authorization_token: New authorization token for the resource. Currently only `github_repository` - resources support token rotation. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - if not resource_id: - raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return cast( - ResourceUpdateResponse, - await self._post( - path_template( - "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", - session_id=session_id, - resource_id=resource_id, - ), - body=await async_maybe_transform( - {"authorization_token": authorization_token}, resource_update_params.ResourceUpdateParams - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=cast( - Any, ResourceUpdateResponse - ), # Union types cannot be passed in as arguments in the type system - ), - ) - - def list( - self, - session_id: str, - *, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaManagedAgentsSessionResource, AsyncPageCursor[BetaManagedAgentsSessionResource]]: - """ - List Session Resources - - Args: - limit: Maximum number of resources to return per page (max 1000). If omitted, returns - all resources. - - page: Opaque cursor from a previous response's next_page field. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/sessions/{session_id}/resources?beta=true", session_id=session_id), - page=AsyncPageCursor[BetaManagedAgentsSessionResource], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "page": page, - }, - resource_list_params.ResourceListParams, - ), - ), - model=cast( - Any, BetaManagedAgentsSessionResource - ), # Union types cannot be passed in as arguments in the type system - ) - - async def delete( - self, - resource_id: str, - *, - session_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeleteSessionResource: - """ - Delete Session Resource - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - if not resource_id: - raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._delete( - path_template( - "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", - session_id=session_id, - resource_id=resource_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsDeleteSessionResource, - ) - - async def add( - self, - session_id: str, - *, - file_id: str, - type: Literal["file"], - mount_path: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsFileResource: - """ - Add Session Resource - - Args: - file_id: ID of a previously uploaded file. - - mount_path: Mount path in the container. Defaults to `/mnt/session/uploads/`. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/sessions/{session_id}/resources?beta=true", session_id=session_id), - body=await async_maybe_transform( - { - "file_id": file_id, - "type": type, - "mount_path": mount_path, - }, - resource_add_params.ResourceAddParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsFileResource, - ) - - -class ResourcesWithRawResponse: - def __init__(self, resources: Resources) -> None: - self._resources = resources - - self.retrieve = _legacy_response.to_raw_response_wrapper( - resources.retrieve, - ) - self.update = _legacy_response.to_raw_response_wrapper( - resources.update, - ) - self.list = _legacy_response.to_raw_response_wrapper( - resources.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - resources.delete, - ) - self.add = _legacy_response.to_raw_response_wrapper( - resources.add, - ) - - -class AsyncResourcesWithRawResponse: - def __init__(self, resources: AsyncResources) -> None: - self._resources = resources - - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - resources.retrieve, - ) - self.update = _legacy_response.async_to_raw_response_wrapper( - resources.update, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - resources.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - resources.delete, - ) - self.add = _legacy_response.async_to_raw_response_wrapper( - resources.add, - ) - - -class ResourcesWithStreamingResponse: - def __init__(self, resources: Resources) -> None: - self._resources = resources - - self.retrieve = to_streamed_response_wrapper( - resources.retrieve, - ) - self.update = to_streamed_response_wrapper( - resources.update, - ) - self.list = to_streamed_response_wrapper( - resources.list, - ) - self.delete = to_streamed_response_wrapper( - resources.delete, - ) - self.add = to_streamed_response_wrapper( - resources.add, - ) - - -class AsyncResourcesWithStreamingResponse: - def __init__(self, resources: AsyncResources) -> None: - self._resources = resources - - self.retrieve = async_to_streamed_response_wrapper( - resources.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - resources.update, - ) - self.list = async_to_streamed_response_wrapper( - resources.list, - ) - self.delete = async_to_streamed_response_wrapper( - resources.delete, - ) - self.add = async_to_streamed_response_wrapper( - resources.add, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/sessions.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/sessions.py deleted file mode 100644 index 0c069bba..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/sessions/sessions.py +++ /dev/null @@ -1,983 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Union, Iterable, Optional -from datetime import datetime -from itertools import chain -from typing_extensions import Literal - -import httpx - -from .... import _legacy_response -from .events import ( - Events, - AsyncEvents, - EventsWithRawResponse, - AsyncEventsWithRawResponse, - EventsWithStreamingResponse, - AsyncEventsWithStreamingResponse, -) -from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from .resources import ( - Resources, - AsyncResources, - ResourcesWithRawResponse, - AsyncResourcesWithRawResponse, - ResourcesWithStreamingResponse, - AsyncResourcesWithStreamingResponse, -) -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ....types.beta import session_list_params, session_create_params, session_update_params -from ...._base_client import AsyncPaginator, make_request_options -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.beta_managed_agents_session import BetaManagedAgentsSession -from ....types.beta.beta_managed_agents_deleted_session import BetaManagedAgentsDeletedSession - -__all__ = ["Sessions", "AsyncSessions"] - - -class Sessions(SyncAPIResource): - @cached_property - def events(self) -> Events: - return Events(self._client) - - @cached_property - def resources(self) -> Resources: - return Resources(self._client) - - @cached_property - def with_raw_response(self) -> SessionsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return SessionsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SessionsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return SessionsWithStreamingResponse(self) - - def create( - self, - *, - agent: session_create_params.Agent, - environment_id: str, - metadata: Dict[str, str] | Omit = omit, - resources: Iterable[session_create_params.Resource] | Omit = omit, - title: Optional[str] | Omit = omit, - vault_ids: SequenceNotStr[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsSession: - """Create Session - - Args: - agent: Agent identifier. - - Accepts the `agent` ID string, which pins the latest version - for the session, or an `agent` object with both id and version specified. - - environment_id: ID of the `environment` defining the container configuration for this session. - - metadata: Arbitrary key-value metadata attached to the session. Maximum 16 pairs, keys up - to 64 chars, values up to 512 chars. - - resources: Resources (e.g. repositories, files) to mount into the session's container. - - title: Human-readable session title. - - vault_ids: Vault IDs for stored credentials the agent can use during the session. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - "/v1/sessions?beta=true", - body=maybe_transform( - { - "agent": agent, - "environment_id": environment_id, - "metadata": metadata, - "resources": resources, - "title": title, - "vault_ids": vault_ids, - }, - session_create_params.SessionCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsSession, - ) - - def retrieve( - self, - session_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsSession: - """ - Get Session - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get( - path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsSession, - ) - - def update( - self, - session_id: str, - *, - metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, - title: Optional[str] | Omit = omit, - vault_ids: SequenceNotStr[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsSession: - """Update Session - - Args: - metadata: Metadata patch. - - Set a key to a string to upsert it, or to null to delete it. - Omit the field to preserve. - - title: Human-readable session title. - - vault_ids: Vault IDs (`vlt_*`) to attach to the session. Not yet supported; requests - setting this field are rejected. Reserved for future use. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), - body=maybe_transform( - { - "metadata": metadata, - "title": title, - "vault_ids": vault_ids, - }, - session_update_params.SessionUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsSession, - ) - - def list( - self, - *, - agent_id: str | Omit = omit, - agent_version: int | Omit = omit, - created_at_gt: Union[str, datetime] | Omit = omit, - created_at_gte: Union[str, datetime] | Omit = omit, - created_at_lt: Union[str, datetime] | Omit = omit, - created_at_lte: Union[str, datetime] | Omit = omit, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaManagedAgentsSession]: - """ - List Sessions - - Args: - agent_id: Filter sessions created with this agent ID. - - agent_version: Filter by agent version. Only applies when agent_id is also set. - - created_at_gt: Return sessions created after this time (exclusive). - - created_at_gte: Return sessions created at or after this time (inclusive). - - created_at_lt: Return sessions created before this time (exclusive). - - created_at_lte: Return sessions created at or before this time (inclusive). - - include_archived: When true, includes archived sessions. Default: false (exclude archived). - - limit: Maximum number of results to return. - - order: Sort direction for results, ordered by created_at. Defaults to desc (newest - first). - - page: Opaque pagination cursor from a previous response's next_page. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - "/v1/sessions?beta=true", - page=SyncPageCursor[BetaManagedAgentsSession], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "agent_id": agent_id, - "agent_version": agent_version, - "created_at_gt": created_at_gt, - "created_at_gte": created_at_gte, - "created_at_lt": created_at_lt, - "created_at_lte": created_at_lte, - "include_archived": include_archived, - "limit": limit, - "order": order, - "page": page, - }, - session_list_params.SessionListParams, - ), - ), - model=BetaManagedAgentsSession, - ) - - def delete( - self, - session_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeletedSession: - """ - Delete Session - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._delete( - path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsDeletedSession, - ) - - def archive( - self, - session_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsSession: - """ - Archive Session - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/sessions/{session_id}/archive?beta=true", session_id=session_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsSession, - ) - - -class AsyncSessions(AsyncAPIResource): - @cached_property - def events(self) -> AsyncEvents: - return AsyncEvents(self._client) - - @cached_property - def resources(self) -> AsyncResources: - return AsyncResources(self._client) - - @cached_property - def with_raw_response(self) -> AsyncSessionsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncSessionsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncSessionsWithStreamingResponse(self) - - async def create( - self, - *, - agent: session_create_params.Agent, - environment_id: str, - metadata: Dict[str, str] | Omit = omit, - resources: Iterable[session_create_params.Resource] | Omit = omit, - title: Optional[str] | Omit = omit, - vault_ids: SequenceNotStr[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsSession: - """Create Session - - Args: - agent: Agent identifier. - - Accepts the `agent` ID string, which pins the latest version - for the session, or an `agent` object with both id and version specified. - - environment_id: ID of the `environment` defining the container configuration for this session. - - metadata: Arbitrary key-value metadata attached to the session. Maximum 16 pairs, keys up - to 64 chars, values up to 512 chars. - - resources: Resources (e.g. repositories, files) to mount into the session's container. - - title: Human-readable session title. - - vault_ids: Vault IDs for stored credentials the agent can use during the session. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - "/v1/sessions?beta=true", - body=await async_maybe_transform( - { - "agent": agent, - "environment_id": environment_id, - "metadata": metadata, - "resources": resources, - "title": title, - "vault_ids": vault_ids, - }, - session_create_params.SessionCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsSession, - ) - - async def retrieve( - self, - session_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsSession: - """ - Get Session - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._get( - path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsSession, - ) - - async def update( - self, - session_id: str, - *, - metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, - title: Optional[str] | Omit = omit, - vault_ids: SequenceNotStr[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsSession: - """Update Session - - Args: - metadata: Metadata patch. - - Set a key to a string to upsert it, or to null to delete it. - Omit the field to preserve. - - title: Human-readable session title. - - vault_ids: Vault IDs (`vlt_*`) to attach to the session. Not yet supported; requests - setting this field are rejected. Reserved for future use. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), - body=await async_maybe_transform( - { - "metadata": metadata, - "title": title, - "vault_ids": vault_ids, - }, - session_update_params.SessionUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsSession, - ) - - def list( - self, - *, - agent_id: str | Omit = omit, - agent_version: int | Omit = omit, - created_at_gt: Union[str, datetime] | Omit = omit, - created_at_gte: Union[str, datetime] | Omit = omit, - created_at_lt: Union[str, datetime] | Omit = omit, - created_at_lte: Union[str, datetime] | Omit = omit, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaManagedAgentsSession, AsyncPageCursor[BetaManagedAgentsSession]]: - """ - List Sessions - - Args: - agent_id: Filter sessions created with this agent ID. - - agent_version: Filter by agent version. Only applies when agent_id is also set. - - created_at_gt: Return sessions created after this time (exclusive). - - created_at_gte: Return sessions created at or after this time (inclusive). - - created_at_lt: Return sessions created before this time (exclusive). - - created_at_lte: Return sessions created at or before this time (inclusive). - - include_archived: When true, includes archived sessions. Default: false (exclude archived). - - limit: Maximum number of results to return. - - order: Sort direction for results, ordered by created_at. Defaults to desc (newest - first). - - page: Opaque pagination cursor from a previous response's next_page. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - "/v1/sessions?beta=true", - page=AsyncPageCursor[BetaManagedAgentsSession], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "agent_id": agent_id, - "agent_version": agent_version, - "created_at_gt": created_at_gt, - "created_at_gte": created_at_gte, - "created_at_lt": created_at_lt, - "created_at_lte": created_at_lte, - "include_archived": include_archived, - "limit": limit, - "order": order, - "page": page, - }, - session_list_params.SessionListParams, - ), - ), - model=BetaManagedAgentsSession, - ) - - async def delete( - self, - session_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeletedSession: - """ - Delete Session - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._delete( - path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsDeletedSession, - ) - - async def archive( - self, - session_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsSession: - """ - Archive Session - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not session_id: - raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/sessions/{session_id}/archive?beta=true", session_id=session_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsSession, - ) - - -class SessionsWithRawResponse: - def __init__(self, sessions: Sessions) -> None: - self._sessions = sessions - - self.create = _legacy_response.to_raw_response_wrapper( - sessions.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - sessions.retrieve, - ) - self.update = _legacy_response.to_raw_response_wrapper( - sessions.update, - ) - self.list = _legacy_response.to_raw_response_wrapper( - sessions.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - sessions.delete, - ) - self.archive = _legacy_response.to_raw_response_wrapper( - sessions.archive, - ) - - @cached_property - def events(self) -> EventsWithRawResponse: - return EventsWithRawResponse(self._sessions.events) - - @cached_property - def resources(self) -> ResourcesWithRawResponse: - return ResourcesWithRawResponse(self._sessions.resources) - - -class AsyncSessionsWithRawResponse: - def __init__(self, sessions: AsyncSessions) -> None: - self._sessions = sessions - - self.create = _legacy_response.async_to_raw_response_wrapper( - sessions.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - sessions.retrieve, - ) - self.update = _legacy_response.async_to_raw_response_wrapper( - sessions.update, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - sessions.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - sessions.delete, - ) - self.archive = _legacy_response.async_to_raw_response_wrapper( - sessions.archive, - ) - - @cached_property - def events(self) -> AsyncEventsWithRawResponse: - return AsyncEventsWithRawResponse(self._sessions.events) - - @cached_property - def resources(self) -> AsyncResourcesWithRawResponse: - return AsyncResourcesWithRawResponse(self._sessions.resources) - - -class SessionsWithStreamingResponse: - def __init__(self, sessions: Sessions) -> None: - self._sessions = sessions - - self.create = to_streamed_response_wrapper( - sessions.create, - ) - self.retrieve = to_streamed_response_wrapper( - sessions.retrieve, - ) - self.update = to_streamed_response_wrapper( - sessions.update, - ) - self.list = to_streamed_response_wrapper( - sessions.list, - ) - self.delete = to_streamed_response_wrapper( - sessions.delete, - ) - self.archive = to_streamed_response_wrapper( - sessions.archive, - ) - - @cached_property - def events(self) -> EventsWithStreamingResponse: - return EventsWithStreamingResponse(self._sessions.events) - - @cached_property - def resources(self) -> ResourcesWithStreamingResponse: - return ResourcesWithStreamingResponse(self._sessions.resources) - - -class AsyncSessionsWithStreamingResponse: - def __init__(self, sessions: AsyncSessions) -> None: - self._sessions = sessions - - self.create = async_to_streamed_response_wrapper( - sessions.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - sessions.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - sessions.update, - ) - self.list = async_to_streamed_response_wrapper( - sessions.list, - ) - self.delete = async_to_streamed_response_wrapper( - sessions.delete, - ) - self.archive = async_to_streamed_response_wrapper( - sessions.archive, - ) - - @cached_property - def events(self) -> AsyncEventsWithStreamingResponse: - return AsyncEventsWithStreamingResponse(self._sessions.events) - - @cached_property - def resources(self) -> AsyncResourcesWithStreamingResponse: - return AsyncResourcesWithStreamingResponse(self._sessions.resources) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/skills/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/skills/__init__.py deleted file mode 100644 index d1c3ef3e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/skills/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .skills import ( - Skills, - AsyncSkills, - SkillsWithRawResponse, - AsyncSkillsWithRawResponse, - SkillsWithStreamingResponse, - AsyncSkillsWithStreamingResponse, -) -from .versions import ( - Versions, - AsyncVersions, - VersionsWithRawResponse, - AsyncVersionsWithRawResponse, - VersionsWithStreamingResponse, - AsyncVersionsWithStreamingResponse, -) - -__all__ = [ - "Versions", - "AsyncVersions", - "VersionsWithRawResponse", - "AsyncVersionsWithRawResponse", - "VersionsWithStreamingResponse", - "AsyncVersionsWithStreamingResponse", - "Skills", - "AsyncSkills", - "SkillsWithRawResponse", - "AsyncSkillsWithRawResponse", - "SkillsWithStreamingResponse", - "AsyncSkillsWithStreamingResponse", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/skills/skills.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/skills/skills.py deleted file mode 100644 index b2241a6d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/skills/skills.py +++ /dev/null @@ -1,676 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Mapping, Optional, cast -from itertools import chain - -import httpx - -from .... import _legacy_response -from .versions import ( - Versions, - AsyncVersions, - VersionsWithRawResponse, - AsyncVersionsWithRawResponse, - VersionsWithStreamingResponse, - AsyncVersionsWithStreamingResponse, -) -from ...._files import deepcopy_with_paths -from ...._types import ( - Body, - Omit, - Query, - Headers, - NotGiven, - FileTypes, - SequenceNotStr, - omit, - not_given, -) -from ...._utils import is_given, extract_files, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ....types.beta import skill_list_params, skill_create_params -from ...._base_client import AsyncPaginator, make_request_options -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.skill_list_response import SkillListResponse -from ....types.beta.skill_create_response import SkillCreateResponse -from ....types.beta.skill_delete_response import SkillDeleteResponse -from ....types.beta.skill_retrieve_response import SkillRetrieveResponse - -__all__ = ["Skills", "AsyncSkills"] - - -class Skills(SyncAPIResource): - @cached_property - def versions(self) -> Versions: - return Versions(self._client) - - @cached_property - def with_raw_response(self) -> SkillsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return SkillsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SkillsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return SkillsWithStreamingResponse(self) - - def create( - self, - *, - display_title: Optional[str] | Omit = omit, - files: Optional[SequenceNotStr[FileTypes]] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SkillCreateResponse: - """ - Create Skill - - Args: - display_title: Display title for the skill. - - This is a human-readable label that is not included in the prompt sent to the - model. - - files: Files to upload for the skill. - - All files must be in the same top-level directory and must include a SKILL.md - file at the root of that directory. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - body = deepcopy_with_paths( - { - "display_title": display_title, - "files": files, - }, - [["files", ""]], - ) - extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers["Content-Type"] = "multipart/form-data" - return self._post( - "/v1/skills?beta=true", - body=maybe_transform(body, skill_create_params.SkillCreateParams), - files=extracted_files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SkillCreateResponse, - ) - - def retrieve( - self, - skill_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SkillRetrieveResponse: - """ - Get Skill - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return self._get( - path_template("/v1/skills/{skill_id}?beta=true", skill_id=skill_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SkillRetrieveResponse, - ) - - def list( - self, - *, - limit: int | Omit = omit, - page: Optional[str] | Omit = omit, - source: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[SkillListResponse]: - """ - List Skills - - Args: - limit: Number of results to return per page. - - Maximum value is 100. Defaults to 20. - - page: Pagination token for fetching a specific page of results. - - Pass the value from a previous response's `next_page` field to get the next page - of results. - - source: Filter skills by source. - - If provided, only skills from the specified source will be returned: - - - `"custom"`: only return user-created skills - - `"anthropic"`: only return Anthropic-created skills - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return self._get_api_list( - "/v1/skills?beta=true", - page=SyncPageCursor[SkillListResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "page": page, - "source": source, - }, - skill_list_params.SkillListParams, - ), - ), - model=SkillListResponse, - ) - - def delete( - self, - skill_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SkillDeleteResponse: - """ - Delete Skill - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return self._delete( - path_template("/v1/skills/{skill_id}?beta=true", skill_id=skill_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SkillDeleteResponse, - ) - - -class AsyncSkills(AsyncAPIResource): - @cached_property - def versions(self) -> AsyncVersions: - return AsyncVersions(self._client) - - @cached_property - def with_raw_response(self) -> AsyncSkillsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncSkillsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSkillsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncSkillsWithStreamingResponse(self) - - async def create( - self, - *, - display_title: Optional[str] | Omit = omit, - files: Optional[SequenceNotStr[FileTypes]] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SkillCreateResponse: - """ - Create Skill - - Args: - display_title: Display title for the skill. - - This is a human-readable label that is not included in the prompt sent to the - model. - - files: Files to upload for the skill. - - All files must be in the same top-level directory and must include a SKILL.md - file at the root of that directory. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - body = deepcopy_with_paths( - { - "display_title": display_title, - "files": files, - }, - [["files", ""]], - ) - extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers["Content-Type"] = "multipart/form-data" - return await self._post( - "/v1/skills?beta=true", - body=await async_maybe_transform(body, skill_create_params.SkillCreateParams), - files=extracted_files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SkillCreateResponse, - ) - - async def retrieve( - self, - skill_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SkillRetrieveResponse: - """ - Get Skill - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return await self._get( - path_template("/v1/skills/{skill_id}?beta=true", skill_id=skill_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SkillRetrieveResponse, - ) - - def list( - self, - *, - limit: int | Omit = omit, - page: Optional[str] | Omit = omit, - source: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[SkillListResponse, AsyncPageCursor[SkillListResponse]]: - """ - List Skills - - Args: - limit: Number of results to return per page. - - Maximum value is 100. Defaults to 20. - - page: Pagination token for fetching a specific page of results. - - Pass the value from a previous response's `next_page` field to get the next page - of results. - - source: Filter skills by source. - - If provided, only skills from the specified source will be returned: - - - `"custom"`: only return user-created skills - - `"anthropic"`: only return Anthropic-created skills - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return self._get_api_list( - "/v1/skills?beta=true", - page=AsyncPageCursor[SkillListResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "page": page, - "source": source, - }, - skill_list_params.SkillListParams, - ), - ), - model=SkillListResponse, - ) - - async def delete( - self, - skill_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SkillDeleteResponse: - """ - Delete Skill - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return await self._delete( - path_template("/v1/skills/{skill_id}?beta=true", skill_id=skill_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=SkillDeleteResponse, - ) - - -class SkillsWithRawResponse: - def __init__(self, skills: Skills) -> None: - self._skills = skills - - self.create = _legacy_response.to_raw_response_wrapper( - skills.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - skills.retrieve, - ) - self.list = _legacy_response.to_raw_response_wrapper( - skills.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - skills.delete, - ) - - @cached_property - def versions(self) -> VersionsWithRawResponse: - return VersionsWithRawResponse(self._skills.versions) - - -class AsyncSkillsWithRawResponse: - def __init__(self, skills: AsyncSkills) -> None: - self._skills = skills - - self.create = _legacy_response.async_to_raw_response_wrapper( - skills.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - skills.retrieve, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - skills.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - skills.delete, - ) - - @cached_property - def versions(self) -> AsyncVersionsWithRawResponse: - return AsyncVersionsWithRawResponse(self._skills.versions) - - -class SkillsWithStreamingResponse: - def __init__(self, skills: Skills) -> None: - self._skills = skills - - self.create = to_streamed_response_wrapper( - skills.create, - ) - self.retrieve = to_streamed_response_wrapper( - skills.retrieve, - ) - self.list = to_streamed_response_wrapper( - skills.list, - ) - self.delete = to_streamed_response_wrapper( - skills.delete, - ) - - @cached_property - def versions(self) -> VersionsWithStreamingResponse: - return VersionsWithStreamingResponse(self._skills.versions) - - -class AsyncSkillsWithStreamingResponse: - def __init__(self, skills: AsyncSkills) -> None: - self._skills = skills - - self.create = async_to_streamed_response_wrapper( - skills.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - skills.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - skills.list, - ) - self.delete = async_to_streamed_response_wrapper( - skills.delete, - ) - - @cached_property - def versions(self) -> AsyncVersionsWithStreamingResponse: - return AsyncVersionsWithStreamingResponse(self._skills.versions) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/skills/versions.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/skills/versions.py deleted file mode 100644 index d849f45d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/skills/versions.py +++ /dev/null @@ -1,652 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Mapping, Optional, cast -from itertools import chain - -import httpx - -from .... import _legacy_response -from ...._files import deepcopy_with_paths -from ...._types import ( - Body, - Omit, - Query, - Headers, - NotGiven, - FileTypes, - SequenceNotStr, - omit, - not_given, -) -from ...._utils import is_given, extract_files, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ...._base_client import AsyncPaginator, make_request_options -from ....types.beta.skills import version_list_params, version_create_params -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.skills.version_list_response import VersionListResponse -from ....types.beta.skills.version_create_response import VersionCreateResponse -from ....types.beta.skills.version_delete_response import VersionDeleteResponse -from ....types.beta.skills.version_retrieve_response import VersionRetrieveResponse - -__all__ = ["Versions", "AsyncVersions"] - - -class Versions(SyncAPIResource): - @cached_property - def with_raw_response(self) -> VersionsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return VersionsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> VersionsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return VersionsWithStreamingResponse(self) - - def create( - self, - skill_id: str, - *, - files: Optional[SequenceNotStr[FileTypes]] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> VersionCreateResponse: - """ - Create Skill Version - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - files: Files to upload for the skill. - - All files must be in the same top-level directory and must include a SKILL.md - file at the root of that directory. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - body = deepcopy_with_paths({"files": files}, [["files", ""]]) - extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers["Content-Type"] = "multipart/form-data" - return self._post( - path_template("/v1/skills/{skill_id}/versions?beta=true", skill_id=skill_id), - body=maybe_transform(body, version_create_params.VersionCreateParams), - files=extracted_files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=VersionCreateResponse, - ) - - def retrieve( - self, - version: str, - *, - skill_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> VersionRetrieveResponse: - """ - Get Skill Version - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - version: Version identifier for the skill. - - Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - if not version: - raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return self._get( - path_template("/v1/skills/{skill_id}/versions/{version}?beta=true", skill_id=skill_id, version=version), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=VersionRetrieveResponse, - ) - - def list( - self, - skill_id: str, - *, - limit: Optional[int] | Omit = omit, - page: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[VersionListResponse]: - """ - List Skill Versions - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - page: Optionally set to the `next_page` token from the previous response. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/skills/{skill_id}/versions?beta=true", skill_id=skill_id), - page=SyncPageCursor[VersionListResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "page": page, - }, - version_list_params.VersionListParams, - ), - ), - model=VersionListResponse, - ) - - def delete( - self, - version: str, - *, - skill_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> VersionDeleteResponse: - """ - Delete Skill Version - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - version: Version identifier for the skill. - - Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - if not version: - raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return self._delete( - path_template("/v1/skills/{skill_id}/versions/{version}?beta=true", skill_id=skill_id, version=version), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=VersionDeleteResponse, - ) - - -class AsyncVersions(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncVersionsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncVersionsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncVersionsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncVersionsWithStreamingResponse(self) - - async def create( - self, - skill_id: str, - *, - files: Optional[SequenceNotStr[FileTypes]] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> VersionCreateResponse: - """ - Create Skill Version - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - files: Files to upload for the skill. - - All files must be in the same top-level directory and must include a SKILL.md - file at the root of that directory. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - body = deepcopy_with_paths({"files": files}, [["files", ""]]) - extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) - # It should be noted that the actual Content-Type header that will be - # sent to the server will contain a `boundary` parameter, e.g. - # multipart/form-data; boundary=---abc-- - extra_headers["Content-Type"] = "multipart/form-data" - return await self._post( - path_template("/v1/skills/{skill_id}/versions?beta=true", skill_id=skill_id), - body=await async_maybe_transform(body, version_create_params.VersionCreateParams), - files=extracted_files, - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=VersionCreateResponse, - ) - - async def retrieve( - self, - version: str, - *, - skill_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> VersionRetrieveResponse: - """ - Get Skill Version - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - version: Version identifier for the skill. - - Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - if not version: - raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return await self._get( - path_template("/v1/skills/{skill_id}/versions/{version}?beta=true", skill_id=skill_id, version=version), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=VersionRetrieveResponse, - ) - - def list( - self, - skill_id: str, - *, - limit: Optional[int] | Omit = omit, - page: Optional[str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[VersionListResponse, AsyncPageCursor[VersionListResponse]]: - """ - List Skill Versions - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - page: Optionally set to the `next_page` token from the previous response. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/skills/{skill_id}/versions?beta=true", skill_id=skill_id), - page=AsyncPageCursor[VersionListResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "page": page, - }, - version_list_params.VersionListParams, - ), - ), - model=VersionListResponse, - ) - - async def delete( - self, - version: str, - *, - skill_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> VersionDeleteResponse: - """ - Delete Skill Version - - Args: - skill_id: Unique identifier for the skill. - - The format and length of IDs may change over time. - - version: Version identifier for the skill. - - Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not skill_id: - raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") - if not version: - raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} - return await self._delete( - path_template("/v1/skills/{skill_id}/versions/{version}?beta=true", skill_id=skill_id, version=version), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=VersionDeleteResponse, - ) - - -class VersionsWithRawResponse: - def __init__(self, versions: Versions) -> None: - self._versions = versions - - self.create = _legacy_response.to_raw_response_wrapper( - versions.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - versions.retrieve, - ) - self.list = _legacy_response.to_raw_response_wrapper( - versions.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - versions.delete, - ) - - -class AsyncVersionsWithRawResponse: - def __init__(self, versions: AsyncVersions) -> None: - self._versions = versions - - self.create = _legacy_response.async_to_raw_response_wrapper( - versions.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - versions.retrieve, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - versions.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - versions.delete, - ) - - -class VersionsWithStreamingResponse: - def __init__(self, versions: Versions) -> None: - self._versions = versions - - self.create = to_streamed_response_wrapper( - versions.create, - ) - self.retrieve = to_streamed_response_wrapper( - versions.retrieve, - ) - self.list = to_streamed_response_wrapper( - versions.list, - ) - self.delete = to_streamed_response_wrapper( - versions.delete, - ) - - -class AsyncVersionsWithStreamingResponse: - def __init__(self, versions: AsyncVersions) -> None: - self._versions = versions - - self.create = async_to_streamed_response_wrapper( - versions.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - versions.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - versions.list, - ) - self.delete = async_to_streamed_response_wrapper( - versions.delete, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/user_profiles.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/user_profiles.py deleted file mode 100644 index f79e2d5c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/user_profiles.py +++ /dev/null @@ -1,720 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from itertools import chain -from typing_extensions import Literal - -import httpx - -from ... import _legacy_response -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...pagination import SyncPageCursor, AsyncPageCursor -from ...types.beta import user_profile_list_params, user_profile_create_params, user_profile_update_params -from ..._base_client import AsyncPaginator, make_request_options -from ...types.anthropic_beta_param import AnthropicBetaParam -from ...types.beta.beta_user_profile import BetaUserProfile -from ...types.beta.beta_user_profile_enrollment_url import BetaUserProfileEnrollmentURL - -__all__ = ["UserProfiles", "AsyncUserProfiles"] - - -class UserProfiles(SyncAPIResource): - @cached_property - def with_raw_response(self) -> UserProfilesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return UserProfilesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> UserProfilesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return UserProfilesWithStreamingResponse(self) - - def create( - self, - *, - external_id: Optional[str] | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaUserProfile: - """ - Create User Profile - - Args: - external_id: Platform's own identifier for this user. Not enforced unique. Maximum 255 - characters. - - metadata: Free-form key-value data to attach to this user profile. Maximum 16 keys, with - keys up to 64 characters and values up to 512 characters. Values must be - non-empty strings. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} - return self._post( - "/v1/user_profiles?beta=true", - body=maybe_transform( - { - "external_id": external_id, - "metadata": metadata, - }, - user_profile_create_params.UserProfileCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaUserProfile, - ) - - def retrieve( - self, - user_profile_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaUserProfile: - """ - Get User Profile - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not user_profile_id: - raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} - return self._get( - path_template("/v1/user_profiles/{user_profile_id}?beta=true", user_profile_id=user_profile_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaUserProfile, - ) - - def update( - self, - user_profile_id: str, - *, - external_id: Optional[str] | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaUserProfile: - """ - Update User Profile - - Args: - external_id: If present, replaces the stored external_id. Omit to leave unchanged. Maximum - 255 characters. - - metadata: Key-value pairs to merge into the stored metadata. Keys provided overwrite - existing values. To remove a key, set its value to an empty string. Keys not - provided are left unchanged. Maximum 16 keys, with keys up to 64 characters and - values up to 512 characters. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not user_profile_id: - raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} - return self._post( - path_template("/v1/user_profiles/{user_profile_id}?beta=true", user_profile_id=user_profile_id), - body=maybe_transform( - { - "external_id": external_id, - "metadata": metadata, - }, - user_profile_update_params.UserProfileUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaUserProfile, - ) - - def list( - self, - *, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaUserProfile]: - """ - List User Profiles - - Args: - limit: Query parameter for limit - - order: Query parameter for order - - page: Query parameter for page - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} - return self._get_api_list( - "/v1/user_profiles?beta=true", - page=SyncPageCursor[BetaUserProfile], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "order": order, - "page": page, - }, - user_profile_list_params.UserProfileListParams, - ), - ), - model=BetaUserProfile, - ) - - def create_enrollment_url( - self, - user_profile_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaUserProfileEnrollmentURL: - """ - Create Enrollment URL - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not user_profile_id: - raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} - return self._post( - path_template( - "/v1/user_profiles/{user_profile_id}/enrollment_url?beta=true", user_profile_id=user_profile_id - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaUserProfileEnrollmentURL, - ) - - -class AsyncUserProfiles(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncUserProfilesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncUserProfilesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncUserProfilesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncUserProfilesWithStreamingResponse(self) - - async def create( - self, - *, - external_id: Optional[str] | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaUserProfile: - """ - Create User Profile - - Args: - external_id: Platform's own identifier for this user. Not enforced unique. Maximum 255 - characters. - - metadata: Free-form key-value data to attach to this user profile. Maximum 16 keys, with - keys up to 64 characters and values up to 512 characters. Values must be - non-empty strings. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} - return await self._post( - "/v1/user_profiles?beta=true", - body=await async_maybe_transform( - { - "external_id": external_id, - "metadata": metadata, - }, - user_profile_create_params.UserProfileCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaUserProfile, - ) - - async def retrieve( - self, - user_profile_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaUserProfile: - """ - Get User Profile - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not user_profile_id: - raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} - return await self._get( - path_template("/v1/user_profiles/{user_profile_id}?beta=true", user_profile_id=user_profile_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaUserProfile, - ) - - async def update( - self, - user_profile_id: str, - *, - external_id: Optional[str] | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaUserProfile: - """ - Update User Profile - - Args: - external_id: If present, replaces the stored external_id. Omit to leave unchanged. Maximum - 255 characters. - - metadata: Key-value pairs to merge into the stored metadata. Keys provided overwrite - existing values. To remove a key, set its value to an empty string. Keys not - provided are left unchanged. Maximum 16 keys, with keys up to 64 characters and - values up to 512 characters. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not user_profile_id: - raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} - return await self._post( - path_template("/v1/user_profiles/{user_profile_id}?beta=true", user_profile_id=user_profile_id), - body=await async_maybe_transform( - { - "external_id": external_id, - "metadata": metadata, - }, - user_profile_update_params.UserProfileUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaUserProfile, - ) - - def list( - self, - *, - limit: int | Omit = omit, - order: Literal["asc", "desc"] | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaUserProfile, AsyncPageCursor[BetaUserProfile]]: - """ - List User Profiles - - Args: - limit: Query parameter for limit - - order: Query parameter for order - - page: Query parameter for page - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} - return self._get_api_list( - "/v1/user_profiles?beta=true", - page=AsyncPageCursor[BetaUserProfile], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "order": order, - "page": page, - }, - user_profile_list_params.UserProfileListParams, - ), - ), - model=BetaUserProfile, - ) - - async def create_enrollment_url( - self, - user_profile_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaUserProfileEnrollmentURL: - """ - Create Enrollment URL - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not user_profile_id: - raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} - return await self._post( - path_template( - "/v1/user_profiles/{user_profile_id}/enrollment_url?beta=true", user_profile_id=user_profile_id - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaUserProfileEnrollmentURL, - ) - - -class UserProfilesWithRawResponse: - def __init__(self, user_profiles: UserProfiles) -> None: - self._user_profiles = user_profiles - - self.create = _legacy_response.to_raw_response_wrapper( - user_profiles.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - user_profiles.retrieve, - ) - self.update = _legacy_response.to_raw_response_wrapper( - user_profiles.update, - ) - self.list = _legacy_response.to_raw_response_wrapper( - user_profiles.list, - ) - self.create_enrollment_url = _legacy_response.to_raw_response_wrapper( - user_profiles.create_enrollment_url, - ) - - -class AsyncUserProfilesWithRawResponse: - def __init__(self, user_profiles: AsyncUserProfiles) -> None: - self._user_profiles = user_profiles - - self.create = _legacy_response.async_to_raw_response_wrapper( - user_profiles.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - user_profiles.retrieve, - ) - self.update = _legacy_response.async_to_raw_response_wrapper( - user_profiles.update, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - user_profiles.list, - ) - self.create_enrollment_url = _legacy_response.async_to_raw_response_wrapper( - user_profiles.create_enrollment_url, - ) - - -class UserProfilesWithStreamingResponse: - def __init__(self, user_profiles: UserProfiles) -> None: - self._user_profiles = user_profiles - - self.create = to_streamed_response_wrapper( - user_profiles.create, - ) - self.retrieve = to_streamed_response_wrapper( - user_profiles.retrieve, - ) - self.update = to_streamed_response_wrapper( - user_profiles.update, - ) - self.list = to_streamed_response_wrapper( - user_profiles.list, - ) - self.create_enrollment_url = to_streamed_response_wrapper( - user_profiles.create_enrollment_url, - ) - - -class AsyncUserProfilesWithStreamingResponse: - def __init__(self, user_profiles: AsyncUserProfiles) -> None: - self._user_profiles = user_profiles - - self.create = async_to_streamed_response_wrapper( - user_profiles.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - user_profiles.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - user_profiles.update, - ) - self.list = async_to_streamed_response_wrapper( - user_profiles.list, - ) - self.create_enrollment_url = async_to_streamed_response_wrapper( - user_profiles.create_enrollment_url, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/vaults/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/vaults/__init__.py deleted file mode 100644 index 6728ac6b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/vaults/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .vaults import ( - Vaults, - AsyncVaults, - VaultsWithRawResponse, - AsyncVaultsWithRawResponse, - VaultsWithStreamingResponse, - AsyncVaultsWithStreamingResponse, -) -from .credentials import ( - Credentials, - AsyncCredentials, - CredentialsWithRawResponse, - AsyncCredentialsWithRawResponse, - CredentialsWithStreamingResponse, - AsyncCredentialsWithStreamingResponse, -) - -__all__ = [ - "Credentials", - "AsyncCredentials", - "CredentialsWithRawResponse", - "AsyncCredentialsWithRawResponse", - "CredentialsWithStreamingResponse", - "AsyncCredentialsWithStreamingResponse", - "Vaults", - "AsyncVaults", - "VaultsWithRawResponse", - "AsyncVaultsWithRawResponse", - "VaultsWithStreamingResponse", - "AsyncVaultsWithStreamingResponse", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/vaults/credentials.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/vaults/credentials.py deleted file mode 100644 index b97ed2a5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/vaults/credentials.py +++ /dev/null @@ -1,895 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from itertools import chain - -import httpx - -from .... import _legacy_response -from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ...._base_client import AsyncPaginator, make_request_options -from ....types.beta.vaults import credential_list_params, credential_create_params, credential_update_params -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.vaults.beta_managed_agents_credential import BetaManagedAgentsCredential -from ....types.beta.vaults.beta_managed_agents_deleted_credential import BetaManagedAgentsDeletedCredential - -__all__ = ["Credentials", "AsyncCredentials"] - - -class Credentials(SyncAPIResource): - @cached_property - def with_raw_response(self) -> CredentialsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return CredentialsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CredentialsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return CredentialsWithStreamingResponse(self) - - def create( - self, - vault_id: str, - *, - auth: credential_create_params.Auth, - display_name: Optional[str] | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsCredential: - """ - Create Credential - - Args: - auth: Authentication details for creating a credential. - - display_name: Human-readable name for the credential. Up to 255 characters. - - metadata: Arbitrary key-value metadata to attach to the credential. Maximum 16 pairs, keys - up to 64 chars, values up to 512 chars. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/vaults/{vault_id}/credentials?beta=true", vault_id=vault_id), - body=maybe_transform( - { - "auth": auth, - "display_name": display_name, - "metadata": metadata, - }, - credential_create_params.CredentialCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsCredential, - ) - - def retrieve( - self, - credential_id: str, - *, - vault_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsCredential: - """ - Get Credential - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - if not credential_id: - raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get( - path_template( - "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", - vault_id=vault_id, - credential_id=credential_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsCredential, - ) - - def update( - self, - credential_id: str, - *, - vault_id: str, - auth: credential_update_params.Auth | Omit = omit, - display_name: Optional[str] | Omit = omit, - metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsCredential: - """ - Update Credential - - Args: - auth: Updated authentication details for a credential. - - display_name: Updated human-readable name for the credential. 1-255 characters. - - metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. - Omitted keys are preserved. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - if not credential_id: - raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template( - "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", - vault_id=vault_id, - credential_id=credential_id, - ), - body=maybe_transform( - { - "auth": auth, - "display_name": display_name, - "metadata": metadata, - }, - credential_update_params.CredentialUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsCredential, - ) - - def list( - self, - vault_id: str, - *, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaManagedAgentsCredential]: - """ - List Credentials - - Args: - include_archived: Whether to include archived credentials in the results. - - limit: Maximum number of credentials to return per page. Defaults to 20, maximum 100. - - page: Opaque pagination token from a previous `list_credentials` response. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/vaults/{vault_id}/credentials?beta=true", vault_id=vault_id), - page=SyncPageCursor[BetaManagedAgentsCredential], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "include_archived": include_archived, - "limit": limit, - "page": page, - }, - credential_list_params.CredentialListParams, - ), - ), - model=BetaManagedAgentsCredential, - ) - - def delete( - self, - credential_id: str, - *, - vault_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeletedCredential: - """ - Delete Credential - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - if not credential_id: - raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._delete( - path_template( - "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", - vault_id=vault_id, - credential_id=credential_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsDeletedCredential, - ) - - def archive( - self, - credential_id: str, - *, - vault_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsCredential: - """ - Archive Credential - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - if not credential_id: - raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template( - "/v1/vaults/{vault_id}/credentials/{credential_id}/archive?beta=true", - vault_id=vault_id, - credential_id=credential_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsCredential, - ) - - -class AsyncCredentials(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncCredentialsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncCredentialsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCredentialsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncCredentialsWithStreamingResponse(self) - - async def create( - self, - vault_id: str, - *, - auth: credential_create_params.Auth, - display_name: Optional[str] | Omit = omit, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsCredential: - """ - Create Credential - - Args: - auth: Authentication details for creating a credential. - - display_name: Human-readable name for the credential. Up to 255 characters. - - metadata: Arbitrary key-value metadata to attach to the credential. Maximum 16 pairs, keys - up to 64 chars, values up to 512 chars. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/vaults/{vault_id}/credentials?beta=true", vault_id=vault_id), - body=await async_maybe_transform( - { - "auth": auth, - "display_name": display_name, - "metadata": metadata, - }, - credential_create_params.CredentialCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsCredential, - ) - - async def retrieve( - self, - credential_id: str, - *, - vault_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsCredential: - """ - Get Credential - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - if not credential_id: - raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._get( - path_template( - "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", - vault_id=vault_id, - credential_id=credential_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsCredential, - ) - - async def update( - self, - credential_id: str, - *, - vault_id: str, - auth: credential_update_params.Auth | Omit = omit, - display_name: Optional[str] | Omit = omit, - metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsCredential: - """ - Update Credential - - Args: - auth: Updated authentication details for a credential. - - display_name: Updated human-readable name for the credential. 1-255 characters. - - metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. - Omitted keys are preserved. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - if not credential_id: - raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template( - "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", - vault_id=vault_id, - credential_id=credential_id, - ), - body=await async_maybe_transform( - { - "auth": auth, - "display_name": display_name, - "metadata": metadata, - }, - credential_update_params.CredentialUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsCredential, - ) - - def list( - self, - vault_id: str, - *, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaManagedAgentsCredential, AsyncPageCursor[BetaManagedAgentsCredential]]: - """ - List Credentials - - Args: - include_archived: Whether to include archived credentials in the results. - - limit: Maximum number of credentials to return per page. Defaults to 20, maximum 100. - - page: Opaque pagination token from a previous `list_credentials` response. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - path_template("/v1/vaults/{vault_id}/credentials?beta=true", vault_id=vault_id), - page=AsyncPageCursor[BetaManagedAgentsCredential], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "include_archived": include_archived, - "limit": limit, - "page": page, - }, - credential_list_params.CredentialListParams, - ), - ), - model=BetaManagedAgentsCredential, - ) - - async def delete( - self, - credential_id: str, - *, - vault_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeletedCredential: - """ - Delete Credential - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - if not credential_id: - raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._delete( - path_template( - "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", - vault_id=vault_id, - credential_id=credential_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsDeletedCredential, - ) - - async def archive( - self, - credential_id: str, - *, - vault_id: str, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsCredential: - """ - Archive Credential - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - if not credential_id: - raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template( - "/v1/vaults/{vault_id}/credentials/{credential_id}/archive?beta=true", - vault_id=vault_id, - credential_id=credential_id, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsCredential, - ) - - -class CredentialsWithRawResponse: - def __init__(self, credentials: Credentials) -> None: - self._credentials = credentials - - self.create = _legacy_response.to_raw_response_wrapper( - credentials.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - credentials.retrieve, - ) - self.update = _legacy_response.to_raw_response_wrapper( - credentials.update, - ) - self.list = _legacy_response.to_raw_response_wrapper( - credentials.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - credentials.delete, - ) - self.archive = _legacy_response.to_raw_response_wrapper( - credentials.archive, - ) - - -class AsyncCredentialsWithRawResponse: - def __init__(self, credentials: AsyncCredentials) -> None: - self._credentials = credentials - - self.create = _legacy_response.async_to_raw_response_wrapper( - credentials.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - credentials.retrieve, - ) - self.update = _legacy_response.async_to_raw_response_wrapper( - credentials.update, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - credentials.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - credentials.delete, - ) - self.archive = _legacy_response.async_to_raw_response_wrapper( - credentials.archive, - ) - - -class CredentialsWithStreamingResponse: - def __init__(self, credentials: Credentials) -> None: - self._credentials = credentials - - self.create = to_streamed_response_wrapper( - credentials.create, - ) - self.retrieve = to_streamed_response_wrapper( - credentials.retrieve, - ) - self.update = to_streamed_response_wrapper( - credentials.update, - ) - self.list = to_streamed_response_wrapper( - credentials.list, - ) - self.delete = to_streamed_response_wrapper( - credentials.delete, - ) - self.archive = to_streamed_response_wrapper( - credentials.archive, - ) - - -class AsyncCredentialsWithStreamingResponse: - def __init__(self, credentials: AsyncCredentials) -> None: - self._credentials = credentials - - self.create = async_to_streamed_response_wrapper( - credentials.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - credentials.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - credentials.update, - ) - self.list = async_to_streamed_response_wrapper( - credentials.list, - ) - self.delete = async_to_streamed_response_wrapper( - credentials.delete, - ) - self.archive = async_to_streamed_response_wrapper( - credentials.archive, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/vaults/vaults.py b/.venv/lib/python3.12/site-packages/anthropic/resources/beta/vaults/vaults.py deleted file mode 100644 index 4990a15a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/beta/vaults/vaults.py +++ /dev/null @@ -1,847 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from itertools import chain - -import httpx - -from .... import _legacy_response -from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform -from ...._compat import cached_property -from .credentials import ( - Credentials, - AsyncCredentials, - CredentialsWithRawResponse, - AsyncCredentialsWithRawResponse, - CredentialsWithStreamingResponse, - AsyncCredentialsWithStreamingResponse, -) -from ...._resource import SyncAPIResource, AsyncAPIResource -from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ....pagination import SyncPageCursor, AsyncPageCursor -from ....types.beta import vault_list_params, vault_create_params, vault_update_params -from ...._base_client import AsyncPaginator, make_request_options -from ....types.anthropic_beta_param import AnthropicBetaParam -from ....types.beta.beta_managed_agents_vault import BetaManagedAgentsVault -from ....types.beta.beta_managed_agents_deleted_vault import BetaManagedAgentsDeletedVault - -__all__ = ["Vaults", "AsyncVaults"] - - -class Vaults(SyncAPIResource): - @cached_property - def credentials(self) -> Credentials: - return Credentials(self._client) - - @cached_property - def with_raw_response(self) -> VaultsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return VaultsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> VaultsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return VaultsWithStreamingResponse(self) - - def create( - self, - *, - display_name: str, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsVault: - """Create Vault - - Args: - display_name: Human-readable name for the vault. - - 1-255 characters. - - metadata: Arbitrary key-value metadata to attach to the vault. Maximum 16 pairs, keys up - to 64 chars, values up to 512 chars. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - "/v1/vaults?beta=true", - body=maybe_transform( - { - "display_name": display_name, - "metadata": metadata, - }, - vault_create_params.VaultCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsVault, - ) - - def retrieve( - self, - vault_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsVault: - """ - Get Vault - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get( - path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsVault, - ) - - def update( - self, - vault_id: str, - *, - display_name: Optional[str] | Omit = omit, - metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsVault: - """Update Vault - - Args: - display_name: Updated human-readable name for the vault. - - 1-255 characters. - - metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. - Omitted keys are preserved. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), - body=maybe_transform( - { - "display_name": display_name, - "metadata": metadata, - }, - vault_update_params.VaultUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsVault, - ) - - def list( - self, - *, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPageCursor[BetaManagedAgentsVault]: - """ - List Vaults - - Args: - include_archived: Whether to include archived vaults in the results. - - limit: Maximum number of vaults to return per page. Defaults to 20, maximum 100. - - page: Opaque pagination token from a previous `list_vaults` response. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - "/v1/vaults?beta=true", - page=SyncPageCursor[BetaManagedAgentsVault], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "include_archived": include_archived, - "limit": limit, - "page": page, - }, - vault_list_params.VaultListParams, - ), - ), - model=BetaManagedAgentsVault, - ) - - def delete( - self, - vault_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeletedVault: - """ - Delete Vault - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._delete( - path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsDeletedVault, - ) - - def archive( - self, - vault_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsVault: - """ - Archive Vault - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._post( - path_template("/v1/vaults/{vault_id}/archive?beta=true", vault_id=vault_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsVault, - ) - - -class AsyncVaults(AsyncAPIResource): - @cached_property - def credentials(self) -> AsyncCredentials: - return AsyncCredentials(self._client) - - @cached_property - def with_raw_response(self) -> AsyncVaultsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncVaultsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncVaultsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncVaultsWithStreamingResponse(self) - - async def create( - self, - *, - display_name: str, - metadata: Dict[str, str] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsVault: - """Create Vault - - Args: - display_name: Human-readable name for the vault. - - 1-255 characters. - - metadata: Arbitrary key-value metadata to attach to the vault. Maximum 16 pairs, keys up - to 64 chars, values up to 512 chars. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - "/v1/vaults?beta=true", - body=await async_maybe_transform( - { - "display_name": display_name, - "metadata": metadata, - }, - vault_create_params.VaultCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsVault, - ) - - async def retrieve( - self, - vault_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsVault: - """ - Get Vault - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._get( - path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsVault, - ) - - async def update( - self, - vault_id: str, - *, - display_name: Optional[str] | Omit = omit, - metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsVault: - """Update Vault - - Args: - display_name: Updated human-readable name for the vault. - - 1-255 characters. - - metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. - Omitted keys are preserved. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), - body=await async_maybe_transform( - { - "display_name": display_name, - "metadata": metadata, - }, - vault_update_params.VaultUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsVault, - ) - - def list( - self, - *, - include_archived: bool | Omit = omit, - limit: int | Omit = omit, - page: str | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[BetaManagedAgentsVault, AsyncPageCursor[BetaManagedAgentsVault]]: - """ - List Vaults - - Args: - include_archived: Whether to include archived vaults in the results. - - limit: Maximum number of vaults to return per page. Defaults to 20, maximum 100. - - page: Opaque pagination token from a previous `list_vaults` response. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return self._get_api_list( - "/v1/vaults?beta=true", - page=AsyncPageCursor[BetaManagedAgentsVault], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "include_archived": include_archived, - "limit": limit, - "page": page, - }, - vault_list_params.VaultListParams, - ), - ), - model=BetaManagedAgentsVault, - ) - - async def delete( - self, - vault_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsDeletedVault: - """ - Delete Vault - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._delete( - path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsDeletedVault, - ) - - async def archive( - self, - vault_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> BetaManagedAgentsVault: - """ - Archive Vault - - Args: - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not vault_id: - raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") - extra_headers = { - **strip_not_given( - { - "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) - if is_given(betas) - else not_given - } - ), - **(extra_headers or {}), - } - extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} - return await self._post( - path_template("/v1/vaults/{vault_id}/archive?beta=true", vault_id=vault_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=BetaManagedAgentsVault, - ) - - -class VaultsWithRawResponse: - def __init__(self, vaults: Vaults) -> None: - self._vaults = vaults - - self.create = _legacy_response.to_raw_response_wrapper( - vaults.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - vaults.retrieve, - ) - self.update = _legacy_response.to_raw_response_wrapper( - vaults.update, - ) - self.list = _legacy_response.to_raw_response_wrapper( - vaults.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - vaults.delete, - ) - self.archive = _legacy_response.to_raw_response_wrapper( - vaults.archive, - ) - - @cached_property - def credentials(self) -> CredentialsWithRawResponse: - return CredentialsWithRawResponse(self._vaults.credentials) - - -class AsyncVaultsWithRawResponse: - def __init__(self, vaults: AsyncVaults) -> None: - self._vaults = vaults - - self.create = _legacy_response.async_to_raw_response_wrapper( - vaults.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - vaults.retrieve, - ) - self.update = _legacy_response.async_to_raw_response_wrapper( - vaults.update, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - vaults.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - vaults.delete, - ) - self.archive = _legacy_response.async_to_raw_response_wrapper( - vaults.archive, - ) - - @cached_property - def credentials(self) -> AsyncCredentialsWithRawResponse: - return AsyncCredentialsWithRawResponse(self._vaults.credentials) - - -class VaultsWithStreamingResponse: - def __init__(self, vaults: Vaults) -> None: - self._vaults = vaults - - self.create = to_streamed_response_wrapper( - vaults.create, - ) - self.retrieve = to_streamed_response_wrapper( - vaults.retrieve, - ) - self.update = to_streamed_response_wrapper( - vaults.update, - ) - self.list = to_streamed_response_wrapper( - vaults.list, - ) - self.delete = to_streamed_response_wrapper( - vaults.delete, - ) - self.archive = to_streamed_response_wrapper( - vaults.archive, - ) - - @cached_property - def credentials(self) -> CredentialsWithStreamingResponse: - return CredentialsWithStreamingResponse(self._vaults.credentials) - - -class AsyncVaultsWithStreamingResponse: - def __init__(self, vaults: AsyncVaults) -> None: - self._vaults = vaults - - self.create = async_to_streamed_response_wrapper( - vaults.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - vaults.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - vaults.update, - ) - self.list = async_to_streamed_response_wrapper( - vaults.list, - ) - self.delete = async_to_streamed_response_wrapper( - vaults.delete, - ) - self.archive = async_to_streamed_response_wrapper( - vaults.archive, - ) - - @cached_property - def credentials(self) -> AsyncCredentialsWithStreamingResponse: - return AsyncCredentialsWithStreamingResponse(self._vaults.credentials) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/completions.py b/.venv/lib/python3.12/site-packages/anthropic/resources/completions.py deleted file mode 100644 index 6162e168..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/completions.py +++ /dev/null @@ -1,827 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Literal, overload - -import httpx - -from .. import _legacy_response -from ..types import completion_create_params -from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from .._utils import is_given, required_args, maybe_transform, strip_not_given, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from .._constants import DEFAULT_TIMEOUT -from .._streaming import Stream, AsyncStream -from .._base_client import make_request_options -from ..types.completion import Completion -from ..types.model_param import ModelParam -from ..types.metadata_param import MetadataParam -from ..types.anthropic_beta_param import AnthropicBetaParam - -__all__ = ["Completions", "AsyncCompletions"] - - -class Completions(SyncAPIResource): - @cached_property - def with_raw_response(self) -> CompletionsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return CompletionsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CompletionsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return CompletionsWithStreamingResponse(self) - - @overload - def create( - self, - *, - max_tokens_to_sample: int, - model: ModelParam, - prompt: str, - metadata: MetadataParam | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Completion: - """[Legacy] Create a Text Completion. - - The Text Completions API is a legacy API. - - We recommend using the - [Messages API](https://docs.claude.com/en/api/messages) going forward. - - Future models and features will not be compatible with Text Completions. See our - [migration guide](https://docs.claude.com/en/api/migrating-from-text-completions-to-messages) - for guidance in migrating from Text Completions to Messages. - - Args: - max_tokens_to_sample: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - prompt: The prompt that you want Claude to complete. - - For proper response generation you will need to format your prompt using - alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: - - ``` - "\n\nHuman: {userQuestion}\n\nAssistant:" - ``` - - See [prompt validation](https://docs.claude.com/en/api/prompt-validation) and - our guide to [prompt design](https://docs.claude.com/en/docs/intro-to-prompting) - for more details. - - metadata: An object describing metadata about the request. - - stop_sequences: Sequences that will cause the model to stop generating. - - Our models stop on `"\n\nHuman:"`, and may include additional built-in stop - sequences in the future. By providing the stop_sequences parameter, you may - include additional strings that will cause the model to stop generating. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/streaming) for details. - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - def create( - self, - *, - max_tokens_to_sample: int, - model: ModelParam, - prompt: str, - stream: Literal[True], - metadata: MetadataParam | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Stream[Completion]: - """[Legacy] Create a Text Completion. - - The Text Completions API is a legacy API. - - We recommend using the - [Messages API](https://docs.claude.com/en/api/messages) going forward. - - Future models and features will not be compatible with Text Completions. See our - [migration guide](https://docs.claude.com/en/api/migrating-from-text-completions-to-messages) - for guidance in migrating from Text Completions to Messages. - - Args: - max_tokens_to_sample: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - prompt: The prompt that you want Claude to complete. - - For proper response generation you will need to format your prompt using - alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: - - ``` - "\n\nHuman: {userQuestion}\n\nAssistant:" - ``` - - See [prompt validation](https://docs.claude.com/en/api/prompt-validation) and - our guide to [prompt design](https://docs.claude.com/en/docs/intro-to-prompting) - for more details. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/streaming) for details. - - metadata: An object describing metadata about the request. - - stop_sequences: Sequences that will cause the model to stop generating. - - Our models stop on `"\n\nHuman:"`, and may include additional built-in stop - sequences in the future. By providing the stop_sequences parameter, you may - include additional strings that will cause the model to stop generating. - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - def create( - self, - *, - max_tokens_to_sample: int, - model: ModelParam, - prompt: str, - stream: bool, - metadata: MetadataParam | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Completion | Stream[Completion]: - """[Legacy] Create a Text Completion. - - The Text Completions API is a legacy API. - - We recommend using the - [Messages API](https://docs.claude.com/en/api/messages) going forward. - - Future models and features will not be compatible with Text Completions. See our - [migration guide](https://docs.claude.com/en/api/migrating-from-text-completions-to-messages) - for guidance in migrating from Text Completions to Messages. - - Args: - max_tokens_to_sample: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - prompt: The prompt that you want Claude to complete. - - For proper response generation you will need to format your prompt using - alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: - - ``` - "\n\nHuman: {userQuestion}\n\nAssistant:" - ``` - - See [prompt validation](https://docs.claude.com/en/api/prompt-validation) and - our guide to [prompt design](https://docs.claude.com/en/docs/intro-to-prompting) - for more details. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/streaming) for details. - - metadata: An object describing metadata about the request. - - stop_sequences: Sequences that will cause the model to stop generating. - - Our models stop on `"\n\nHuman:"`, and may include additional built-in stop - sequences in the future. By providing the stop_sequences parameter, you may - include additional strings that will cause the model to stop generating. - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @required_args(["max_tokens_to_sample", "model", "prompt"], ["max_tokens_to_sample", "model", "prompt", "stream"]) - def create( - self, - *, - max_tokens_to_sample: int, - model: ModelParam, - prompt: str, - metadata: MetadataParam | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Literal[True] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Completion | Stream[Completion]: - if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: - timeout = 600 - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **(extra_headers or {}), - } - return self._post( - "/v1/complete", - body=maybe_transform( - { - "max_tokens_to_sample": max_tokens_to_sample, - "model": model, - "prompt": prompt, - "metadata": metadata, - "stop_sequences": stop_sequences, - "stream": stream, - "temperature": temperature, - "top_k": top_k, - "top_p": top_p, - }, - completion_create_params.CompletionCreateParamsStreaming - if stream - else completion_create_params.CompletionCreateParamsNonStreaming, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Completion, - stream=stream or False, - stream_cls=Stream[Completion], - ) - - -class AsyncCompletions(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncCompletionsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncCompletionsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncCompletionsWithStreamingResponse(self) - - @overload - async def create( - self, - *, - max_tokens_to_sample: int, - model: ModelParam, - prompt: str, - metadata: MetadataParam | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Completion: - """[Legacy] Create a Text Completion. - - The Text Completions API is a legacy API. - - We recommend using the - [Messages API](https://docs.claude.com/en/api/messages) going forward. - - Future models and features will not be compatible with Text Completions. See our - [migration guide](https://docs.claude.com/en/api/migrating-from-text-completions-to-messages) - for guidance in migrating from Text Completions to Messages. - - Args: - max_tokens_to_sample: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - prompt: The prompt that you want Claude to complete. - - For proper response generation you will need to format your prompt using - alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: - - ``` - "\n\nHuman: {userQuestion}\n\nAssistant:" - ``` - - See [prompt validation](https://docs.claude.com/en/api/prompt-validation) and - our guide to [prompt design](https://docs.claude.com/en/docs/intro-to-prompting) - for more details. - - metadata: An object describing metadata about the request. - - stop_sequences: Sequences that will cause the model to stop generating. - - Our models stop on `"\n\nHuman:"`, and may include additional built-in stop - sequences in the future. By providing the stop_sequences parameter, you may - include additional strings that will cause the model to stop generating. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/streaming) for details. - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - async def create( - self, - *, - max_tokens_to_sample: int, - model: ModelParam, - prompt: str, - stream: Literal[True], - metadata: MetadataParam | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncStream[Completion]: - """[Legacy] Create a Text Completion. - - The Text Completions API is a legacy API. - - We recommend using the - [Messages API](https://docs.claude.com/en/api/messages) going forward. - - Future models and features will not be compatible with Text Completions. See our - [migration guide](https://docs.claude.com/en/api/migrating-from-text-completions-to-messages) - for guidance in migrating from Text Completions to Messages. - - Args: - max_tokens_to_sample: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - prompt: The prompt that you want Claude to complete. - - For proper response generation you will need to format your prompt using - alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: - - ``` - "\n\nHuman: {userQuestion}\n\nAssistant:" - ``` - - See [prompt validation](https://docs.claude.com/en/api/prompt-validation) and - our guide to [prompt design](https://docs.claude.com/en/docs/intro-to-prompting) - for more details. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/streaming) for details. - - metadata: An object describing metadata about the request. - - stop_sequences: Sequences that will cause the model to stop generating. - - Our models stop on `"\n\nHuman:"`, and may include additional built-in stop - sequences in the future. By providing the stop_sequences parameter, you may - include additional strings that will cause the model to stop generating. - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - async def create( - self, - *, - max_tokens_to_sample: int, - model: ModelParam, - prompt: str, - stream: bool, - metadata: MetadataParam | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Completion | AsyncStream[Completion]: - """[Legacy] Create a Text Completion. - - The Text Completions API is a legacy API. - - We recommend using the - [Messages API](https://docs.claude.com/en/api/messages) going forward. - - Future models and features will not be compatible with Text Completions. See our - [migration guide](https://docs.claude.com/en/api/migrating-from-text-completions-to-messages) - for guidance in migrating from Text Completions to Messages. - - Args: - max_tokens_to_sample: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - prompt: The prompt that you want Claude to complete. - - For proper response generation you will need to format your prompt using - alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: - - ``` - "\n\nHuman: {userQuestion}\n\nAssistant:" - ``` - - See [prompt validation](https://docs.claude.com/en/api/prompt-validation) and - our guide to [prompt design](https://docs.claude.com/en/docs/intro-to-prompting) - for more details. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/streaming) for details. - - metadata: An object describing metadata about the request. - - stop_sequences: Sequences that will cause the model to stop generating. - - Our models stop on `"\n\nHuman:"`, and may include additional built-in stop - sequences in the future. By providing the stop_sequences parameter, you may - include additional strings that will cause the model to stop generating. - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @required_args(["max_tokens_to_sample", "model", "prompt"], ["max_tokens_to_sample", "model", "prompt", "stream"]) - async def create( - self, - *, - max_tokens_to_sample: int, - model: ModelParam, - prompt: str, - metadata: MetadataParam | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Literal[True] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Completion | AsyncStream[Completion]: - if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: - timeout = 600 - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **(extra_headers or {}), - } - return await self._post( - "/v1/complete", - body=await async_maybe_transform( - { - "max_tokens_to_sample": max_tokens_to_sample, - "model": model, - "prompt": prompt, - "metadata": metadata, - "stop_sequences": stop_sequences, - "stream": stream, - "temperature": temperature, - "top_k": top_k, - "top_p": top_p, - }, - completion_create_params.CompletionCreateParamsStreaming - if stream - else completion_create_params.CompletionCreateParamsNonStreaming, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Completion, - stream=stream or False, - stream_cls=AsyncStream[Completion], - ) - - -class CompletionsWithRawResponse: - def __init__(self, completions: Completions) -> None: - self._completions = completions - - self.create = _legacy_response.to_raw_response_wrapper( - completions.create, - ) - - -class AsyncCompletionsWithRawResponse: - def __init__(self, completions: AsyncCompletions) -> None: - self._completions = completions - - self.create = _legacy_response.async_to_raw_response_wrapper( - completions.create, - ) - - -class CompletionsWithStreamingResponse: - def __init__(self, completions: Completions) -> None: - self._completions = completions - - self.create = to_streamed_response_wrapper( - completions.create, - ) - - -class AsyncCompletionsWithStreamingResponse: - def __init__(self, completions: AsyncCompletions) -> None: - self._completions = completions - - self.create = async_to_streamed_response_wrapper( - completions.create, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/messages/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/resources/messages/__init__.py deleted file mode 100644 index 6e7cf9d9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/messages/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .batches import ( - Batches, - AsyncBatches, - BatchesWithRawResponse, - AsyncBatchesWithRawResponse, - BatchesWithStreamingResponse, - AsyncBatchesWithStreamingResponse, -) -from .messages import ( - DEPRECATED_MODELS, - Messages, - AsyncMessages, - MessagesWithRawResponse, - AsyncMessagesWithRawResponse, - MessagesWithStreamingResponse, - AsyncMessagesWithStreamingResponse, -) - -__all__ = [ - "Batches", - "AsyncBatches", - "BatchesWithRawResponse", - "AsyncBatchesWithRawResponse", - "BatchesWithStreamingResponse", - "AsyncBatchesWithStreamingResponse", - "Messages", - "AsyncMessages", - "MessagesWithRawResponse", - "AsyncMessagesWithRawResponse", - "MessagesWithStreamingResponse", - "AsyncMessagesWithStreamingResponse", - "DEPRECATED_MODELS", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/messages/batches.py b/.venv/lib/python3.12/site-packages/anthropic/resources/messages/batches.py deleted file mode 100644 index 21333711..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/messages/batches.py +++ /dev/null @@ -1,714 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable - -import httpx - -from ... import _legacy_response -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import path_template, maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ...pagination import SyncPage, AsyncPage -from ..._exceptions import AnthropicError -from ..._base_client import AsyncPaginator, make_request_options -from ...types.messages import batch_list_params, batch_create_params -from ..._decoders.jsonl import JSONLDecoder, AsyncJSONLDecoder -from ...types.messages.message_batch import MessageBatch -from ...types.messages.deleted_message_batch import DeletedMessageBatch -from ...types.messages.message_batch_individual_response import MessageBatchIndividualResponse - -__all__ = ["Batches", "AsyncBatches"] - - -class Batches(SyncAPIResource): - @cached_property - def with_raw_response(self) -> BatchesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return BatchesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> BatchesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return BatchesWithStreamingResponse(self) - - def create( - self, - *, - requests: Iterable[batch_create_params.Request], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> MessageBatch: - """ - Send a batch of Message creation requests. - - The Message Batches API can be used to process multiple Messages API requests at - once. Once a Message Batch is created, it begins processing immediately. Batches - can take up to 24 hours to complete. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - requests: List of requests for prompt completion. Each is an individual request to create - a Message. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/v1/messages/batches", - body=maybe_transform({"requests": requests}, batch_create_params.BatchCreateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MessageBatch, - ) - - def retrieve( - self, - message_batch_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> MessageBatch: - """This endpoint is idempotent and can be used to poll for Message Batch - completion. - - To access the results of a Message Batch, make a request to the - `results_url` field in the response. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - return self._get( - path_template("/v1/messages/batches/{message_batch_id}", message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MessageBatch, - ) - - def list( - self, - *, - after_id: str | Omit = omit, - before_id: str | Omit = omit, - limit: int | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPage[MessageBatch]: - """List all Message Batches within a Workspace. - - Most recently created batches are - returned first. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - after_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately after this object. - - before_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately before this object. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/v1/messages/batches", - page=SyncPage[MessageBatch], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after_id": after_id, - "before_id": before_id, - "limit": limit, - }, - batch_list_params.BatchListParams, - ), - ), - model=MessageBatch, - ) - - def delete( - self, - message_batch_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DeletedMessageBatch: - """ - Delete a Message Batch. - - Message Batches can only be deleted once they've finished processing. If you'd - like to delete an in-progress batch, you must first cancel it. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - return self._delete( - path_template("/v1/messages/batches/{message_batch_id}", message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=DeletedMessageBatch, - ) - - def cancel( - self, - message_batch_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> MessageBatch: - """Batches may be canceled any time before processing ends. - - Once cancellation is - initiated, the batch enters a `canceling` state, at which time the system may - complete any in-progress, non-interruptible requests before finalizing - cancellation. - - The number of canceled requests is specified in `request_counts`. To determine - which requests were canceled, check the individual results within the batch. - Note that cancellation may not result in any canceled requests if they were - non-interruptible. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - return self._post( - path_template("/v1/messages/batches/{message_batch_id}/cancel", message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MessageBatch, - ) - - def results( - self, - message_batch_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> JSONLDecoder[MessageBatchIndividualResponse]: - """ - Streams the results of a Message Batch as a `.jsonl` file. - - Each line in the file is a JSON object containing the result of a single request - in the Message Batch. Results are not guaranteed to be in the same order as - requests. Use the `custom_id` field to match results to requests. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - - batch = self.retrieve(message_batch_id=message_batch_id) - if not batch.results_url: - raise AnthropicError( - f"No `results_url` for the given batch; Has it finished processing? {batch.processing_status}" - ) - - extra_headers = {"Accept": "application/binary", **(extra_headers or {})} - return self._get( - path_template(batch.results_url, message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=JSONLDecoder[MessageBatchIndividualResponse], - stream=True, - ) - - -class AsyncBatches(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncBatchesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncBatchesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncBatchesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncBatchesWithStreamingResponse(self) - - async def create( - self, - *, - requests: Iterable[batch_create_params.Request], - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> MessageBatch: - """ - Send a batch of Message creation requests. - - The Message Batches API can be used to process multiple Messages API requests at - once. Once a Message Batch is created, it begins processing immediately. Batches - can take up to 24 hours to complete. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - requests: List of requests for prompt completion. Each is an individual request to create - a Message. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/v1/messages/batches", - body=await async_maybe_transform({"requests": requests}, batch_create_params.BatchCreateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MessageBatch, - ) - - async def retrieve( - self, - message_batch_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> MessageBatch: - """This endpoint is idempotent and can be used to poll for Message Batch - completion. - - To access the results of a Message Batch, make a request to the - `results_url` field in the response. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - return await self._get( - path_template("/v1/messages/batches/{message_batch_id}", message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MessageBatch, - ) - - def list( - self, - *, - after_id: str | Omit = omit, - before_id: str | Omit = omit, - limit: int | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[MessageBatch, AsyncPage[MessageBatch]]: - """List all Message Batches within a Workspace. - - Most recently created batches are - returned first. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - after_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately after this object. - - before_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately before this object. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/v1/messages/batches", - page=AsyncPage[MessageBatch], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after_id": after_id, - "before_id": before_id, - "limit": limit, - }, - batch_list_params.BatchListParams, - ), - ), - model=MessageBatch, - ) - - async def delete( - self, - message_batch_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DeletedMessageBatch: - """ - Delete a Message Batch. - - Message Batches can only be deleted once they've finished processing. If you'd - like to delete an in-progress batch, you must first cancel it. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - return await self._delete( - path_template("/v1/messages/batches/{message_batch_id}", message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=DeletedMessageBatch, - ) - - async def cancel( - self, - message_batch_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> MessageBatch: - """Batches may be canceled any time before processing ends. - - Once cancellation is - initiated, the batch enters a `canceling` state, at which time the system may - complete any in-progress, non-interruptible requests before finalizing - cancellation. - - The number of canceled requests is specified in `request_counts`. To determine - which requests were canceled, check the individual results within the batch. - Note that cancellation may not result in any canceled requests if they were - non-interruptible. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - return await self._post( - path_template("/v1/messages/batches/{message_batch_id}/cancel", message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MessageBatch, - ) - - async def results( - self, - message_batch_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncJSONLDecoder[MessageBatchIndividualResponse]: - """ - Streams the results of a Message Batch as a `.jsonl` file. - - Each line in the file is a JSON object containing the result of a single request - in the Message Batch. Results are not guaranteed to be in the same order as - requests. Use the `custom_id` field to match results to requests. - - Learn more about the Message Batches API in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/batch-processing) - - Args: - message_batch_id: ID of the Message Batch. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_batch_id: - raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") - - batch = await self.retrieve(message_batch_id=message_batch_id) - if not batch.results_url: - raise AnthropicError( - f"No `results_url` for the given batch; Has it finished processing? {batch.processing_status}" - ) - - extra_headers = {"Accept": "application/binary", **(extra_headers or {})} - return await self._get( - path_template(batch.results_url, message_batch_id=message_batch_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=AsyncJSONLDecoder[MessageBatchIndividualResponse], - stream=True, - ) - - -class BatchesWithRawResponse: - def __init__(self, batches: Batches) -> None: - self._batches = batches - - self.create = _legacy_response.to_raw_response_wrapper( - batches.create, - ) - self.retrieve = _legacy_response.to_raw_response_wrapper( - batches.retrieve, - ) - self.list = _legacy_response.to_raw_response_wrapper( - batches.list, - ) - self.delete = _legacy_response.to_raw_response_wrapper( - batches.delete, - ) - self.cancel = _legacy_response.to_raw_response_wrapper( - batches.cancel, - ) - - -class AsyncBatchesWithRawResponse: - def __init__(self, batches: AsyncBatches) -> None: - self._batches = batches - - self.create = _legacy_response.async_to_raw_response_wrapper( - batches.create, - ) - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - batches.retrieve, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - batches.list, - ) - self.delete = _legacy_response.async_to_raw_response_wrapper( - batches.delete, - ) - self.cancel = _legacy_response.async_to_raw_response_wrapper( - batches.cancel, - ) - - -class BatchesWithStreamingResponse: - def __init__(self, batches: Batches) -> None: - self._batches = batches - - self.create = to_streamed_response_wrapper( - batches.create, - ) - self.retrieve = to_streamed_response_wrapper( - batches.retrieve, - ) - self.list = to_streamed_response_wrapper( - batches.list, - ) - self.delete = to_streamed_response_wrapper( - batches.delete, - ) - self.cancel = to_streamed_response_wrapper( - batches.cancel, - ) - - -class AsyncBatchesWithStreamingResponse: - def __init__(self, batches: AsyncBatches) -> None: - self._batches = batches - - self.create = async_to_streamed_response_wrapper( - batches.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - batches.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - batches.list, - ) - self.delete = async_to_streamed_response_wrapper( - batches.delete, - ) - self.cancel = async_to_streamed_response_wrapper( - batches.cancel, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/messages/messages.py b/.venv/lib/python3.12/site-packages/anthropic/resources/messages/messages.py deleted file mode 100644 index 35471ea4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/messages/messages.py +++ /dev/null @@ -1,3018 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import warnings -from typing import Type, Union, Iterable, Optional, cast -from functools import partial -from typing_extensions import Literal, overload - -import httpx -import pydantic - -from ... import _legacy_response -from ...types import ( - ThinkingConfigParam, - message_create_params, - message_count_tokens_params, -) -from .batches import ( - Batches, - AsyncBatches, - BatchesWithRawResponse, - AsyncBatchesWithRawResponse, - BatchesWithStreamingResponse, - AsyncBatchesWithStreamingResponse, -) -from ..._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import is_given, required_args, maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._models import TypeAdapter -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ..._constants import DEFAULT_TIMEOUT, MODEL_NONSTREAMING_TOKENS -from ..._streaming import Stream, AsyncStream -from ..._base_client import make_request_options -from ..._utils._utils import is_dict -from ...lib.streaming import MessageStreamManager, AsyncMessageStreamManager -from ...types.message import Message -from ...types.model_param import ModelParam -from ...types.message_param import MessageParam -from ...lib._parse._response import ResponseFormatT, parse_response -from ...types.metadata_param import MetadataParam -from ...types.parsed_message import ParsedMessage -from ...lib._parse._transform import transform_schema -from ...types.text_block_param import TextBlockParam -from ...types.tool_union_param import ToolUnionParam -from ...types.tool_choice_param import ToolChoiceParam -from ...types.output_config_param import OutputConfigParam -from ...types.message_tokens_count import MessageTokensCount -from ...types.thinking_config_param import ThinkingConfigParam -from ...types.json_output_format_param import JSONOutputFormatParam -from ...types.raw_message_stream_event import RawMessageStreamEvent -from ...types.cache_control_ephemeral_param import CacheControlEphemeralParam -from ...types.message_count_tokens_tool_param import MessageCountTokensToolParam - -__all__ = ["Messages", "AsyncMessages"] - - -DEPRECATED_MODELS = { - "claude-1.3": "November 6th, 2024", - "claude-1.3-100k": "November 6th, 2024", - "claude-instant-1.1": "November 6th, 2024", - "claude-instant-1.1-100k": "November 6th, 2024", - "claude-instant-1.2": "November 6th, 2024", - "claude-3-sonnet-20240229": "July 21st, 2025", - "claude-3-opus-20240229": "January 5th, 2026", - "claude-2.1": "July 21st, 2025", - "claude-2.0": "July 21st, 2025", - "claude-3-7-sonnet-latest": "February 19th, 2026", - "claude-3-7-sonnet-20250219": "February 19th, 2026", - "claude-3-5-haiku-latest": "February 19th, 2026", - "claude-3-5-haiku-20241022": "February 19th, 2026", - "claude-opus-4-0": "June 15th, 2026", - "claude-opus-4-20250514": "June 15th, 2026", - "claude-sonnet-4-0": "June 15th, 2026", - "claude-sonnet-4-20250514": "June 15th, 2026", -} - -MODELS_TO_WARN_WITH_THINKING_ENABLED = ["claude-opus-4-6", "claude-mythos-preview"] - - -class Messages(SyncAPIResource): - @cached_property - def batches(self) -> Batches: - return Batches(self._client) - - @cached_property - def with_raw_response(self) -> MessagesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return MessagesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> MessagesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return MessagesWithStreamingResponse(self) - - @overload - def create( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - container: Optional[str] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Message: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - def create( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - stream: Literal[True], - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - container: Optional[str] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Stream[RawMessageStreamEvent]: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - def create( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - stream: bool, - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - container: Optional[str] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Message | Stream[RawMessageStreamEvent]: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @required_args(["max_tokens", "messages", "model"], ["max_tokens", "messages", "model", "stream"]) - def create( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - container: Optional[str] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Literal[True] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Message | Stream[RawMessageStreamEvent]: - if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: - timeout = self._client._calculate_nonstreaming_timeout( - max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) - ) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - return self._post( - "/v1/messages", - body=maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "container": container, - "inference_geo": inference_geo, - "metadata": metadata, - "output_config": output_config, - "service_tier": service_tier, - "stop_sequences": stop_sequences, - "stream": stream, - "system": system, - "temperature": temperature, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - "top_k": top_k, - "top_p": top_p, - }, - message_create_params.MessageCreateParamsStreaming - if stream - else message_create_params.MessageCreateParamsNonStreaming, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Message, - stream=stream or False, - stream_cls=Stream[RawMessageStreamEvent], - ) - - def stream( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - output_format: None | JSONOutputFormatParam | type[ResponseFormatT] | Omit = omit, - container: Optional[str] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> MessageStreamManager[ResponseFormatT]: - """Create a Message stream""" - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - extra_headers = { - "X-Stainless-Helper-Method": "stream", - "X-Stainless-Stream-Helper": "messages", - **(extra_headers or {}), - } - - transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN - - if is_dict(output_format): - transformed_output_format = cast(JSONOutputFormatParam, output_format) - elif is_given(output_format) and output_format is not None: - adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) - - try: - schema = adapted_type.json_schema() - transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") - except pydantic.errors.PydanticSchemaGenerationError as e: - raise TypeError( - ( - "Could not generate JSON schema for the given `output_format` type. " - "Use a type that works with `pydantic.TypeAdapter`" - ) - ) from e - - # Merge output_format into output_config - merged_output_config: OutputConfigParam | Omit = omit - if is_given(transformed_output_format): - if is_given(output_config): - merged_output_config = {**output_config, "format": transformed_output_format} - else: - merged_output_config = {"format": transformed_output_format} - elif is_given(output_config): - merged_output_config = output_config - - make_request = partial( - self._post, - "/v1/messages", - body=maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "inference_geo": inference_geo, - "metadata": metadata, - "output_config": merged_output_config, - "container": container, - "service_tier": service_tier, - "stop_sequences": stop_sequences, - "system": system, - "temperature": temperature, - "top_k": top_k, - "top_p": top_p, - "tools": tools, - "thinking": thinking, - "tool_choice": tool_choice, - "stream": True, - }, - message_create_params.MessageCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Message, - stream=True, - stream_cls=Stream[RawMessageStreamEvent], - ) - return MessageStreamManager( - make_request, - output_format=NOT_GIVEN if is_dict(output_format) else cast(ResponseFormatT, output_format), - ) - - def parse( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Literal[True] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ParsedMessage[ResponseFormatT]: - if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: - timeout = self._client._calculate_nonstreaming_timeout( - max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) - ) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - extra_headers = { - "X-Stainless-Helper": "messages.parse", - **(extra_headers or {}), - } - - transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN - - if is_given(output_format) and output_format is not None: - adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) - - try: - schema = adapted_type.json_schema() - transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") - except pydantic.errors.PydanticSchemaGenerationError as e: - raise TypeError( - ( - "Could not generate JSON schema for the given `output_format` type. " - "Use a type that works with `pydantic.TypeAdapter`" - ) - ) from e - - def parser(response: Message) -> ParsedMessage[ResponseFormatT]: - return parse_response( - response=response, - output_format=cast( - ResponseFormatT, - output_format if is_given(output_format) and output_format is not None else NOT_GIVEN, - ), - ) - - # Merge output_format into output_config - merged_output_config: OutputConfigParam | Omit = omit - if is_given(transformed_output_format): - if is_given(output_config): - merged_output_config = {**output_config, "format": transformed_output_format} - else: - merged_output_config = {"format": transformed_output_format} - elif is_given(output_config): - merged_output_config = output_config - - return self._post( - "/v1/messages", - body=maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "metadata": metadata, - "output_config": merged_output_config, - "service_tier": service_tier, - "stop_sequences": stop_sequences, - "stream": stream, - "system": system, - "temperature": temperature, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - "top_k": top_k, - "top_p": top_p, - }, - message_create_params.MessageCreateParamsNonStreaming, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - post_parser=parser, - ), - cast_to=cast(Type[ParsedMessage[ResponseFormatT]], Message), - stream=False, - ) - - def count_tokens( - self, - *, - messages: Iterable[MessageParam], - model: ModelParam, - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - output_format: None | JSONOutputFormatParam | type | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[MessageCountTokensToolParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> MessageTokensCount: - """ - Count the number of tokens in a Message. - - The Token Count API can be used to count the number of tokens in a Message, - including tools, images, and documents, without creating it. - - Learn more about token counting in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/token-counting) - - Args: - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - output_config: Configuration options for the model's output, such as the output format. - - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # Transform output_format if provided - transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN - - if is_dict(output_format): - transformed_output_format = cast(JSONOutputFormatParam, output_format) - elif is_given(output_format) and output_format is not None: - adapted_type: TypeAdapter[type] = TypeAdapter(output_format) - - try: - schema = adapted_type.json_schema() - transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") - except pydantic.errors.PydanticSchemaGenerationError as e: - raise TypeError( - ( - "Could not generate JSON schema for the given `output_format` type. " - "Use a type that works with `pydantic.TypeAdapter`" - ) - ) from e - - # Merge output_format into output_config - merged_output_config: OutputConfigParam | Omit = omit - if is_given(transformed_output_format): - if is_given(output_config): - merged_output_config = {**output_config, "format": transformed_output_format} - else: - merged_output_config = {"format": transformed_output_format} - elif is_given(output_config): - merged_output_config = output_config - - return self._post( - "/v1/messages/count_tokens", - body=maybe_transform( - { - "messages": messages, - "model": model, - "messages": messages, - "model": model, - "cache_control": cache_control, - "output_config": merged_output_config, - "system": system, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - }, - message_count_tokens_params.MessageCountTokensParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MessageTokensCount, - ) - - -class AsyncMessages(AsyncAPIResource): - @cached_property - def batches(self) -> AsyncBatches: - return AsyncBatches(self._client) - - @cached_property - def with_raw_response(self) -> AsyncMessagesWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncMessagesWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncMessagesWithStreamingResponse(self) - - @overload - async def create( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - container: Optional[str] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Message: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - async def create( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - stream: Literal[True], - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - container: Optional[str] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncStream[RawMessageStreamEvent]: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @overload - async def create( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - stream: bool, - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - container: Optional[str] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Message | AsyncStream[RawMessageStreamEvent]: - """ - Send a structured list of input messages with text and/or image content, and the - model will generate the next message in the conversation. - - The Messages API can be used for either single queries or stateless multi-turn - conversations. - - Learn more about the Messages API in our - [user guide](https://docs.claude.com/en/docs/initial-setup) - - Args: - max_tokens: The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - stream: Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - container: Container identifier for reuse across requests. - - inference_geo: Specifies the geographic region for inference processing. If not specified, the - workspace's `default_inference_geo` is used. - - metadata: An object describing metadata about the request. - - output_config: Configuration options for the model's output, such as the output format. - - service_tier: Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - - stop_sequences: Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - temperature: Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - top_k: Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - - top_p: Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - ... - - @required_args(["max_tokens", "messages", "model"], ["max_tokens", "messages", "model", "stream"]) - async def create( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - container: Optional[str] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Literal[True] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Message | AsyncStream[RawMessageStreamEvent]: - if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: - timeout = self._client._calculate_nonstreaming_timeout( - max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) - ) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - return await self._post( - "/v1/messages", - body=await async_maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "container": container, - "inference_geo": inference_geo, - "metadata": metadata, - "output_config": output_config, - "service_tier": service_tier, - "stop_sequences": stop_sequences, - "stream": stream, - "system": system, - "temperature": temperature, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - "top_k": top_k, - "top_p": top_p, - }, - message_create_params.MessageCreateParamsStreaming - if stream - else message_create_params.MessageCreateParamsNonStreaming, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Message, - stream=stream or False, - stream_cls=AsyncStream[RawMessageStreamEvent], - ) - - def stream( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - inference_geo: Optional[str] | Omit = omit, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - output_format: None | JSONOutputFormatParam | type[ResponseFormatT] | Omit = omit, - container: Optional[str] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncMessageStreamManager[ResponseFormatT]: - """Create a Message stream""" - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - extra_headers = { - "X-Stainless-Helper-Method": "stream", - "X-Stainless-Stream-Helper": "messages", - **(extra_headers or {}), - } - - transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN - - if is_dict(output_format): - transformed_output_format = cast(JSONOutputFormatParam, output_format) - elif is_given(output_format) and output_format is not None: - adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) - - try: - schema = adapted_type.json_schema() - transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") - except pydantic.errors.PydanticSchemaGenerationError as e: - raise TypeError( - ( - "Could not generate JSON schema for the given `output_format` type. " - "Use a type that works with `pydantic.TypeAdapter`" - ) - ) from e - - # Merge output_format into output_config - merged_output_config: OutputConfigParam | Omit = omit - if is_given(transformed_output_format): - if is_given(output_config): - merged_output_config = {**output_config, "format": transformed_output_format} - else: - merged_output_config = {"format": transformed_output_format} - elif is_given(output_config): - merged_output_config = output_config - - request = self._post( - "/v1/messages", - body=maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "cache_control": cache_control, - "inference_geo": inference_geo, - "metadata": metadata, - "output_config": merged_output_config, - "container": container, - "service_tier": service_tier, - "stop_sequences": stop_sequences, - "system": system, - "temperature": temperature, - "top_k": top_k, - "top_p": top_p, - "tools": tools, - "thinking": thinking, - "tool_choice": tool_choice, - "stream": True, - }, - message_create_params.MessageCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Message, - stream=True, - stream_cls=AsyncStream[RawMessageStreamEvent], - ) - return AsyncMessageStreamManager( - request, - output_format=NOT_GIVEN if is_dict(output_format) else cast(ResponseFormatT, output_format), - ) - - async def parse( - self, - *, - max_tokens: int, - messages: Iterable[MessageParam], - model: ModelParam, - metadata: MetadataParam | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - output_format: Optional[type[ResponseFormatT]] | Omit = omit, - service_tier: Literal["auto", "standard_only"] | Omit = omit, - stop_sequences: SequenceNotStr[str] | Omit = omit, - stream: Literal[False] | Literal[True] | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - temperature: float | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[ToolUnionParam] | Omit = omit, - top_k: int | Omit = omit, - top_p: float | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> ParsedMessage[ResponseFormatT]: - if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: - timeout = self._client._calculate_nonstreaming_timeout( - max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) - ) - - if model in DEPRECATED_MODELS: - warnings.warn( - f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", - DeprecationWarning, - stacklevel=3, - ) - - if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": - warnings.warn( - f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", - UserWarning, - stacklevel=3, - ) - - extra_headers = { - "X-Stainless-Helper": "messages.parse", - **(extra_headers or {}), - } - - transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN - - if is_given(output_format) and output_format is not None: - adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) - - try: - schema = adapted_type.json_schema() - transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") - except pydantic.errors.PydanticSchemaGenerationError as e: - raise TypeError( - ( - "Could not generate JSON schema for the given `output_format` type. " - "Use a type that works with `pydantic.TypeAdapter`" - ) - ) from e - - def parser(response: Message) -> ParsedMessage[ResponseFormatT]: - return parse_response( - response=response, - output_format=cast( - ResponseFormatT, - output_format if is_given(output_format) and output_format is not None else NOT_GIVEN, - ), - ) - - # Merge output_format into output_config - merged_output_config: OutputConfigParam | Omit = omit - if is_given(transformed_output_format): - if is_given(output_config): - merged_output_config = {**output_config, "format": transformed_output_format} - else: - merged_output_config = {"format": transformed_output_format} - elif is_given(output_config): - merged_output_config = output_config - - return await self._post( - "/v1/messages", - body=await async_maybe_transform( - { - "max_tokens": max_tokens, - "messages": messages, - "model": model, - "metadata": metadata, - "output_config": merged_output_config, - "service_tier": service_tier, - "stop_sequences": stop_sequences, - "stream": stream, - "system": system, - "temperature": temperature, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - "top_k": top_k, - "top_p": top_p, - }, - message_create_params.MessageCreateParamsNonStreaming, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - post_parser=parser, - ), - cast_to=cast(Type[ParsedMessage[ResponseFormatT]], Message), - stream=False, - ) - - async def count_tokens( - self, - *, - messages: Iterable[MessageParam], - model: ModelParam, - cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, - output_config: OutputConfigParam | Omit = omit, - output_format: None | JSONOutputFormatParam | type | Omit = omit, - system: Union[str, Iterable[TextBlockParam]] | Omit = omit, - thinking: ThinkingConfigParam | Omit = omit, - tool_choice: ToolChoiceParam | Omit = omit, - tools: Iterable[MessageCountTokensToolParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> MessageTokensCount: - """ - Count the number of tokens in a Message. - - The Token Count API can be used to count the number of tokens in a Message, - including tools, images, and documents, without creating it. - - Learn more about token counting in our - [user guide](https://docs.claude.com/en/docs/build-with-claude/token-counting) - - Args: - messages: Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - - model: The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - - cache_control: Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - - output_config: Configuration options for the model's output, such as the output format. - - - system: System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - - thinking: Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - - tool_choice: How the model should use the provided tools. The model can use a specific tool, - any available tool, decide by itself, or not use tools at all. - - tools: Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - # Transform output_format if provided - transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN - - if is_dict(output_format): - transformed_output_format = cast(JSONOutputFormatParam, output_format) - elif is_given(output_format) and output_format is not None: - adapted_type: TypeAdapter[type] = TypeAdapter(output_format) - - try: - schema = adapted_type.json_schema() - transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") - except pydantic.errors.PydanticSchemaGenerationError as e: - raise TypeError( - ( - "Could not generate JSON schema for the given `output_format` type. " - "Use a type that works with `pydantic.TypeAdapter`" - ) - ) from e - - # Merge output_format into output_config - merged_output_config: OutputConfigParam | Omit = omit - if is_given(transformed_output_format): - if is_given(output_config): - merged_output_config = {**output_config, "format": transformed_output_format} - else: - merged_output_config = {"format": transformed_output_format} - elif is_given(output_config): - merged_output_config = output_config - - return await self._post( - "/v1/messages/count_tokens", - body=await async_maybe_transform( - { - "messages": messages, - "model": model, - "messages": messages, - "model": model, - "cache_control": cache_control, - "output_config": merged_output_config, - "system": system, - "thinking": thinking, - "tool_choice": tool_choice, - "tools": tools, - }, - message_count_tokens_params.MessageCountTokensParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=MessageTokensCount, - ) - - -class MessagesWithRawResponse: - def __init__(self, messages: Messages) -> None: - self._messages = messages - - self.create = _legacy_response.to_raw_response_wrapper( - messages.create, - ) - self.count_tokens = _legacy_response.to_raw_response_wrapper( - messages.count_tokens, - ) - - @cached_property - def batches(self) -> BatchesWithRawResponse: - return BatchesWithRawResponse(self._messages.batches) - - -class AsyncMessagesWithRawResponse: - def __init__(self, messages: AsyncMessages) -> None: - self._messages = messages - - self.create = _legacy_response.async_to_raw_response_wrapper( - messages.create, - ) - self.count_tokens = _legacy_response.async_to_raw_response_wrapper( - messages.count_tokens, - ) - - @cached_property - def batches(self) -> AsyncBatchesWithRawResponse: - return AsyncBatchesWithRawResponse(self._messages.batches) - - -class MessagesWithStreamingResponse: - def __init__(self, messages: Messages) -> None: - self._messages = messages - - self.create = to_streamed_response_wrapper( - messages.create, - ) - self.count_tokens = to_streamed_response_wrapper( - messages.count_tokens, - ) - - @cached_property - def batches(self) -> BatchesWithStreamingResponse: - return BatchesWithStreamingResponse(self._messages.batches) - - -class AsyncMessagesWithStreamingResponse: - def __init__(self, messages: AsyncMessages) -> None: - self._messages = messages - - self.create = async_to_streamed_response_wrapper( - messages.create, - ) - self.count_tokens = async_to_streamed_response_wrapper( - messages.count_tokens, - ) - - @cached_property - def batches(self) -> AsyncBatchesWithStreamingResponse: - return AsyncBatchesWithStreamingResponse(self._messages.batches) diff --git a/.venv/lib/python3.12/site-packages/anthropic/resources/models.py b/.venv/lib/python3.12/site-packages/anthropic/resources/models.py deleted file mode 100644 index 093dd8ab..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/resources/models.py +++ /dev/null @@ -1,331 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List - -import httpx - -from .. import _legacy_response -from ..types import model_list_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import is_given, path_template, maybe_transform, strip_not_given -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper -from ..pagination import SyncPage, AsyncPage -from .._base_client import AsyncPaginator, make_request_options -from ..types.model_info import ModelInfo -from ..types.anthropic_beta_param import AnthropicBetaParam - -__all__ = ["Models", "AsyncModels"] - - -class Models(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ModelsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return ModelsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ModelsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return ModelsWithStreamingResponse(self) - - def retrieve( - self, - model_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ModelInfo: - """ - Get a specific model. - - The Models API response can be used to determine information about a specific - model or resolve a model alias to a model ID. - - Args: - model_id: Model identifier or alias. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model_id: - raise ValueError(f"Expected a non-empty value for `model_id` but received {model_id!r}") - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **(extra_headers or {}), - } - return self._get( - path_template("/v1/models/{model_id}", model_id=model_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ModelInfo, - ) - - def list( - self, - *, - after_id: str | Omit = omit, - before_id: str | Omit = omit, - limit: int | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncPage[ModelInfo]: - """ - List available models. - - The Models API response can be used to determine which models are available for - use in the API. More recently released models are listed first. - - Args: - after_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately after this object. - - before_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately before this object. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **(extra_headers or {}), - } - return self._get_api_list( - "/v1/models", - page=SyncPage[ModelInfo], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after_id": after_id, - "before_id": before_id, - "limit": limit, - }, - model_list_params.ModelListParams, - ), - ), - model=ModelInfo, - ) - - -class AsyncModels(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncModelsWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers - """ - return AsyncModelsWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncModelsWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response - """ - return AsyncModelsWithStreamingResponse(self) - - async def retrieve( - self, - model_id: str, - *, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ModelInfo: - """ - Get a specific model. - - The Models API response can be used to determine information about a specific - model or resolve a model alias to a model ID. - - Args: - model_id: Model identifier or alias. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not model_id: - raise ValueError(f"Expected a non-empty value for `model_id` but received {model_id!r}") - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **(extra_headers or {}), - } - return await self._get( - path_template("/v1/models/{model_id}", model_id=model_id), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ModelInfo, - ) - - def list( - self, - *, - after_id: str | Omit = omit, - before_id: str | Omit = omit, - limit: int | Omit = omit, - betas: List[AnthropicBetaParam] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[ModelInfo, AsyncPage[ModelInfo]]: - """ - List available models. - - The Models API response can be used to determine which models are available for - use in the API. More recently released models are listed first. - - Args: - after_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately after this object. - - before_id: ID of the object to use as a cursor for pagination. When provided, returns the - page of results immediately before this object. - - limit: Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - - betas: Optional header to specify the beta version(s) you want to use. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - extra_headers = { - **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), - **(extra_headers or {}), - } - return self._get_api_list( - "/v1/models", - page=AsyncPage[ModelInfo], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after_id": after_id, - "before_id": before_id, - "limit": limit, - }, - model_list_params.ModelListParams, - ), - ), - model=ModelInfo, - ) - - -class ModelsWithRawResponse: - def __init__(self, models: Models) -> None: - self._models = models - - self.retrieve = _legacy_response.to_raw_response_wrapper( - models.retrieve, - ) - self.list = _legacy_response.to_raw_response_wrapper( - models.list, - ) - - -class AsyncModelsWithRawResponse: - def __init__(self, models: AsyncModels) -> None: - self._models = models - - self.retrieve = _legacy_response.async_to_raw_response_wrapper( - models.retrieve, - ) - self.list = _legacy_response.async_to_raw_response_wrapper( - models.list, - ) - - -class ModelsWithStreamingResponse: - def __init__(self, models: Models) -> None: - self._models = models - - self.retrieve = to_streamed_response_wrapper( - models.retrieve, - ) - self.list = to_streamed_response_wrapper( - models.list, - ) - - -class AsyncModelsWithStreamingResponse: - def __init__(self, models: AsyncModels) -> None: - self._models = models - - self.retrieve = async_to_streamed_response_wrapper( - models.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - models.list, - ) diff --git a/.venv/lib/python3.12/site-packages/anthropic/tools/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/tools/__init__.py deleted file mode 100644 index 9ad79d8d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/tools/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .memory import * diff --git a/.venv/lib/python3.12/site-packages/anthropic/tools/memory.py b/.venv/lib/python3.12/site-packages/anthropic/tools/memory.py deleted file mode 100644 index 3ed0106c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/tools/memory.py +++ /dev/null @@ -1,13 +0,0 @@ -from ..lib.tools._beta_builtin_memory_tool import ( - BetaAbstractMemoryTool, - BetaAsyncAbstractMemoryTool, - BetaLocalFilesystemMemoryTool, - BetaAsyncLocalFilesystemMemoryTool, -) - -__all__ = [ - "BetaLocalFilesystemMemoryTool", - "BetaAsyncLocalFilesystemMemoryTool", - "BetaAbstractMemoryTool", - "BetaAsyncAbstractMemoryTool", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/types/__init__.py deleted file mode 100644 index 8f52227c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/__init__.py +++ /dev/null @@ -1,273 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .model import Model as Model -from .usage import Usage as Usage -from .shared import ( - ErrorType as ErrorType, - ErrorObject as ErrorObject, - BillingError as BillingError, - ErrorResponse as ErrorResponse, - NotFoundError as NotFoundError, - APIErrorObject as APIErrorObject, - RateLimitError as RateLimitError, - OverloadedError as OverloadedError, - PermissionError as PermissionError, - AuthenticationError as AuthenticationError, - GatewayTimeoutError as GatewayTimeoutError, - InvalidRequestError as InvalidRequestError, -) -from .message import Message as Message -from .container import Container as Container -from .beta_error import BetaError as BetaError -from .completion import Completion as Completion -from .model_info import ModelInfo as ModelInfo -from .text_block import TextBlock as TextBlock -from .text_delta import TextDelta as TextDelta -from .tool_param import ToolParam as ToolParam -from .model_param import ModelParam as ModelParam -from .stop_reason import StopReason as StopReason -from .content_block import ContentBlock as ContentBlock -from .direct_caller import DirectCaller as DirectCaller -from .message_param import MessageParam as MessageParam -from .text_citation import TextCitation as TextCitation -from .beta_api_error import BetaAPIError as BetaAPIError -from .cache_creation import CacheCreation as CacheCreation -from .document_block import DocumentBlock as DocumentBlock -from .metadata_param import MetadataParam as MetadataParam -from .parsed_message import ( - ParsedMessage as ParsedMessage, - ParsedTextBlock as ParsedTextBlock, - ParsedContentBlock as ParsedContentBlock, -) -from .thinking_block import ThinkingBlock as ThinkingBlock -from .thinking_delta import ThinkingDelta as ThinkingDelta -from .thinking_types import ThinkingTypes as ThinkingTypes -from .tool_use_block import ToolUseBlock as ToolUseBlock -from .citations_delta import CitationsDelta as CitationsDelta -from .signature_delta import SignatureDelta as SignatureDelta -from .web_fetch_block import WebFetchBlock as WebFetchBlock -from .citations_config import CitationsConfig as CitationsConfig -from .input_json_delta import InputJSONDelta as InputJSONDelta -from .text_block_param import TextBlockParam as TextBlockParam -from .tool_union_param import ToolUnionParam as ToolUnionParam -from .base64_pdf_source import Base64PDFSource as Base64PDFSource -from .effort_capability import EffortCapability as EffortCapability -from .image_block_param import ImageBlockParam as ImageBlockParam -from .model_list_params import ModelListParams as ModelListParams -from .plain_text_source import PlainTextSource as PlainTextSource -from .server_tool_usage import ServerToolUsage as ServerToolUsage -from .tool_choice_param import ToolChoiceParam as ToolChoiceParam -from .beta_billing_error import BetaBillingError as BetaBillingError -from .capability_support import CapabilitySupport as CapabilitySupport -from .message_stop_event import MessageStopEvent as MessageStopEvent -from .model_capabilities import ModelCapabilities as ModelCapabilities -from .server_tool_caller import ServerToolCaller as ServerToolCaller -from .beta_error_response import BetaErrorResponse as BetaErrorResponse -from .content_block_param import ContentBlockParam as ContentBlockParam -from .direct_caller_param import DirectCallerParam as DirectCallerParam -from .message_delta_event import MessageDeltaEvent as MessageDeltaEvent -from .message_delta_usage import MessageDeltaUsage as MessageDeltaUsage -from .message_start_event import MessageStartEvent as MessageStartEvent -from .output_config_param import OutputConfigParam as OutputConfigParam -from .text_citation_param import TextCitationParam as TextCitationParam -from .thinking_capability import ThinkingCapability as ThinkingCapability -from .user_location_param import UserLocationParam as UserLocationParam -from .anthropic_beta_param import AnthropicBetaParam as AnthropicBetaParam -from .beta_not_found_error import BetaNotFoundError as BetaNotFoundError -from .document_block_param import DocumentBlockParam as DocumentBlockParam -from .message_stream_event import MessageStreamEvent as MessageStreamEvent -from .message_tokens_count import MessageTokensCount as MessageTokensCount -from .refusal_stop_details import RefusalStopDetails as RefusalStopDetails -from .thinking_block_param import ThinkingBlockParam as ThinkingBlockParam -from .tool_reference_block import ToolReferenceBlock as ToolReferenceBlock -from .tool_use_block_param import ToolUseBlockParam as ToolUseBlockParam -from .url_pdf_source_param import URLPDFSourceParam as URLPDFSourceParam -from .beta_overloaded_error import BetaOverloadedError as BetaOverloadedError -from .beta_permission_error import BetaPermissionError as BetaPermissionError -from .beta_rate_limit_error import BetaRateLimitError as BetaRateLimitError -from .message_create_params import MessageCreateParams as MessageCreateParams -from .server_tool_use_block import ServerToolUseBlock as ServerToolUseBlock -from .thinking_config_param import ThinkingConfigParam as ThinkingConfigParam -from .tool_choice_any_param import ToolChoiceAnyParam as ToolChoiceAnyParam -from .web_fetch_block_param import WebFetchBlockParam as WebFetchBlockParam -from .citation_char_location import CitationCharLocation as CitationCharLocation -from .citation_page_location import CitationPageLocation as CitationPageLocation -from .citations_config_param import CitationsConfigParam as CitationsConfigParam -from .container_upload_block import ContainerUploadBlock as ContainerUploadBlock -from .raw_message_stop_event import RawMessageStopEvent as RawMessageStopEvent -from .tool_choice_auto_param import ToolChoiceAutoParam as ToolChoiceAutoParam -from .tool_choice_none_param import ToolChoiceNoneParam as ToolChoiceNoneParam -from .tool_choice_tool_param import ToolChoiceToolParam as ToolChoiceToolParam -from .url_image_source_param import URLImageSourceParam as URLImageSourceParam -from .base64_pdf_source_param import Base64PDFSourceParam as Base64PDFSourceParam -from .plain_text_source_param import PlainTextSourceParam as PlainTextSourceParam -from .raw_content_block_delta import RawContentBlockDelta as RawContentBlockDelta -from .raw_message_delta_event import RawMessageDeltaEvent as RawMessageDeltaEvent -from .raw_message_start_event import RawMessageStartEvent as RawMessageStartEvent -from .redacted_thinking_block import RedactedThinkingBlock as RedactedThinkingBlock -from .tool_result_block_param import ToolResultBlockParam as ToolResultBlockParam -from .web_search_result_block import WebSearchResultBlock as WebSearchResultBlock -from .completion_create_params import CompletionCreateParams as CompletionCreateParams -from .content_block_stop_event import ContentBlockStopEvent as ContentBlockStopEvent -from .json_output_format_param import JSONOutputFormatParam as JSONOutputFormatParam -from .raw_message_stream_event import RawMessageStreamEvent as RawMessageStreamEvent -from .server_tool_caller_param import ServerToolCallerParam as ServerToolCallerParam -from .tool_bash_20250124_param import ToolBash20250124Param as ToolBash20250124Param -from .base64_image_source_param import Base64ImageSourceParam as Base64ImageSourceParam -from .beta_authentication_error import BetaAuthenticationError as BetaAuthenticationError -from .content_block_delta_event import ContentBlockDeltaEvent as ContentBlockDeltaEvent -from .content_block_start_event import ContentBlockStartEvent as ContentBlockStartEvent -from .search_result_block_param import SearchResultBlockParam as SearchResultBlockParam -from .beta_gateway_timeout_error import BetaGatewayTimeoutError as BetaGatewayTimeoutError -from .beta_invalid_request_error import BetaInvalidRequestError as BetaInvalidRequestError -from .content_block_source_param import ContentBlockSourceParam as ContentBlockSourceParam -from .memory_tool_20250818_param import MemoryTool20250818Param as MemoryTool20250818Param -from .tool_reference_block_param import ToolReferenceBlockParam as ToolReferenceBlockParam -from .code_execution_output_block import CodeExecutionOutputBlock as CodeExecutionOutputBlock -from .code_execution_result_block import CodeExecutionResultBlock as CodeExecutionResultBlock -from .message_count_tokens_params import MessageCountTokensParams as MessageCountTokensParams -from .server_tool_caller_20260120 import ServerToolCaller20260120 as ServerToolCaller20260120 -from .server_tool_use_block_param import ServerToolUseBlockParam as ServerToolUseBlockParam -from .web_fetch_tool_result_block import WebFetchToolResultBlock as WebFetchToolResultBlock -from .citation_char_location_param import CitationCharLocationParam as CitationCharLocationParam -from .citation_page_location_param import CitationPageLocationParam as CitationPageLocationParam -from .container_upload_block_param import ContainerUploadBlockParam as ContainerUploadBlockParam -from .raw_content_block_stop_event import RawContentBlockStopEvent as RawContentBlockStopEvent -from .web_search_tool_result_block import WebSearchToolResultBlock as WebSearchToolResultBlock -from .web_search_tool_result_error import WebSearchToolResultError as WebSearchToolResultError -from .cache_control_ephemeral_param import CacheControlEphemeralParam as CacheControlEphemeralParam -from .context_management_capability import ContextManagementCapability as ContextManagementCapability -from .raw_content_block_delta_event import RawContentBlockDeltaEvent as RawContentBlockDeltaEvent -from .raw_content_block_start_event import RawContentBlockStartEvent as RawContentBlockStartEvent -from .redacted_thinking_block_param import RedactedThinkingBlockParam as RedactedThinkingBlockParam -from .thinking_config_enabled_param import ThinkingConfigEnabledParam as ThinkingConfigEnabledParam -from .tool_search_tool_result_block import ToolSearchToolResultBlock as ToolSearchToolResultBlock -from .tool_search_tool_result_error import ToolSearchToolResultError as ToolSearchToolResultError -from .web_fetch_tool_20250910_param import WebFetchTool20250910Param as WebFetchTool20250910Param -from .web_fetch_tool_20260209_param import WebFetchTool20260209Param as WebFetchTool20260209Param -from .web_fetch_tool_20260309_param import WebFetchTool20260309Param as WebFetchTool20260309Param -from .web_search_result_block_param import WebSearchResultBlockParam as WebSearchResultBlockParam -from .thinking_config_adaptive_param import ThinkingConfigAdaptiveParam as ThinkingConfigAdaptiveParam -from .thinking_config_disabled_param import ThinkingConfigDisabledParam as ThinkingConfigDisabledParam -from .web_search_tool_20250305_param import WebSearchTool20250305Param as WebSearchTool20250305Param -from .web_search_tool_20260209_param import WebSearchTool20260209Param as WebSearchTool20260209Param -from .citation_content_block_location import CitationContentBlockLocation as CitationContentBlockLocation -from .message_count_tokens_tool_param import MessageCountTokensToolParam as MessageCountTokensToolParam -from .tool_text_editor_20250124_param import ToolTextEditor20250124Param as ToolTextEditor20250124Param -from .tool_text_editor_20250429_param import ToolTextEditor20250429Param as ToolTextEditor20250429Param -from .tool_text_editor_20250728_param import ToolTextEditor20250728Param as ToolTextEditor20250728Param -from .bash_code_execution_output_block import BashCodeExecutionOutputBlock as BashCodeExecutionOutputBlock -from .bash_code_execution_result_block import BashCodeExecutionResultBlock as BashCodeExecutionResultBlock -from .citations_search_result_location import CitationsSearchResultLocation as CitationsSearchResultLocation -from .code_execution_tool_result_block import CodeExecutionToolResultBlock as CodeExecutionToolResultBlock -from .code_execution_tool_result_error import CodeExecutionToolResultError as CodeExecutionToolResultError -from .web_fetch_tool_result_error_code import WebFetchToolResultErrorCode as WebFetchToolResultErrorCode -from .code_execution_output_block_param import CodeExecutionOutputBlockParam as CodeExecutionOutputBlockParam -from .code_execution_result_block_param import CodeExecutionResultBlockParam as CodeExecutionResultBlockParam -from .server_tool_caller_20260120_param import ServerToolCaller20260120Param as ServerToolCaller20260120Param -from .web_fetch_tool_result_block_param import WebFetchToolResultBlockParam as WebFetchToolResultBlockParam -from .web_fetch_tool_result_error_block import WebFetchToolResultErrorBlock as WebFetchToolResultErrorBlock -from .web_search_tool_result_error_code import WebSearchToolResultErrorCode as WebSearchToolResultErrorCode -from .code_execution_tool_20250522_param import CodeExecutionTool20250522Param as CodeExecutionTool20250522Param -from .code_execution_tool_20250825_param import CodeExecutionTool20250825Param as CodeExecutionTool20250825Param -from .code_execution_tool_20260120_param import CodeExecutionTool20260120Param as CodeExecutionTool20260120Param -from .content_block_source_content_param import ContentBlockSourceContentParam as ContentBlockSourceContentParam -from .tool_search_tool_result_error_code import ToolSearchToolResultErrorCode as ToolSearchToolResultErrorCode -from .web_search_tool_result_block_param import WebSearchToolResultBlockParam as WebSearchToolResultBlockParam -from .tool_search_tool_result_block_param import ToolSearchToolResultBlockParam as ToolSearchToolResultBlockParam -from .tool_search_tool_result_error_param import ToolSearchToolResultErrorParam as ToolSearchToolResultErrorParam -from .web_search_tool_request_error_param import WebSearchToolRequestErrorParam as WebSearchToolRequestErrorParam -from .citations_web_search_result_location import CitationsWebSearchResultLocation as CitationsWebSearchResultLocation -from .tool_search_tool_bm25_20251119_param import ToolSearchToolBm25_20251119Param as ToolSearchToolBm25_20251119Param -from .tool_search_tool_search_result_block import ToolSearchToolSearchResultBlock as ToolSearchToolSearchResultBlock -from .web_search_tool_result_block_content import WebSearchToolResultBlockContent as WebSearchToolResultBlockContent -from .bash_code_execution_tool_result_block import BashCodeExecutionToolResultBlock as BashCodeExecutionToolResultBlock -from .bash_code_execution_tool_result_error import BashCodeExecutionToolResultError as BashCodeExecutionToolResultError -from .citation_content_block_location_param import ( - CitationContentBlockLocationParam as CitationContentBlockLocationParam, -) -from .citation_search_result_location_param import ( - CitationSearchResultLocationParam as CitationSearchResultLocationParam, -) -from .code_execution_tool_result_error_code import CodeExecutionToolResultErrorCode as CodeExecutionToolResultErrorCode -from .encrypted_code_execution_result_block import ( - EncryptedCodeExecutionResultBlock as EncryptedCodeExecutionResultBlock, -) -from .tool_search_tool_regex_20251119_param import ToolSearchToolRegex20251119Param as ToolSearchToolRegex20251119Param -from .bash_code_execution_output_block_param import ( - BashCodeExecutionOutputBlockParam as BashCodeExecutionOutputBlockParam, -) -from .bash_code_execution_result_block_param import ( - BashCodeExecutionResultBlockParam as BashCodeExecutionResultBlockParam, -) -from .code_execution_tool_result_block_param import ( - CodeExecutionToolResultBlockParam as CodeExecutionToolResultBlockParam, -) -from .code_execution_tool_result_error_param import ( - CodeExecutionToolResultErrorParam as CodeExecutionToolResultErrorParam, -) -from .web_fetch_tool_result_error_block_param import ( - WebFetchToolResultErrorBlockParam as WebFetchToolResultErrorBlockParam, -) -from .code_execution_tool_result_block_content import ( - CodeExecutionToolResultBlockContent as CodeExecutionToolResultBlockContent, -) -from .citation_web_search_result_location_param import ( - CitationWebSearchResultLocationParam as CitationWebSearchResultLocationParam, -) -from .bash_code_execution_tool_result_error_code import ( - BashCodeExecutionToolResultErrorCode as BashCodeExecutionToolResultErrorCode, -) -from .tool_search_tool_search_result_block_param import ( - ToolSearchToolSearchResultBlockParam as ToolSearchToolSearchResultBlockParam, -) -from .bash_code_execution_tool_result_block_param import ( - BashCodeExecutionToolResultBlockParam as BashCodeExecutionToolResultBlockParam, -) -from .bash_code_execution_tool_result_error_param import ( - BashCodeExecutionToolResultErrorParam as BashCodeExecutionToolResultErrorParam, -) -from .encrypted_code_execution_result_block_param import ( - EncryptedCodeExecutionResultBlockParam as EncryptedCodeExecutionResultBlockParam, -) -from .text_editor_code_execution_tool_result_block import ( - TextEditorCodeExecutionToolResultBlock as TextEditorCodeExecutionToolResultBlock, -) -from .text_editor_code_execution_tool_result_error import ( - TextEditorCodeExecutionToolResultError as TextEditorCodeExecutionToolResultError, -) -from .text_editor_code_execution_view_result_block import ( - TextEditorCodeExecutionViewResultBlock as TextEditorCodeExecutionViewResultBlock, -) -from .text_editor_code_execution_create_result_block import ( - TextEditorCodeExecutionCreateResultBlock as TextEditorCodeExecutionCreateResultBlock, -) -from .web_search_tool_result_block_param_content_param import ( - WebSearchToolResultBlockParamContentParam as WebSearchToolResultBlockParamContentParam, -) -from .text_editor_code_execution_tool_result_error_code import ( - TextEditorCodeExecutionToolResultErrorCode as TextEditorCodeExecutionToolResultErrorCode, -) -from .text_editor_code_execution_tool_result_block_param import ( - TextEditorCodeExecutionToolResultBlockParam as TextEditorCodeExecutionToolResultBlockParam, -) -from .text_editor_code_execution_tool_result_error_param import ( - TextEditorCodeExecutionToolResultErrorParam as TextEditorCodeExecutionToolResultErrorParam, -) -from .text_editor_code_execution_view_result_block_param import ( - TextEditorCodeExecutionViewResultBlockParam as TextEditorCodeExecutionViewResultBlockParam, -) -from .text_editor_code_execution_str_replace_result_block import ( - TextEditorCodeExecutionStrReplaceResultBlock as TextEditorCodeExecutionStrReplaceResultBlock, -) -from .code_execution_tool_result_block_param_content_param import ( - CodeExecutionToolResultBlockParamContentParam as CodeExecutionToolResultBlockParamContentParam, -) -from .text_editor_code_execution_create_result_block_param import ( - TextEditorCodeExecutionCreateResultBlockParam as TextEditorCodeExecutionCreateResultBlockParam, -) -from .text_editor_code_execution_str_replace_result_block_param import ( - TextEditorCodeExecutionStrReplaceResultBlockParam as TextEditorCodeExecutionStrReplaceResultBlockParam, -) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/anthropic_beta_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/anthropic_beta_param.py deleted file mode 100644 index b54a7968..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/anthropic_beta_param.py +++ /dev/null @@ -1,37 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, TypeAlias - -__all__ = ["AnthropicBetaParam"] - -AnthropicBetaParam: TypeAlias = Union[ - str, - Literal[ - "message-batches-2024-09-24", - "prompt-caching-2024-07-31", - "computer-use-2024-10-22", - "computer-use-2025-01-24", - "pdfs-2024-09-25", - "token-counting-2024-11-01", - "token-efficient-tools-2025-02-19", - "output-128k-2025-02-19", - "files-api-2025-04-14", - "mcp-client-2025-04-04", - "mcp-client-2025-11-20", - "dev-full-thinking-2025-05-14", - "interleaved-thinking-2025-05-14", - "code-execution-2025-05-22", - "extended-cache-ttl-2025-04-11", - "context-1m-2025-08-07", - "context-management-2025-06-27", - "model-context-window-exceeded-2025-08-26", - "skills-2025-10-02", - "fast-mode-2026-02-01", - "output-300k-2026-03-24", - "user-profiles-2026-03-24", - "advisor-tool-2026-03-01", - ], -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/base64_image_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/base64_image_source_param.py deleted file mode 100644 index 93fdb9d1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/base64_image_source_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, Annotated, TypedDict - -from .._types import Base64FileInput -from .._utils import PropertyInfo -from .._models import set_pydantic_config - -__all__ = ["Base64ImageSourceParam"] - - -class Base64ImageSourceParam(TypedDict, total=False): - data: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]] - - media_type: Required[Literal["image/jpeg", "image/png", "image/gif", "image/webp"]] - - type: Required[Literal["base64"]] - - -set_pydantic_config(Base64ImageSourceParam, {"arbitrary_types_allowed": True}) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/base64_pdf_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/base64_pdf_source.py deleted file mode 100644 index 2972dcf3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/base64_pdf_source.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["Base64PDFSource"] - - -class Base64PDFSource(BaseModel): - data: str - - media_type: Literal["application/pdf"] - - type: Literal["base64"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/base64_pdf_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/base64_pdf_source_param.py deleted file mode 100644 index ac247a19..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/base64_pdf_source_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, Annotated, TypedDict - -from .._types import Base64FileInput -from .._utils import PropertyInfo -from .._models import set_pydantic_config - -__all__ = ["Base64PDFSourceParam"] - - -class Base64PDFSourceParam(TypedDict, total=False): - data: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]] - - media_type: Required[Literal["application/pdf"]] - - type: Required[Literal["base64"]] - - -set_pydantic_config(Base64PDFSourceParam, {"arbitrary_types_allowed": True}) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_output_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_output_block.py deleted file mode 100644 index 02d492ae..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_output_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BashCodeExecutionOutputBlock"] - - -class BashCodeExecutionOutputBlock(BaseModel): - file_id: str - - type: Literal["bash_code_execution_output"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_output_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_output_block_param.py deleted file mode 100644 index ec8dc25d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_output_block_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BashCodeExecutionOutputBlockParam"] - - -class BashCodeExecutionOutputBlockParam(TypedDict, total=False): - file_id: Required[str] - - type: Required[Literal["bash_code_execution_output"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_result_block.py deleted file mode 100644 index bbc5c5bf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_result_block.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from .._models import BaseModel -from .bash_code_execution_output_block import BashCodeExecutionOutputBlock - -__all__ = ["BashCodeExecutionResultBlock"] - - -class BashCodeExecutionResultBlock(BaseModel): - content: List[BashCodeExecutionOutputBlock] - - return_code: int - - stderr: str - - stdout: str - - type: Literal["bash_code_execution_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_result_block_param.py deleted file mode 100644 index f09bdc4a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_result_block_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Literal, Required, TypedDict - -from .bash_code_execution_output_block_param import BashCodeExecutionOutputBlockParam - -__all__ = ["BashCodeExecutionResultBlockParam"] - - -class BashCodeExecutionResultBlockParam(TypedDict, total=False): - content: Required[Iterable[BashCodeExecutionOutputBlockParam]] - - return_code: Required[int] - - stderr: Required[str] - - stdout: Required[str] - - type: Required[Literal["bash_code_execution_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_block.py deleted file mode 100644 index eb928cdf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_block.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, TypeAlias - -from .._models import BaseModel -from .bash_code_execution_result_block import BashCodeExecutionResultBlock -from .bash_code_execution_tool_result_error import BashCodeExecutionToolResultError - -__all__ = ["BashCodeExecutionToolResultBlock", "Content"] - -Content: TypeAlias = Union[BashCodeExecutionToolResultError, BashCodeExecutionResultBlock] - - -class BashCodeExecutionToolResultBlock(BaseModel): - content: Content - - tool_use_id: str - - type: Literal["bash_code_execution_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_block_param.py deleted file mode 100644 index 6acd4160..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_block_param.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam -from .bash_code_execution_result_block_param import BashCodeExecutionResultBlockParam -from .bash_code_execution_tool_result_error_param import BashCodeExecutionToolResultErrorParam - -__all__ = ["BashCodeExecutionToolResultBlockParam", "Content"] - -Content: TypeAlias = Union[BashCodeExecutionToolResultErrorParam, BashCodeExecutionResultBlockParam] - - -class BashCodeExecutionToolResultBlockParam(TypedDict, total=False): - content: Required[Content] - - tool_use_id: Required[str] - - type: Required[Literal["bash_code_execution_tool_result"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_error.py deleted file mode 100644 index dfe8ff7a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_error.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel -from .bash_code_execution_tool_result_error_code import BashCodeExecutionToolResultErrorCode - -__all__ = ["BashCodeExecutionToolResultError"] - - -class BashCodeExecutionToolResultError(BaseModel): - error_code: BashCodeExecutionToolResultErrorCode - - type: Literal["bash_code_execution_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_error_code.py b/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_error_code.py deleted file mode 100644 index 90d8ca1f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_error_code.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["BashCodeExecutionToolResultErrorCode"] - -BashCodeExecutionToolResultErrorCode: TypeAlias = Literal[ - "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "output_file_too_large" -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_error_param.py deleted file mode 100644 index 7aca1de5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/bash_code_execution_tool_result_error_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .bash_code_execution_tool_result_error_code import BashCodeExecutionToolResultErrorCode - -__all__ = ["BashCodeExecutionToolResultErrorParam"] - - -class BashCodeExecutionToolResultErrorParam(TypedDict, total=False): - error_code: Required[BashCodeExecutionToolResultErrorCode] - - type: Required[Literal["bash_code_execution_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/__init__.py deleted file mode 100644 index cb192466..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/__init__.py +++ /dev/null @@ -1,496 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .beta_skill import BetaSkill as BetaSkill -from .beta_usage import BetaUsage as BetaUsage -from .beta_message import BetaMessage as BetaMessage -from .deleted_file import DeletedFile as DeletedFile -from .beta_packages import BetaPackages as BetaPackages -from .file_metadata import FileMetadata as FileMetadata -from .beta_container import BetaContainer as BetaContainer -from .beta_file_scope import BetaFileScope as BetaFileScope -from .beta_model_info import BetaModelInfo as BetaModelInfo -from .beta_text_block import BetaTextBlock as BetaTextBlock -from .beta_text_delta import BetaTextDelta as BetaTextDelta -from .beta_tool_param import BetaToolParam as BetaToolParam -from .beta_environment import BetaEnvironment as BetaEnvironment -from .beta_stop_reason import BetaStopReason as BetaStopReason -from .file_list_params import FileListParams as FileListParams -from .agent_list_params import AgentListParams as AgentListParams -from .beta_cloud_config import BetaCloudConfig as BetaCloudConfig -from .beta_skill_params import BetaSkillParams as BetaSkillParams -from .beta_user_profile import BetaUserProfile as BetaUserProfile -from .model_list_params import ModelListParams as ModelListParams -from .skill_list_params import SkillListParams as SkillListParams -from .vault_list_params import VaultListParams as VaultListParams -from .beta_content_block import BetaContentBlock as BetaContentBlock -from .beta_direct_caller import BetaDirectCaller as BetaDirectCaller -from .beta_message_param import BetaMessageParam as BetaMessageParam -from .beta_text_citation import BetaTextCitation as BetaTextCitation -from .file_upload_params import FileUploadParams as FileUploadParams -from .agent_create_params import AgentCreateParams as AgentCreateParams -from .agent_update_params import AgentUpdateParams as AgentUpdateParams -from .beta_cache_creation import BetaCacheCreation as BetaCacheCreation -from .beta_document_block import BetaDocumentBlock as BetaDocumentBlock -from .beta_metadata_param import BetaMetadataParam as BetaMetadataParam -from .beta_thinking_block import BetaThinkingBlock as BetaThinkingBlock -from .beta_thinking_delta import BetaThinkingDelta as BetaThinkingDelta -from .beta_thinking_types import BetaThinkingTypes as BetaThinkingTypes -from .beta_tool_use_block import BetaToolUseBlock as BetaToolUseBlock -from .session_list_params import SessionListParams as SessionListParams -from .skill_create_params import SkillCreateParams as SkillCreateParams -from .skill_list_response import SkillListResponse as SkillListResponse -from .vault_create_params import VaultCreateParams as VaultCreateParams -from .vault_update_params import VaultUpdateParams as VaultUpdateParams -from .beta_citation_config import BetaCitationConfig as BetaCitationConfig -from .beta_citations_delta import BetaCitationsDelta as BetaCitationsDelta -from .beta_limited_network import BetaLimitedNetwork as BetaLimitedNetwork -from .beta_packages_params import BetaPackagesParams as BetaPackagesParams -from .beta_signature_delta import BetaSignatureDelta as BetaSignatureDelta -from .beta_web_fetch_block import BetaWebFetchBlock as BetaWebFetchBlock -from .agent_retrieve_params import AgentRetrieveParams as AgentRetrieveParams -from .beta_compaction_block import BetaCompactionBlock as BetaCompactionBlock -from .beta_container_params import BetaContainerParams as BetaContainerParams -from .beta_input_json_delta import BetaInputJSONDelta as BetaInputJSONDelta -from .beta_iterations_usage import BetaIterationsUsage as BetaIterationsUsage -from .beta_text_block_param import BetaTextBlockParam as BetaTextBlockParam -from .beta_tool_union_param import BetaToolUnionParam as BetaToolUnionParam -from .message_create_params import MessageCreateParams as MessageCreateParams -from .session_create_params import SessionCreateParams as SessionCreateParams -from .session_update_params import SessionUpdateParams as SessionUpdateParams -from .skill_create_response import SkillCreateResponse as SkillCreateResponse -from .skill_delete_response import SkillDeleteResponse as SkillDeleteResponse -from .beta_base64_pdf_source import BetaBase64PDFSource as BetaBase64PDFSource -from .beta_effort_capability import BetaEffortCapability as BetaEffortCapability -from .beta_image_block_param import BetaImageBlockParam as BetaImageBlockParam -from .beta_mcp_toolset_param import BetaMCPToolsetParam as BetaMCPToolsetParam -from .beta_plain_text_source import BetaPlainTextSource as BetaPlainTextSource -from .beta_server_tool_usage import BetaServerToolUsage as BetaServerToolUsage -from .beta_tool_choice_param import BetaToolChoiceParam as BetaToolChoiceParam -from .beta_capability_support import BetaCapabilitySupport as BetaCapabilitySupport -from .beta_mcp_tool_use_block import BetaMCPToolUseBlock as BetaMCPToolUseBlock -from .beta_model_capabilities import BetaModelCapabilities as BetaModelCapabilities -from .beta_server_tool_caller import BetaServerToolCaller as BetaServerToolCaller -from .environment_list_params import EnvironmentListParams as EnvironmentListParams -from .skill_retrieve_response import SkillRetrieveResponse as SkillRetrieveResponse -from .beta_cloud_config_params import BetaCloudConfigParams as BetaCloudConfigParams -from .beta_content_block_param import BetaContentBlockParam as BetaContentBlockParam -from .beta_direct_caller_param import BetaDirectCallerParam as BetaDirectCallerParam -from .beta_message_delta_usage import BetaMessageDeltaUsage as BetaMessageDeltaUsage -from .beta_output_config_param import BetaOutputConfigParam as BetaOutputConfigParam -from .beta_text_citation_param import BetaTextCitationParam as BetaTextCitationParam -from .beta_thinking_capability import BetaThinkingCapability as BetaThinkingCapability -from .beta_user_location_param import BetaUserLocationParam as BetaUserLocationParam -from .memory_store_list_params import MemoryStoreListParams as MemoryStoreListParams -from .user_profile_list_params import UserProfileListParams as UserProfileListParams -from .beta_advisor_result_block import BetaAdvisorResultBlock as BetaAdvisorResultBlock -from .beta_managed_agents_agent import BetaManagedAgentsAgent as BetaManagedAgentsAgent -from .beta_managed_agents_model import BetaManagedAgentsModel as BetaManagedAgentsModel -from .beta_managed_agents_vault import BetaManagedAgentsVault as BetaManagedAgentsVault -from .beta_message_tokens_count import BetaMessageTokensCount as BetaMessageTokensCount -from .beta_refusal_stop_details import BetaRefusalStopDetails as BetaRefusalStopDetails -from .beta_thinking_block_param import BetaThinkingBlockParam as BetaThinkingBlockParam -from .beta_thinking_turns_param import BetaThinkingTurnsParam as BetaThinkingTurnsParam -from .beta_tool_reference_block import BetaToolReferenceBlock as BetaToolReferenceBlock -from .beta_tool_use_block_param import BetaToolUseBlockParam as BetaToolUseBlockParam -from .beta_tool_uses_keep_param import BetaToolUsesKeepParam as BetaToolUsesKeepParam -from .beta_unrestricted_network import BetaUnrestrictedNetwork as BetaUnrestrictedNetwork -from .beta_url_pdf_source_param import BetaURLPDFSourceParam as BetaURLPDFSourceParam -from .environment_create_params import EnvironmentCreateParams as EnvironmentCreateParams -from .environment_update_params import EnvironmentUpdateParams as EnvironmentUpdateParams -from .beta_mcp_tool_config_param import BetaMCPToolConfigParam as BetaMCPToolConfigParam -from .beta_mcp_tool_result_block import BetaMCPToolResultBlock as BetaMCPToolResultBlock -from .beta_server_tool_use_block import BetaServerToolUseBlock as BetaServerToolUseBlock -from .beta_thinking_config_param import BetaThinkingConfigParam as BetaThinkingConfigParam -from .beta_tool_choice_any_param import BetaToolChoiceAnyParam as BetaToolChoiceAnyParam -from .beta_web_fetch_block_param import BetaWebFetchBlockParam as BetaWebFetchBlockParam -from .memory_store_create_params import MemoryStoreCreateParams as MemoryStoreCreateParams -from .memory_store_update_params import MemoryStoreUpdateParams as MemoryStoreUpdateParams -from .user_profile_create_params import UserProfileCreateParams as UserProfileCreateParams -from .user_profile_update_params import UserProfileUpdateParams as UserProfileUpdateParams -from .beta_base64_pdf_block_param import BetaBase64PDFBlockParam as BetaBase64PDFBlockParam -from .beta_citation_char_location import BetaCitationCharLocation as BetaCitationCharLocation -from .beta_citation_page_location import BetaCitationPageLocation as BetaCitationPageLocation -from .beta_citations_config_param import BetaCitationsConfigParam as BetaCitationsConfigParam -from .beta_compaction_block_param import BetaCompactionBlockParam as BetaCompactionBlockParam -from .beta_container_upload_block import BetaContainerUploadBlock as BetaContainerUploadBlock -from .beta_limited_network_params import BetaLimitedNetworkParams as BetaLimitedNetworkParams -from .beta_managed_agents_session import BetaManagedAgentsSession as BetaManagedAgentsSession -from .beta_raw_message_stop_event import BetaRawMessageStopEvent as BetaRawMessageStopEvent -from .beta_tool_choice_auto_param import BetaToolChoiceAutoParam as BetaToolChoiceAutoParam -from .beta_tool_choice_none_param import BetaToolChoiceNoneParam as BetaToolChoiceNoneParam -from .beta_tool_choice_tool_param import BetaToolChoiceToolParam as BetaToolChoiceToolParam -from .beta_url_image_source_param import BetaURLImageSourceParam as BetaURLImageSourceParam -from .message_count_tokens_params import MessageCountTokensParams as MessageCountTokensParams -from .beta_base64_pdf_source_param import BetaBase64PDFSourceParam as BetaBase64PDFSourceParam -from .beta_file_image_source_param import BetaFileImageSourceParam as BetaFileImageSourceParam -from .beta_message_iteration_usage import BetaMessageIterationUsage as BetaMessageIterationUsage -from .beta_plain_text_source_param import BetaPlainTextSourceParam as BetaPlainTextSourceParam -from .beta_raw_content_block_delta import BetaRawContentBlockDelta as BetaRawContentBlockDelta -from .beta_raw_message_delta_event import BetaRawMessageDeltaEvent as BetaRawMessageDeltaEvent -from .beta_raw_message_start_event import BetaRawMessageStartEvent as BetaRawMessageStartEvent -from .beta_redacted_thinking_block import BetaRedactedThinkingBlock as BetaRedactedThinkingBlock -from .beta_token_task_budget_param import BetaTokenTaskBudgetParam as BetaTokenTaskBudgetParam -from .beta_tool_result_block_param import BetaToolResultBlockParam as BetaToolResultBlockParam -from .beta_tool_uses_trigger_param import BetaToolUsesTriggerParam as BetaToolUsesTriggerParam -from .beta_web_search_result_block import BetaWebSearchResultBlock as BetaWebSearchResultBlock -from .beta_all_thinking_turns_param import BetaAllThinkingTurnsParam as BetaAllThinkingTurnsParam -from .beta_json_output_format_param import BetaJSONOutputFormatParam as BetaJSONOutputFormatParam -from .beta_mcp_tool_use_block_param import BetaMCPToolUseBlockParam as BetaMCPToolUseBlockParam -from .beta_raw_message_stream_event import BetaRawMessageStreamEvent as BetaRawMessageStreamEvent -from .beta_server_tool_caller_param import BetaServerToolCallerParam as BetaServerToolCallerParam -from .beta_tool_bash_20241022_param import BetaToolBash20241022Param as BetaToolBash20241022Param -from .beta_tool_bash_20250124_param import BetaToolBash20250124Param as BetaToolBash20250124Param -from .beta_user_profile_trust_grant import BetaUserProfileTrustGrant as BetaUserProfileTrustGrant -from .beta_advisor_tool_result_block import BetaAdvisorToolResultBlock as BetaAdvisorToolResultBlock -from .beta_advisor_tool_result_error import BetaAdvisorToolResultError as BetaAdvisorToolResultError -from .beta_base64_image_source_param import BetaBase64ImageSourceParam as BetaBase64ImageSourceParam -from .beta_search_result_block_param import BetaSearchResultBlockParam as BetaSearchResultBlockParam -from .beta_advisor_result_block_param import BetaAdvisorResultBlockParam as BetaAdvisorResultBlockParam -from .beta_compaction_iteration_usage import BetaCompactionIterationUsage as BetaCompactionIterationUsage -from .beta_content_block_source_param import BetaContentBlockSourceParam as BetaContentBlockSourceParam -from .beta_file_document_source_param import BetaFileDocumentSourceParam as BetaFileDocumentSourceParam -from .beta_input_tokens_trigger_param import BetaInputTokensTriggerParam as BetaInputTokensTriggerParam -from .beta_managed_agents_custom_tool import BetaManagedAgentsCustomTool as BetaManagedAgentsCustomTool -from .beta_managed_agents_mcp_toolset import BetaManagedAgentsMCPToolset as BetaManagedAgentsMCPToolset -from .beta_managed_agents_model_param import BetaManagedAgentsModelParam as BetaManagedAgentsModelParam -from .beta_memory_tool_20250818_param import BetaMemoryTool20250818Param as BetaMemoryTool20250818Param -from .beta_tool_reference_block_param import BetaToolReferenceBlockParam as BetaToolReferenceBlockParam -from .beta_unrestricted_network_param import BetaUnrestrictedNetworkParam as BetaUnrestrictedNetworkParam -from .beta_advisor_tool_20260301_param import BetaAdvisorTool20260301Param as BetaAdvisorTool20260301Param -from .beta_code_execution_output_block import BetaCodeExecutionOutputBlock as BetaCodeExecutionOutputBlock -from .beta_code_execution_result_block import BetaCodeExecutionResultBlock as BetaCodeExecutionResultBlock -from .beta_compact_20260112_edit_param import BetaCompact20260112EditParam as BetaCompact20260112EditParam -from .beta_context_management_response import BetaContextManagementResponse as BetaContextManagementResponse -from .beta_environment_delete_response import BetaEnvironmentDeleteResponse as BetaEnvironmentDeleteResponse -from .beta_managed_agents_agent_params import BetaManagedAgentsAgentParams as BetaManagedAgentsAgentParams -from .beta_managed_agents_custom_skill import BetaManagedAgentsCustomSkill as BetaManagedAgentsCustomSkill -from .beta_managed_agents_memory_store import BetaManagedAgentsMemoryStore as BetaManagedAgentsMemoryStore -from .beta_managed_agents_model_config import BetaManagedAgentsModelConfig as BetaManagedAgentsModelConfig -from .beta_managed_agents_skill_params import BetaManagedAgentsSkillParams as BetaManagedAgentsSkillParams -from .beta_server_tool_caller_20260120 import BetaServerToolCaller20260120 as BetaServerToolCaller20260120 -from .beta_server_tool_use_block_param import BetaServerToolUseBlockParam as BetaServerToolUseBlockParam -from .beta_user_profile_enrollment_url import BetaUserProfileEnrollmentURL as BetaUserProfileEnrollmentURL -from .beta_web_fetch_tool_result_block import BetaWebFetchToolResultBlock as BetaWebFetchToolResultBlock -from .beta_citation_char_location_param import BetaCitationCharLocationParam as BetaCitationCharLocationParam -from .beta_citation_page_location_param import BetaCitationPageLocationParam as BetaCitationPageLocationParam -from .beta_container_upload_block_param import BetaContainerUploadBlockParam as BetaContainerUploadBlockParam -from .beta_managed_agents_deleted_vault import BetaManagedAgentsDeletedVault as BetaManagedAgentsDeletedVault -from .beta_managed_agents_session_agent import BetaManagedAgentsSessionAgent as BetaManagedAgentsSessionAgent -from .beta_managed_agents_session_stats import BetaManagedAgentsSessionStats as BetaManagedAgentsSessionStats -from .beta_managed_agents_session_usage import BetaManagedAgentsSessionUsage as BetaManagedAgentsSessionUsage -from .beta_memory_tool_20250818_command import BetaMemoryTool20250818Command as BetaMemoryTool20250818Command -from .beta_raw_content_block_stop_event import BetaRawContentBlockStopEvent as BetaRawContentBlockStopEvent -from .beta_request_document_block_param import BetaRequestDocumentBlockParam as BetaRequestDocumentBlockParam -from .beta_web_search_tool_result_block import BetaWebSearchToolResultBlock as BetaWebSearchToolResultBlock -from .beta_web_search_tool_result_error import BetaWebSearchToolResultError as BetaWebSearchToolResultError -from .beta_advisor_redacted_result_block import BetaAdvisorRedactedResultBlock as BetaAdvisorRedactedResultBlock -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam as BetaCacheControlEphemeralParam -from .beta_context_management_capability import BetaContextManagementCapability as BetaContextManagementCapability -from .beta_mcp_tool_default_config_param import BetaMCPToolDefaultConfigParam as BetaMCPToolDefaultConfigParam -from .beta_raw_content_block_delta_event import BetaRawContentBlockDeltaEvent as BetaRawContentBlockDeltaEvent -from .beta_raw_content_block_start_event import BetaRawContentBlockStartEvent as BetaRawContentBlockStartEvent -from .beta_redacted_thinking_block_param import BetaRedactedThinkingBlockParam as BetaRedactedThinkingBlockParam -from .beta_thinking_config_enabled_param import BetaThinkingConfigEnabledParam as BetaThinkingConfigEnabledParam -from .beta_tool_search_tool_result_block import BetaToolSearchToolResultBlock as BetaToolSearchToolResultBlock -from .beta_tool_search_tool_result_error import BetaToolSearchToolResultError as BetaToolSearchToolResultError -from .beta_web_fetch_tool_20250910_param import BetaWebFetchTool20250910Param as BetaWebFetchTool20250910Param -from .beta_web_fetch_tool_20260209_param import BetaWebFetchTool20260209Param as BetaWebFetchTool20260209Param -from .beta_web_fetch_tool_20260309_param import BetaWebFetchTool20260309Param as BetaWebFetchTool20260309Param -from .beta_web_search_result_block_param import BetaWebSearchResultBlockParam as BetaWebSearchResultBlockParam -from .beta_compaction_content_block_delta import BetaCompactionContentBlockDelta as BetaCompactionContentBlockDelta -from .beta_managed_agents_anthropic_skill import BetaManagedAgentsAnthropicSkill as BetaManagedAgentsAnthropicSkill -from .beta_managed_agents_branch_checkout import BetaManagedAgentsBranchCheckout as BetaManagedAgentsBranchCheckout -from .beta_managed_agents_commit_checkout import BetaManagedAgentsCommitCheckout as BetaManagedAgentsCommitCheckout -from .beta_managed_agents_deleted_session import BetaManagedAgentsDeletedSession as BetaManagedAgentsDeletedSession -from .beta_managed_agents_mcp_tool_config import BetaManagedAgentsMCPToolConfig as BetaManagedAgentsMCPToolConfig -from .beta_thinking_config_adaptive_param import BetaThinkingConfigAdaptiveParam as BetaThinkingConfigAdaptiveParam -from .beta_thinking_config_disabled_param import BetaThinkingConfigDisabledParam as BetaThinkingConfigDisabledParam -from .beta_web_search_tool_20250305_param import BetaWebSearchTool20250305Param as BetaWebSearchTool20250305Param -from .beta_web_search_tool_20260209_param import BetaWebSearchTool20260209Param as BetaWebSearchTool20260209Param -from .beta_advisor_message_iteration_usage import BetaAdvisorMessageIterationUsage as BetaAdvisorMessageIterationUsage -from .beta_advisor_tool_result_block_param import BetaAdvisorToolResultBlockParam as BetaAdvisorToolResultBlockParam -from .beta_advisor_tool_result_error_param import BetaAdvisorToolResultErrorParam as BetaAdvisorToolResultErrorParam -from .beta_citation_content_block_location import BetaCitationContentBlockLocation as BetaCitationContentBlockLocation -from .beta_citation_search_result_location import BetaCitationSearchResultLocation as BetaCitationSearchResultLocation -from .beta_context_management_config_param import BetaContextManagementConfigParam as BetaContextManagementConfigParam -from .beta_tool_text_editor_20241022_param import BetaToolTextEditor20241022Param as BetaToolTextEditor20241022Param -from .beta_tool_text_editor_20250124_param import BetaToolTextEditor20250124Param as BetaToolTextEditor20250124Param -from .beta_tool_text_editor_20250429_param import BetaToolTextEditor20250429Param as BetaToolTextEditor20250429Param -from .beta_tool_text_editor_20250728_param import BetaToolTextEditor20250728Param as BetaToolTextEditor20250728Param -from .beta_bash_code_execution_output_block import BetaBashCodeExecutionOutputBlock as BetaBashCodeExecutionOutputBlock -from .beta_bash_code_execution_result_block import BetaBashCodeExecutionResultBlock as BetaBashCodeExecutionResultBlock -from .beta_code_execution_tool_result_block import BetaCodeExecutionToolResultBlock as BetaCodeExecutionToolResultBlock -from .beta_code_execution_tool_result_error import BetaCodeExecutionToolResultError as BetaCodeExecutionToolResultError -from .beta_managed_agents_agent_tool_config import BetaManagedAgentsAgentToolConfig as BetaManagedAgentsAgentToolConfig -from .beta_managed_agents_always_ask_policy import BetaManagedAgentsAlwaysAskPolicy as BetaManagedAgentsAlwaysAskPolicy -from .beta_tool_computer_use_20241022_param import BetaToolComputerUse20241022Param as BetaToolComputerUse20241022Param -from .beta_tool_computer_use_20250124_param import BetaToolComputerUse20250124Param as BetaToolComputerUse20250124Param -from .beta_tool_computer_use_20251124_param import BetaToolComputerUse20251124Param as BetaToolComputerUse20251124Param -from .beta_web_fetch_tool_result_error_code import BetaWebFetchToolResultErrorCode as BetaWebFetchToolResultErrorCode -from .beta_code_execution_output_block_param import ( - BetaCodeExecutionOutputBlockParam as BetaCodeExecutionOutputBlockParam, -) -from .beta_code_execution_result_block_param import ( - BetaCodeExecutionResultBlockParam as BetaCodeExecutionResultBlockParam, -) -from .beta_input_tokens_clear_at_least_param import BetaInputTokensClearAtLeastParam as BetaInputTokensClearAtLeastParam -from .beta_managed_agents_custom_tool_params import ( - BetaManagedAgentsCustomToolParams as BetaManagedAgentsCustomToolParams, -) -from .beta_managed_agents_mcp_toolset_params import ( - BetaManagedAgentsMCPToolsetParams as BetaManagedAgentsMCPToolsetParams, -) -from .beta_memory_tool_20250818_view_command import ( - BetaMemoryTool20250818ViewCommand as BetaMemoryTool20250818ViewCommand, -) -from .beta_server_tool_caller_20260120_param import ( - BetaServerToolCaller20260120Param as BetaServerToolCaller20260120Param, -) -from .beta_web_fetch_tool_result_block_param import BetaWebFetchToolResultBlockParam as BetaWebFetchToolResultBlockParam -from .beta_web_fetch_tool_result_error_block import BetaWebFetchToolResultErrorBlock as BetaWebFetchToolResultErrorBlock -from .beta_web_search_tool_result_error_code import BetaWebSearchToolResultErrorCode as BetaWebSearchToolResultErrorCode -from .beta_clear_thinking_20251015_edit_param import ( - BetaClearThinking20251015EditParam as BetaClearThinking20251015EditParam, -) -from .beta_code_execution_tool_20250522_param import ( - BetaCodeExecutionTool20250522Param as BetaCodeExecutionTool20250522Param, -) -from .beta_code_execution_tool_20250825_param import ( - BetaCodeExecutionTool20250825Param as BetaCodeExecutionTool20250825Param, -) -from .beta_code_execution_tool_20260120_param import ( - BetaCodeExecutionTool20260120Param as BetaCodeExecutionTool20260120Param, -) -from .beta_content_block_source_content_param import ( - BetaContentBlockSourceContentParam as BetaContentBlockSourceContentParam, -) -from .beta_managed_agents_always_allow_policy import ( - BetaManagedAgentsAlwaysAllowPolicy as BetaManagedAgentsAlwaysAllowPolicy, -) -from .beta_managed_agents_custom_skill_params import ( - BetaManagedAgentsCustomSkillParams as BetaManagedAgentsCustomSkillParams, -) -from .beta_managed_agents_model_config_params import ( - BetaManagedAgentsModelConfigParams as BetaManagedAgentsModelConfigParams, -) -from .beta_web_search_tool_result_block_param import ( - BetaWebSearchToolResultBlockParam as BetaWebSearchToolResultBlockParam, -) -from .beta_advisor_redacted_result_block_param import ( - BetaAdvisorRedactedResultBlockParam as BetaAdvisorRedactedResultBlockParam, -) -from .beta_clear_tool_uses_20250919_edit_param import ( - BetaClearToolUses20250919EditParam as BetaClearToolUses20250919EditParam, -) -from .beta_managed_agents_cache_creation_usage import ( - BetaManagedAgentsCacheCreationUsage as BetaManagedAgentsCacheCreationUsage, -) -from .beta_managed_agents_deleted_memory_store import ( - BetaManagedAgentsDeletedMemoryStore as BetaManagedAgentsDeletedMemoryStore, -) -from .beta_managed_agents_file_resource_params import ( - BetaManagedAgentsFileResourceParams as BetaManagedAgentsFileResourceParams, -) -from .beta_memory_tool_20250818_create_command import ( - BetaMemoryTool20250818CreateCommand as BetaMemoryTool20250818CreateCommand, -) -from .beta_memory_tool_20250818_delete_command import ( - BetaMemoryTool20250818DeleteCommand as BetaMemoryTool20250818DeleteCommand, -) -from .beta_memory_tool_20250818_insert_command import ( - BetaMemoryTool20250818InsertCommand as BetaMemoryTool20250818InsertCommand, -) -from .beta_memory_tool_20250818_rename_command import ( - BetaMemoryTool20250818RenameCommand as BetaMemoryTool20250818RenameCommand, -) -from .beta_request_mcp_tool_result_block_param import ( - BetaRequestMCPToolResultBlockParam as BetaRequestMCPToolResultBlockParam, -) -from .beta_tool_search_tool_result_block_param import ( - BetaToolSearchToolResultBlockParam as BetaToolSearchToolResultBlockParam, -) -from .beta_tool_search_tool_result_error_param import ( - BetaToolSearchToolResultErrorParam as BetaToolSearchToolResultErrorParam, -) -from .beta_web_search_tool_request_error_param import ( - BetaWebSearchToolRequestErrorParam as BetaWebSearchToolRequestErrorParam, -) -from .beta_citations_web_search_result_location import ( - BetaCitationsWebSearchResultLocation as BetaCitationsWebSearchResultLocation, -) -from .beta_managed_agents_agent_toolset20260401 import ( - BetaManagedAgentsAgentToolset20260401 as BetaManagedAgentsAgentToolset20260401, -) -from .beta_managed_agents_branch_checkout_param import ( - BetaManagedAgentsBranchCheckoutParam as BetaManagedAgentsBranchCheckoutParam, -) -from .beta_managed_agents_commit_checkout_param import ( - BetaManagedAgentsCommitCheckoutParam as BetaManagedAgentsCommitCheckoutParam, -) -from .beta_managed_agents_url_mcp_server_params import ( - BetaManagedAgentsURLMCPServerParams as BetaManagedAgentsURLMCPServerParams, -) -from .beta_tool_search_tool_bm25_20251119_param import ( - BetaToolSearchToolBm25_20251119Param as BetaToolSearchToolBm25_20251119Param, -) -from .beta_tool_search_tool_search_result_block import ( - BetaToolSearchToolSearchResultBlock as BetaToolSearchToolSearchResultBlock, -) -from .beta_web_search_tool_result_block_content import ( - BetaWebSearchToolResultBlockContent as BetaWebSearchToolResultBlockContent, -) -from .beta_bash_code_execution_tool_result_block import ( - BetaBashCodeExecutionToolResultBlock as BetaBashCodeExecutionToolResultBlock, -) -from .beta_bash_code_execution_tool_result_error import ( - BetaBashCodeExecutionToolResultError as BetaBashCodeExecutionToolResultError, -) -from .beta_citation_content_block_location_param import ( - BetaCitationContentBlockLocationParam as BetaCitationContentBlockLocationParam, -) -from .beta_citation_search_result_location_param import ( - BetaCitationSearchResultLocationParam as BetaCitationSearchResultLocationParam, -) -from .beta_clear_thinking_20251015_edit_response import ( - BetaClearThinking20251015EditResponse as BetaClearThinking20251015EditResponse, -) -from .beta_code_execution_tool_result_error_code import ( - BetaCodeExecutionToolResultErrorCode as BetaCodeExecutionToolResultErrorCode, -) -from .beta_encrypted_code_execution_result_block import ( - BetaEncryptedCodeExecutionResultBlock as BetaEncryptedCodeExecutionResultBlock, -) -from .beta_managed_agents_anthropic_skill_params import ( - BetaManagedAgentsAnthropicSkillParams as BetaManagedAgentsAnthropicSkillParams, -) -from .beta_managed_agents_mcp_tool_config_params import ( - BetaManagedAgentsMCPToolConfigParams as BetaManagedAgentsMCPToolConfigParams, -) -from .beta_tool_search_tool_regex_20251119_param import ( - BetaToolSearchToolRegex20251119Param as BetaToolSearchToolRegex20251119Param, -) -from .beta_bash_code_execution_output_block_param import ( - BetaBashCodeExecutionOutputBlockParam as BetaBashCodeExecutionOutputBlockParam, -) -from .beta_bash_code_execution_result_block_param import ( - BetaBashCodeExecutionResultBlockParam as BetaBashCodeExecutionResultBlockParam, -) -from .beta_clear_tool_uses_20250919_edit_response import ( - BetaClearToolUses20250919EditResponse as BetaClearToolUses20250919EditResponse, -) -from .beta_code_execution_tool_result_block_param import ( - BetaCodeExecutionToolResultBlockParam as BetaCodeExecutionToolResultBlockParam, -) -from .beta_code_execution_tool_result_error_param import ( - BetaCodeExecutionToolResultErrorParam as BetaCodeExecutionToolResultErrorParam, -) -from .beta_managed_agents_always_ask_policy_param import ( - BetaManagedAgentsAlwaysAskPolicyParam as BetaManagedAgentsAlwaysAskPolicyParam, -) -from .beta_managed_agents_agent_tool_config_params import ( - BetaManagedAgentsAgentToolConfigParams as BetaManagedAgentsAgentToolConfigParams, -) -from .beta_managed_agents_custom_tool_input_schema import ( - BetaManagedAgentsCustomToolInputSchema as BetaManagedAgentsCustomToolInputSchema, -) -from .beta_request_mcp_server_url_definition_param import ( - BetaRequestMCPServerURLDefinitionParam as BetaRequestMCPServerURLDefinitionParam, -) -from .beta_web_fetch_tool_result_error_block_param import ( - BetaWebFetchToolResultErrorBlockParam as BetaWebFetchToolResultErrorBlockParam, -) -from .beta_code_execution_tool_result_block_content import ( - BetaCodeExecutionToolResultBlockContent as BetaCodeExecutionToolResultBlockContent, -) -from .beta_count_tokens_context_management_response import ( - BetaCountTokensContextManagementResponse as BetaCountTokensContextManagementResponse, -) -from .beta_managed_agents_always_allow_policy_param import ( - BetaManagedAgentsAlwaysAllowPolicyParam as BetaManagedAgentsAlwaysAllowPolicyParam, -) -from .beta_managed_agents_mcp_server_url_definition import ( - BetaManagedAgentsMCPServerURLDefinition as BetaManagedAgentsMCPServerURLDefinition, -) -from .beta_memory_tool_20250818_str_replace_command import ( - BetaMemoryTool20250818StrReplaceCommand as BetaMemoryTool20250818StrReplaceCommand, -) -from .beta_citation_web_search_result_location_param import ( - BetaCitationWebSearchResultLocationParam as BetaCitationWebSearchResultLocationParam, -) -from .beta_managed_agents_mcp_toolset_default_config import ( - BetaManagedAgentsMCPToolsetDefaultConfig as BetaManagedAgentsMCPToolsetDefaultConfig, -) -from .beta_managed_agents_memory_store_resource_param import ( - BetaManagedAgentsMemoryStoreResourceParam as BetaManagedAgentsMemoryStoreResourceParam, -) -from .beta_tool_search_tool_search_result_block_param import ( - BetaToolSearchToolSearchResultBlockParam as BetaToolSearchToolSearchResultBlockParam, -) -from .beta_bash_code_execution_tool_result_block_param import ( - BetaBashCodeExecutionToolResultBlockParam as BetaBashCodeExecutionToolResultBlockParam, -) -from .beta_bash_code_execution_tool_result_error_param import ( - BetaBashCodeExecutionToolResultErrorParam as BetaBashCodeExecutionToolResultErrorParam, -) -from .beta_encrypted_code_execution_result_block_param import ( - BetaEncryptedCodeExecutionResultBlockParam as BetaEncryptedCodeExecutionResultBlockParam, -) -from .beta_managed_agents_agent_toolset20260401_params import ( - BetaManagedAgentsAgentToolset20260401Params as BetaManagedAgentsAgentToolset20260401Params, -) -from .beta_managed_agents_agent_toolset_default_config import ( - BetaManagedAgentsAgentToolsetDefaultConfig as BetaManagedAgentsAgentToolsetDefaultConfig, -) -from .beta_request_mcp_server_tool_configuration_param import ( - BetaRequestMCPServerToolConfigurationParam as BetaRequestMCPServerToolConfigurationParam, -) -from .beta_text_editor_code_execution_tool_result_block import ( - BetaTextEditorCodeExecutionToolResultBlock as BetaTextEditorCodeExecutionToolResultBlock, -) -from .beta_text_editor_code_execution_tool_result_error import ( - BetaTextEditorCodeExecutionToolResultError as BetaTextEditorCodeExecutionToolResultError, -) -from .beta_text_editor_code_execution_view_result_block import ( - BetaTextEditorCodeExecutionViewResultBlock as BetaTextEditorCodeExecutionViewResultBlock, -) -from .beta_managed_agents_custom_tool_input_schema_param import ( - BetaManagedAgentsCustomToolInputSchemaParam as BetaManagedAgentsCustomToolInputSchemaParam, -) -from .beta_text_editor_code_execution_create_result_block import ( - BetaTextEditorCodeExecutionCreateResultBlock as BetaTextEditorCodeExecutionCreateResultBlock, -) -from .beta_managed_agents_github_repository_resource_params import ( - BetaManagedAgentsGitHubRepositoryResourceParams as BetaManagedAgentsGitHubRepositoryResourceParams, -) -from .beta_managed_agents_mcp_toolset_default_config_params import ( - BetaManagedAgentsMCPToolsetDefaultConfigParams as BetaManagedAgentsMCPToolsetDefaultConfigParams, -) -from .beta_web_search_tool_result_block_param_content_param import ( - BetaWebSearchToolResultBlockParamContentParam as BetaWebSearchToolResultBlockParamContentParam, -) -from .beta_managed_agents_agent_toolset_default_config_params import ( - BetaManagedAgentsAgentToolsetDefaultConfigParams as BetaManagedAgentsAgentToolsetDefaultConfigParams, -) -from .beta_text_editor_code_execution_tool_result_block_param import ( - BetaTextEditorCodeExecutionToolResultBlockParam as BetaTextEditorCodeExecutionToolResultBlockParam, -) -from .beta_text_editor_code_execution_tool_result_error_param import ( - BetaTextEditorCodeExecutionToolResultErrorParam as BetaTextEditorCodeExecutionToolResultErrorParam, -) -from .beta_text_editor_code_execution_view_result_block_param import ( - BetaTextEditorCodeExecutionViewResultBlockParam as BetaTextEditorCodeExecutionViewResultBlockParam, -) -from .beta_text_editor_code_execution_str_replace_result_block import ( - BetaTextEditorCodeExecutionStrReplaceResultBlock as BetaTextEditorCodeExecutionStrReplaceResultBlock, -) -from .beta_code_execution_tool_result_block_param_content_param import ( - BetaCodeExecutionToolResultBlockParamContentParam as BetaCodeExecutionToolResultBlockParamContentParam, -) -from .beta_text_editor_code_execution_create_result_block_param import ( - BetaTextEditorCodeExecutionCreateResultBlockParam as BetaTextEditorCodeExecutionCreateResultBlockParam, -) -from .beta_text_editor_code_execution_str_replace_result_block_param import ( - BetaTextEditorCodeExecutionStrReplaceResultBlockParam as BetaTextEditorCodeExecutionStrReplaceResultBlockParam, -) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_create_params.py deleted file mode 100644 index f8a128b3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_create_params.py +++ /dev/null @@ -1,69 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Union, Iterable, Optional -from typing_extensions import Required, Annotated, TypeAlias, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_model_param import BetaManagedAgentsModelParam -from .beta_managed_agents_skill_params import BetaManagedAgentsSkillParams -from .beta_managed_agents_custom_tool_params import BetaManagedAgentsCustomToolParams -from .beta_managed_agents_mcp_toolset_params import BetaManagedAgentsMCPToolsetParams -from .beta_managed_agents_model_config_params import BetaManagedAgentsModelConfigParams -from .beta_managed_agents_url_mcp_server_params import BetaManagedAgentsURLMCPServerParams -from .beta_managed_agents_agent_toolset20260401_params import BetaManagedAgentsAgentToolset20260401Params - -__all__ = ["AgentCreateParams", "Model", "Tool"] - - -class AgentCreateParams(TypedDict, total=False): - model: Required[Model] - """Model identifier. - - Accepts the - [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), - e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration - control - """ - - name: Required[str] - """Human-readable name for the agent. 1-256 characters.""" - - description: Optional[str] - """Description of what the agent does. Up to 2048 characters.""" - - mcp_servers: Iterable[BetaManagedAgentsURLMCPServerParams] - """MCP servers this agent connects to. - - Maximum 20. Names must be unique within the array. - """ - - metadata: Dict[str, str] - """Arbitrary key-value metadata. - - Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. - """ - - skills: Iterable[BetaManagedAgentsSkillParams] - """Skills available to the agent. Maximum 20.""" - - system: Optional[str] - """System prompt for the agent. Up to 100,000 characters.""" - - tools: Iterable[Tool] - """Tool configurations available to the agent. - - Maximum of 128 tools across all toolsets allowed. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" - - -Model: TypeAlias = Union[BetaManagedAgentsModelParam, BetaManagedAgentsModelConfigParams] - -Tool: TypeAlias = Union[ - BetaManagedAgentsAgentToolset20260401Params, BetaManagedAgentsMCPToolsetParams, BetaManagedAgentsCustomToolParams -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_list_params.py deleted file mode 100644 index b2954175..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_list_params.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Union -from datetime import datetime -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["AgentListParams"] - - -class AgentListParams(TypedDict, total=False): - created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] - """Return agents created at or after this time (inclusive).""" - - created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] - """Return agents created at or before this time (inclusive).""" - - include_archived: bool - """Include archived agents in results. Defaults to false.""" - - limit: int - """Maximum results per page. Default 20, maximum 100.""" - - page: str - """Opaque pagination cursor from a previous response.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_retrieve_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_retrieve_params.py deleted file mode 100644 index 037de968..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_retrieve_params.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["AgentRetrieveParams"] - - -class AgentRetrieveParams(TypedDict, total=False): - version: int - """Agent version. - - Omit for the most recent version. Must be at least 1 if specified. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_update_params.py deleted file mode 100644 index b206bb89..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agent_update_params.py +++ /dev/null @@ -1,90 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Union, Iterable, Optional -from typing_extensions import Required, Annotated, TypeAlias, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_model_param import BetaManagedAgentsModelParam -from .beta_managed_agents_skill_params import BetaManagedAgentsSkillParams -from .beta_managed_agents_custom_tool_params import BetaManagedAgentsCustomToolParams -from .beta_managed_agents_mcp_toolset_params import BetaManagedAgentsMCPToolsetParams -from .beta_managed_agents_model_config_params import BetaManagedAgentsModelConfigParams -from .beta_managed_agents_url_mcp_server_params import BetaManagedAgentsURLMCPServerParams -from .beta_managed_agents_agent_toolset20260401_params import BetaManagedAgentsAgentToolset20260401Params - -__all__ = ["AgentUpdateParams", "Model", "Tool"] - - -class AgentUpdateParams(TypedDict, total=False): - version: Required[int] - """The agent's current version, used to prevent concurrent overwrites. - - Obtain this value from a create or retrieve response. The request fails if this - does not match the server's current version. - """ - - description: Optional[str] - """Description. - - Up to 2048 characters. Omit to preserve; send empty string or null to clear. - """ - - mcp_servers: Optional[Iterable[BetaManagedAgentsURLMCPServerParams]] - """MCP servers. - - Full replacement. Omit to preserve; send empty array or null to clear. Names - must be unique. Maximum 20. - """ - - metadata: Optional[Dict[str, Optional[str]]] - """Metadata patch. - - Set a key to a string to upsert it, or to null to delete it. Omit the field to - preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values - up to 512 chars. - """ - - model: Model - """Model identifier. - - Accepts the - [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), - e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration - control. Omit to preserve. Cannot be cleared. - """ - - name: str - """Human-readable name. 1-256 characters. Omit to preserve. Cannot be cleared.""" - - skills: Optional[Iterable[BetaManagedAgentsSkillParams]] - """Skills. - - Full replacement. Omit to preserve; send empty array or null to clear. - Maximum 20. - """ - - system: Optional[str] - """System prompt. - - Up to 100,000 characters. Omit to preserve; send empty string or null to clear. - """ - - tools: Optional[Iterable[Tool]] - """Tool configurations available to the agent. - - Full replacement. Omit to preserve; send empty array or null to clear. Maximum - of 128 tools across all toolsets allowed. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" - - -Model: TypeAlias = Union[BetaManagedAgentsModelParam, BetaManagedAgentsModelConfigParams] - -Tool: TypeAlias = Union[ - BetaManagedAgentsAgentToolset20260401Params, BetaManagedAgentsMCPToolsetParams, BetaManagedAgentsCustomToolParams -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agents/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/agents/__init__.py deleted file mode 100644 index 184a387f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agents/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .version_list_params import VersionListParams as VersionListParams diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agents/version_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/agents/version_list_params.py deleted file mode 100644 index 29fc9542..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/agents/version_list_params.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam - -__all__ = ["VersionListParams"] - - -class VersionListParams(TypedDict, total=False): - limit: int - """Maximum results per page. Default 20, maximum 100.""" - - page: str - """Opaque pagination cursor.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_message_iteration_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_message_iteration_usage.py deleted file mode 100644 index 2bd662fb..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_message_iteration_usage.py +++ /dev/null @@ -1,39 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..model import Model -from ..._models import BaseModel -from .beta_cache_creation import BetaCacheCreation - -__all__ = ["BetaAdvisorMessageIterationUsage"] - - -class BetaAdvisorMessageIterationUsage(BaseModel): - """Token usage for an advisor sub-inference iteration.""" - - cache_creation: Optional[BetaCacheCreation] = None - """Breakdown of cached tokens by TTL""" - - cache_creation_input_tokens: int - """The number of input tokens used to create the cache entry.""" - - cache_read_input_tokens: int - """The number of input tokens read from the cache.""" - - input_tokens: int - """The number of input tokens which were used.""" - - model: Model - """ - The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - output_tokens: int - """The number of output tokens which were used.""" - - type: Literal["advisor_message"] - """Usage for an advisor sub-inference iteration""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_redacted_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_redacted_result_block.py deleted file mode 100644 index 62696c4b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_redacted_result_block.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaAdvisorRedactedResultBlock"] - - -class BetaAdvisorRedactedResultBlock(BaseModel): - encrypted_content: str - """Opaque blob containing the advisor's output. - - Round-trip verbatim; do not inspect or modify. - """ - - type: Literal["advisor_redacted_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_redacted_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_redacted_result_block_param.py deleted file mode 100644 index 0c1cfcad..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_redacted_result_block_param.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaAdvisorRedactedResultBlockParam"] - - -class BetaAdvisorRedactedResultBlockParam(TypedDict, total=False): - encrypted_content: Required[str] - """Opaque blob produced by a prior response; must be round-tripped verbatim.""" - - type: Required[Literal["advisor_redacted_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_result_block.py deleted file mode 100644 index 9f702ec3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_result_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaAdvisorResultBlock"] - - -class BetaAdvisorResultBlock(BaseModel): - text: str - - type: Literal["advisor_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_result_block_param.py deleted file mode 100644 index 4500eec9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_result_block_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaAdvisorResultBlockParam"] - - -class BetaAdvisorResultBlockParam(TypedDict, total=False): - text: Required[str] - - type: Required[Literal["advisor_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_20260301_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_20260301_param.py deleted file mode 100644 index d94dfbee..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_20260301_param.py +++ /dev/null @@ -1,53 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from ..model_param import ModelParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaAdvisorTool20260301Param"] - - -class BetaAdvisorTool20260301Param(TypedDict, total=False): - model: Required[ModelParam] - """ - The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - name: Required[Literal["advisor"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["advisor_20260301"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - caching: Optional[BetaCacheControlEphemeralParam] - """Caching for the advisor's own prompt. - - When set, each advisor call writes a cache entry at the given TTL so subsequent - calls in the same conversation read the stable prefix. When omitted, the advisor - prompt is not cached. - """ - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_block.py deleted file mode 100644 index 196d4766..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_block.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, TypeAlias - -from ..._models import BaseModel -from .beta_advisor_result_block import BetaAdvisorResultBlock -from .beta_advisor_tool_result_error import BetaAdvisorToolResultError -from .beta_advisor_redacted_result_block import BetaAdvisorRedactedResultBlock - -__all__ = ["BetaAdvisorToolResultBlock", "Content"] - -Content: TypeAlias = Union[BetaAdvisorToolResultError, BetaAdvisorResultBlock, BetaAdvisorRedactedResultBlock] - - -class BetaAdvisorToolResultBlock(BaseModel): - content: Content - - tool_use_id: str - - type: Literal["advisor_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_block_param.py deleted file mode 100644 index a5b702b2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_block_param.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_advisor_result_block_param import BetaAdvisorResultBlockParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_advisor_tool_result_error_param import BetaAdvisorToolResultErrorParam -from .beta_advisor_redacted_result_block_param import BetaAdvisorRedactedResultBlockParam - -__all__ = ["BetaAdvisorToolResultBlockParam", "Content"] - -Content: TypeAlias = Union[ - BetaAdvisorToolResultErrorParam, BetaAdvisorResultBlockParam, BetaAdvisorRedactedResultBlockParam -] - - -class BetaAdvisorToolResultBlockParam(TypedDict, total=False): - content: Required[Content] - - tool_use_id: Required[str] - - type: Required[Literal["advisor_tool_result"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_error.py deleted file mode 100644 index d317615f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_error.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaAdvisorToolResultError"] - - -class BetaAdvisorToolResultError(BaseModel): - error_code: Literal[ - "max_uses_exceeded", - "prompt_too_long", - "too_many_requests", - "overloaded", - "unavailable", - "execution_time_exceeded", - ] - - type: Literal["advisor_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_error_param.py deleted file mode 100644 index e64f87b3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_advisor_tool_result_error_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaAdvisorToolResultErrorParam"] - - -class BetaAdvisorToolResultErrorParam(TypedDict, total=False): - error_code: Required[ - Literal[ - "max_uses_exceeded", - "prompt_too_long", - "too_many_requests", - "overloaded", - "unavailable", - "execution_time_exceeded", - ] - ] - - type: Required[Literal["advisor_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_all_thinking_turns_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_all_thinking_turns_param.py deleted file mode 100644 index aae4eddc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_all_thinking_turns_param.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaAllThinkingTurnsParam"] - - -class BetaAllThinkingTurnsParam(TypedDict, total=False): - type: Required[Literal["all"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_image_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_image_source_param.py deleted file mode 100644 index 8f13ce38..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_image_source_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, Annotated, TypedDict - -from ..._types import Base64FileInput -from ..._utils import PropertyInfo -from ..._models import set_pydantic_config - -__all__ = ["BetaBase64ImageSourceParam"] - - -class BetaBase64ImageSourceParam(TypedDict, total=False): - data: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]] - - media_type: Required[Literal["image/jpeg", "image/png", "image/gif", "image/webp"]] - - type: Required[Literal["base64"]] - - -set_pydantic_config(BetaBase64ImageSourceParam, {"arbitrary_types_allowed": True}) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_pdf_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_pdf_block_param.py deleted file mode 100644 index 1f6425ac..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_pdf_block_param.py +++ /dev/null @@ -1,7 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .beta_request_document_block_param import BetaRequestDocumentBlockParam - -BetaBase64PDFBlockParam = BetaRequestDocumentBlockParam diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_pdf_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_pdf_source.py deleted file mode 100644 index 28cf5c56..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_pdf_source.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaBase64PDFSource"] - - -class BetaBase64PDFSource(BaseModel): - data: str - - media_type: Literal["application/pdf"] - - type: Literal["base64"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_pdf_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_pdf_source_param.py deleted file mode 100644 index 1137c957..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_base64_pdf_source_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, Annotated, TypedDict - -from ..._types import Base64FileInput -from ..._utils import PropertyInfo -from ..._models import set_pydantic_config - -__all__ = ["BetaBase64PDFSourceParam"] - - -class BetaBase64PDFSourceParam(TypedDict, total=False): - data: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]] - - media_type: Required[Literal["application/pdf"]] - - type: Required[Literal["base64"]] - - -set_pydantic_config(BetaBase64PDFSourceParam, {"arbitrary_types_allowed": True}) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_output_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_output_block.py deleted file mode 100644 index 958b91ea..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_output_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaBashCodeExecutionOutputBlock"] - - -class BetaBashCodeExecutionOutputBlock(BaseModel): - file_id: str - - type: Literal["bash_code_execution_output"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_output_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_output_block_param.py deleted file mode 100644 index 2d4fc540..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_output_block_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaBashCodeExecutionOutputBlockParam"] - - -class BetaBashCodeExecutionOutputBlockParam(TypedDict, total=False): - file_id: Required[str] - - type: Required[Literal["bash_code_execution_output"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_result_block.py deleted file mode 100644 index f9217471..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_result_block.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_bash_code_execution_output_block import BetaBashCodeExecutionOutputBlock - -__all__ = ["BetaBashCodeExecutionResultBlock"] - - -class BetaBashCodeExecutionResultBlock(BaseModel): - content: List[BetaBashCodeExecutionOutputBlock] - - return_code: int - - stderr: str - - stdout: str - - type: Literal["bash_code_execution_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_result_block_param.py deleted file mode 100644 index af215824..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_result_block_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Literal, Required, TypedDict - -from .beta_bash_code_execution_output_block_param import BetaBashCodeExecutionOutputBlockParam - -__all__ = ["BetaBashCodeExecutionResultBlockParam"] - - -class BetaBashCodeExecutionResultBlockParam(TypedDict, total=False): - content: Required[Iterable[BetaBashCodeExecutionOutputBlockParam]] - - return_code: Required[int] - - stderr: Required[str] - - stdout: Required[str] - - type: Required[Literal["bash_code_execution_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_block.py deleted file mode 100644 index c117cadd..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_block.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, TypeAlias - -from ..._models import BaseModel -from .beta_bash_code_execution_result_block import BetaBashCodeExecutionResultBlock -from .beta_bash_code_execution_tool_result_error import BetaBashCodeExecutionToolResultError - -__all__ = ["BetaBashCodeExecutionToolResultBlock", "Content"] - -Content: TypeAlias = Union[BetaBashCodeExecutionToolResultError, BetaBashCodeExecutionResultBlock] - - -class BetaBashCodeExecutionToolResultBlock(BaseModel): - content: Content - - tool_use_id: str - - type: Literal["bash_code_execution_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_block_param.py deleted file mode 100644 index dc22c546..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_block_param.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_bash_code_execution_result_block_param import BetaBashCodeExecutionResultBlockParam -from .beta_bash_code_execution_tool_result_error_param import BetaBashCodeExecutionToolResultErrorParam - -__all__ = ["BetaBashCodeExecutionToolResultBlockParam", "Content"] - -Content: TypeAlias = Union[BetaBashCodeExecutionToolResultErrorParam, BetaBashCodeExecutionResultBlockParam] - - -class BetaBashCodeExecutionToolResultBlockParam(TypedDict, total=False): - content: Required[Content] - - tool_use_id: Required[str] - - type: Required[Literal["bash_code_execution_tool_result"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_error.py deleted file mode 100644 index 3a0c3a25..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_error.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaBashCodeExecutionToolResultError"] - - -class BetaBashCodeExecutionToolResultError(BaseModel): - error_code: Literal[ - "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "output_file_too_large" - ] - - type: Literal["bash_code_execution_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_error_param.py deleted file mode 100644 index 33569d34..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_bash_code_execution_tool_result_error_param.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaBashCodeExecutionToolResultErrorParam"] - - -class BetaBashCodeExecutionToolResultErrorParam(TypedDict, total=False): - error_code: Required[ - Literal[ - "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "output_file_too_large" - ] - ] - - type: Required[Literal["bash_code_execution_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cache_control_ephemeral_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cache_control_ephemeral_param.py deleted file mode 100644 index 221a9f76..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cache_control_ephemeral_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaCacheControlEphemeralParam"] - - -class BetaCacheControlEphemeralParam(TypedDict, total=False): - type: Required[Literal["ephemeral"]] - - ttl: Literal["5m", "1h"] - """The time-to-live for the cache control breakpoint. - - This may be one the following values: - - - `5m`: 5 minutes - - `1h`: 1 hour - - Defaults to `5m`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cache_creation.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cache_creation.py deleted file mode 100644 index 366fc8e1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cache_creation.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel - -__all__ = ["BetaCacheCreation"] - - -class BetaCacheCreation(BaseModel): - ephemeral_1h_input_tokens: int - """The number of input tokens used to create the 1 hour cache entry.""" - - ephemeral_5m_input_tokens: int - """The number of input tokens used to create the 5 minute cache entry.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_capability_support.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_capability_support.py deleted file mode 100644 index ed45c353..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_capability_support.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel - -__all__ = ["BetaCapabilitySupport"] - - -class BetaCapabilitySupport(BaseModel): - """Indicates whether a capability is supported.""" - - supported: bool - """Whether this capability is supported by the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_char_location.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_char_location.py deleted file mode 100644 index 8efb8527..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_char_location.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaCitationCharLocation"] - - -class BetaCitationCharLocation(BaseModel): - cited_text: str - - document_index: int - - document_title: Optional[str] = None - - end_char_index: int - - file_id: Optional[str] = None - - start_char_index: int - - type: Literal["char_location"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_char_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_char_location_param.py deleted file mode 100644 index 8c09f5a7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_char_location_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaCitationCharLocationParam"] - - -class BetaCitationCharLocationParam(TypedDict, total=False): - cited_text: Required[str] - - document_index: Required[int] - - document_title: Required[Optional[str]] - - end_char_index: Required[int] - - start_char_index: Required[int] - - type: Required[Literal["char_location"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_config.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_config.py deleted file mode 100644 index 8e949bd0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_config.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel - -__all__ = ["BetaCitationConfig"] - - -class BetaCitationConfig(BaseModel): - enabled: bool diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_content_block_location.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_content_block_location.py deleted file mode 100644 index 6608ea40..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_content_block_location.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaCitationContentBlockLocation"] - - -class BetaCitationContentBlockLocation(BaseModel): - cited_text: str - - document_index: int - - document_title: Optional[str] = None - - end_block_index: int - - file_id: Optional[str] = None - - start_block_index: int - - type: Literal["content_block_location"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_content_block_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_content_block_location_param.py deleted file mode 100644 index 9e378a78..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_content_block_location_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaCitationContentBlockLocationParam"] - - -class BetaCitationContentBlockLocationParam(TypedDict, total=False): - cited_text: Required[str] - - document_index: Required[int] - - document_title: Required[Optional[str]] - - end_block_index: Required[int] - - start_block_index: Required[int] - - type: Required[Literal["content_block_location"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_page_location.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_page_location.py deleted file mode 100644 index 71a7b9a4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_page_location.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaCitationPageLocation"] - - -class BetaCitationPageLocation(BaseModel): - cited_text: str - - document_index: int - - document_title: Optional[str] = None - - end_page_number: int - - file_id: Optional[str] = None - - start_page_number: int - - type: Literal["page_location"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_page_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_page_location_param.py deleted file mode 100644 index 60e5b1c2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_page_location_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaCitationPageLocationParam"] - - -class BetaCitationPageLocationParam(TypedDict, total=False): - cited_text: Required[str] - - document_index: Required[int] - - document_title: Required[Optional[str]] - - end_page_number: Required[int] - - start_page_number: Required[int] - - type: Required[Literal["page_location"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_search_result_location.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_search_result_location.py deleted file mode 100644 index a6e3e003..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_search_result_location.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaCitationSearchResultLocation"] - - -class BetaCitationSearchResultLocation(BaseModel): - cited_text: str - - end_block_index: int - - search_result_index: int - - source: str - - start_block_index: int - - title: Optional[str] = None - - type: Literal["search_result_location"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_search_result_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_search_result_location_param.py deleted file mode 100644 index d0dfbea5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_search_result_location_param.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaCitationSearchResultLocationParam"] - - -class BetaCitationSearchResultLocationParam(TypedDict, total=False): - cited_text: Required[str] - - end_block_index: Required[int] - - search_result_index: Required[int] - - source: Required[str] - - start_block_index: Required[int] - - title: Required[Optional[str]] - - type: Required[Literal["search_result_location"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_web_search_result_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_web_search_result_location_param.py deleted file mode 100644 index 90625af1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citation_web_search_result_location_param.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaCitationWebSearchResultLocationParam"] - - -class BetaCitationWebSearchResultLocationParam(TypedDict, total=False): - cited_text: Required[str] - - encrypted_index: Required[str] - - title: Required[Optional[str]] - - type: Required[Literal["web_search_result_location"]] - - url: Required[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citations_config_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citations_config_param.py deleted file mode 100644 index 409cfde7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citations_config_param.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["BetaCitationsConfigParam"] - - -class BetaCitationsConfigParam(TypedDict, total=False): - enabled: bool diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citations_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citations_delta.py deleted file mode 100644 index e8368180..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citations_delta.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_citation_char_location import BetaCitationCharLocation -from .beta_citation_page_location import BetaCitationPageLocation -from .beta_citation_content_block_location import BetaCitationContentBlockLocation -from .beta_citation_search_result_location import BetaCitationSearchResultLocation -from .beta_citations_web_search_result_location import BetaCitationsWebSearchResultLocation - -__all__ = ["BetaCitationsDelta", "Citation"] - -Citation: TypeAlias = Annotated[ - Union[ - BetaCitationCharLocation, - BetaCitationPageLocation, - BetaCitationContentBlockLocation, - BetaCitationsWebSearchResultLocation, - BetaCitationSearchResultLocation, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaCitationsDelta(BaseModel): - citation: Citation - - type: Literal["citations_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citations_web_search_result_location.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citations_web_search_result_location.py deleted file mode 100644 index 111ffe7c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_citations_web_search_result_location.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaCitationsWebSearchResultLocation"] - - -class BetaCitationsWebSearchResultLocation(BaseModel): - cited_text: str - - encrypted_index: str - - title: Optional[str] = None - - type: Literal["web_search_result_location"] - - url: str diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_thinking_20251015_edit_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_thinking_20251015_edit_param.py deleted file mode 100644 index a711c723..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_thinking_20251015_edit_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_thinking_turns_param import BetaThinkingTurnsParam -from .beta_all_thinking_turns_param import BetaAllThinkingTurnsParam - -__all__ = ["BetaClearThinking20251015EditParam", "Keep"] - -Keep: TypeAlias = Union[BetaThinkingTurnsParam, BetaAllThinkingTurnsParam, Literal["all"]] - - -class BetaClearThinking20251015EditParam(TypedDict, total=False): - type: Required[Literal["clear_thinking_20251015"]] - - keep: Keep - """Number of most recent assistant turns to keep thinking blocks for. - - Older turns will have their thinking blocks removed. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_thinking_20251015_edit_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_thinking_20251015_edit_response.py deleted file mode 100644 index 08d174b5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_thinking_20251015_edit_response.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaClearThinking20251015EditResponse"] - - -class BetaClearThinking20251015EditResponse(BaseModel): - cleared_input_tokens: int - """Number of input tokens cleared by this edit.""" - - cleared_thinking_turns: int - """Number of thinking turns that were cleared.""" - - type: Literal["clear_thinking_20251015"] - """The type of context management edit applied.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_tool_uses_20250919_edit_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_tool_uses_20250919_edit_param.py deleted file mode 100644 index 64b901f6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_tool_uses_20250919_edit_param.py +++ /dev/null @@ -1,38 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from ..._types import SequenceNotStr -from .beta_tool_uses_keep_param import BetaToolUsesKeepParam -from .beta_tool_uses_trigger_param import BetaToolUsesTriggerParam -from .beta_input_tokens_trigger_param import BetaInputTokensTriggerParam -from .beta_input_tokens_clear_at_least_param import BetaInputTokensClearAtLeastParam - -__all__ = ["BetaClearToolUses20250919EditParam", "Trigger"] - -Trigger: TypeAlias = Union[BetaInputTokensTriggerParam, BetaToolUsesTriggerParam] - - -class BetaClearToolUses20250919EditParam(TypedDict, total=False): - type: Required[Literal["clear_tool_uses_20250919"]] - - clear_at_least: Optional[BetaInputTokensClearAtLeastParam] - """Minimum number of tokens that must be cleared when triggered. - - Context will only be modified if at least this many tokens can be removed. - """ - - clear_tool_inputs: Union[bool, SequenceNotStr[str], None] - """Whether to clear all tool inputs (bool) or specific tool inputs to clear (list)""" - - exclude_tools: Optional[SequenceNotStr[str]] - """Tool names whose uses are preserved from clearing""" - - keep: BetaToolUsesKeepParam - """Number of tool uses to retain in the conversation""" - - trigger: Trigger - """Condition that triggers the context management strategy""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_tool_uses_20250919_edit_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_tool_uses_20250919_edit_response.py deleted file mode 100644 index 42a7dfae..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_clear_tool_uses_20250919_edit_response.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaClearToolUses20250919EditResponse"] - - -class BetaClearToolUses20250919EditResponse(BaseModel): - cleared_input_tokens: int - """Number of input tokens cleared by this edit.""" - - cleared_tool_uses: int - """Number of tool uses that were cleared.""" - - type: Literal["clear_tool_uses_20250919"] - """The type of context management edit applied.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cloud_config.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cloud_config.py deleted file mode 100644 index beaa6a03..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cloud_config.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_packages import BetaPackages -from .beta_limited_network import BetaLimitedNetwork -from .beta_unrestricted_network import BetaUnrestrictedNetwork - -__all__ = ["BetaCloudConfig", "Networking"] - -Networking: TypeAlias = Annotated[ - Union[BetaUnrestrictedNetwork, BetaLimitedNetwork], PropertyInfo(discriminator="type") -] - - -class BetaCloudConfig(BaseModel): - """`cloud` environment configuration.""" - - networking: Networking - """Network configuration policy.""" - - packages: BetaPackages - """Package manager configuration.""" - - type: Literal["cloud"] - """Environment type""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cloud_config_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cloud_config_params.py deleted file mode 100644 index 40cd4078..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_cloud_config_params.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_packages_params import BetaPackagesParams -from .beta_limited_network_params import BetaLimitedNetworkParams -from .beta_unrestricted_network_param import BetaUnrestrictedNetworkParam - -__all__ = ["BetaCloudConfigParams", "Networking"] - -Networking: TypeAlias = Union[BetaUnrestrictedNetworkParam, BetaLimitedNetworkParams] - - -class BetaCloudConfigParams(TypedDict, total=False): - """Request params for `cloud` environment configuration. - - Fields default to null; on update, omitted fields preserve the - existing value. - """ - - type: Required[Literal["cloud"]] - """Environment type""" - - networking: Optional[Networking] - """Network configuration policy. Omit on update to preserve the existing value.""" - - packages: Optional[BetaPackagesParams] - """Specify packages (and optionally their versions) available in this environment. - - When versioning, use the version semantics relevant for the package manager, - e.g. for `pip` use `package==1.0.0`. You are responsible for validating the - package and version exist. Unversioned installs the latest. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_output_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_output_block.py deleted file mode 100644 index 6f02c3ee..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_output_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaCodeExecutionOutputBlock"] - - -class BetaCodeExecutionOutputBlock(BaseModel): - file_id: str - - type: Literal["code_execution_output"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_output_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_output_block_param.py deleted file mode 100644 index 35a6293c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_output_block_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaCodeExecutionOutputBlockParam"] - - -class BetaCodeExecutionOutputBlockParam(TypedDict, total=False): - file_id: Required[str] - - type: Required[Literal["code_execution_output"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_result_block.py deleted file mode 100644 index be7a60b2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_result_block.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_code_execution_output_block import BetaCodeExecutionOutputBlock - -__all__ = ["BetaCodeExecutionResultBlock"] - - -class BetaCodeExecutionResultBlock(BaseModel): - content: List[BetaCodeExecutionOutputBlock] - - return_code: int - - stderr: str - - stdout: str - - type: Literal["code_execution_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_result_block_param.py deleted file mode 100644 index 3557d63f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_result_block_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Literal, Required, TypedDict - -from .beta_code_execution_output_block_param import BetaCodeExecutionOutputBlockParam - -__all__ = ["BetaCodeExecutionResultBlockParam"] - - -class BetaCodeExecutionResultBlockParam(TypedDict, total=False): - content: Required[Iterable[BetaCodeExecutionOutputBlockParam]] - - return_code: Required[int] - - stderr: Required[str] - - stdout: Required[str] - - type: Required[Literal["code_execution_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_20250522_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_20250522_param.py deleted file mode 100644 index 1683a257..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_20250522_param.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaCodeExecutionTool20250522Param"] - - -class BetaCodeExecutionTool20250522Param(TypedDict, total=False): - name: Required[Literal["code_execution"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["code_execution_20250522"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_20250825_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_20250825_param.py deleted file mode 100644 index 053c4423..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_20250825_param.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaCodeExecutionTool20250825Param"] - - -class BetaCodeExecutionTool20250825Param(TypedDict, total=False): - name: Required[Literal["code_execution"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["code_execution_20250825"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_20260120_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_20260120_param.py deleted file mode 100644 index 8d96b1fc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_20260120_param.py +++ /dev/null @@ -1,38 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaCodeExecutionTool20260120Param"] - - -class BetaCodeExecutionTool20260120Param(TypedDict, total=False): - """ - Code execution tool with REPL state persistence (daemon mode + gVisor checkpoint). - """ - - name: Required[Literal["code_execution"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["code_execution_20260120"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block.py deleted file mode 100644 index 141bbc09..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_code_execution_tool_result_block_content import BetaCodeExecutionToolResultBlockContent - -__all__ = ["BetaCodeExecutionToolResultBlock"] - - -class BetaCodeExecutionToolResultBlock(BaseModel): - content: BetaCodeExecutionToolResultBlockContent - """Code execution result with encrypted stdout for PFC + web_search results.""" - - tool_use_id: str - - type: Literal["code_execution_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block_content.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block_content.py deleted file mode 100644 index 2977a051..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block_content.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import TypeAlias - -from .beta_code_execution_result_block import BetaCodeExecutionResultBlock -from .beta_code_execution_tool_result_error import BetaCodeExecutionToolResultError -from .beta_encrypted_code_execution_result_block import BetaEncryptedCodeExecutionResultBlock - -__all__ = ["BetaCodeExecutionToolResultBlockContent"] - -BetaCodeExecutionToolResultBlockContent: TypeAlias = Union[ - BetaCodeExecutionToolResultError, BetaCodeExecutionResultBlock, BetaEncryptedCodeExecutionResultBlock -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block_param.py deleted file mode 100644 index fdb6a09c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_code_execution_tool_result_block_param_content_param import BetaCodeExecutionToolResultBlockParamContentParam - -__all__ = ["BetaCodeExecutionToolResultBlockParam"] - - -class BetaCodeExecutionToolResultBlockParam(TypedDict, total=False): - content: Required[BetaCodeExecutionToolResultBlockParamContentParam] - """Code execution result with encrypted stdout for PFC + web_search results.""" - - tool_use_id: Required[str] - - type: Required[Literal["code_execution_tool_result"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block_param_content_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block_param_content_param.py deleted file mode 100644 index 17296904..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_block_param_content_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .beta_code_execution_result_block_param import BetaCodeExecutionResultBlockParam -from .beta_code_execution_tool_result_error_param import BetaCodeExecutionToolResultErrorParam -from .beta_encrypted_code_execution_result_block_param import BetaEncryptedCodeExecutionResultBlockParam - -__all__ = ["BetaCodeExecutionToolResultBlockParamContentParam"] - -BetaCodeExecutionToolResultBlockParamContentParam: TypeAlias = Union[ - BetaCodeExecutionToolResultErrorParam, BetaCodeExecutionResultBlockParam, BetaEncryptedCodeExecutionResultBlockParam -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_error.py deleted file mode 100644 index d499584c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_error.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_code_execution_tool_result_error_code import BetaCodeExecutionToolResultErrorCode - -__all__ = ["BetaCodeExecutionToolResultError"] - - -class BetaCodeExecutionToolResultError(BaseModel): - error_code: BetaCodeExecutionToolResultErrorCode - - type: Literal["code_execution_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_error_code.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_error_code.py deleted file mode 100644 index f56326b4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_error_code.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["BetaCodeExecutionToolResultErrorCode"] - -BetaCodeExecutionToolResultErrorCode: TypeAlias = Literal[ - "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded" -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_error_param.py deleted file mode 100644 index cb57a434..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_code_execution_tool_result_error_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .beta_code_execution_tool_result_error_code import BetaCodeExecutionToolResultErrorCode - -__all__ = ["BetaCodeExecutionToolResultErrorParam"] - - -class BetaCodeExecutionToolResultErrorParam(TypedDict, total=False): - error_code: Required[BetaCodeExecutionToolResultErrorCode] - - type: Required[Literal["code_execution_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compact_20260112_edit_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compact_20260112_edit_param.py deleted file mode 100644 index 2faacdca..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compact_20260112_edit_param.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_input_tokens_trigger_param import BetaInputTokensTriggerParam - -__all__ = ["BetaCompact20260112EditParam"] - - -class BetaCompact20260112EditParam(TypedDict, total=False): - """ - Automatically compact older context when reaching the configured trigger threshold. - """ - - type: Required[Literal["compact_20260112"]] - - instructions: Optional[str] - """Additional instructions for summarization.""" - - pause_after_compaction: bool - """Whether to pause after compaction and return the compaction block to the user.""" - - trigger: Optional[BetaInputTokensTriggerParam] - """When to trigger compaction. Defaults to 150000 input tokens.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_block.py deleted file mode 100644 index 53da79fa..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_block.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaCompactionBlock"] - - -class BetaCompactionBlock(BaseModel): - """A compaction block returned when autocompact is triggered. - - When content is None, it indicates the compaction failed to produce a valid - summary (e.g., malformed output from the model). Clients may round-trip - compaction blocks with null content; the server treats them as no-ops. - """ - - content: Optional[str] = None - """Summary of compacted content, or null if compaction failed""" - - encrypted_content: Optional[str] = None - """Opaque metadata from prior compaction, to be round-tripped verbatim""" - - type: Literal["compaction"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_block_param.py deleted file mode 100644 index 39d4ed82..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_block_param.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaCompactionBlockParam"] - - -class BetaCompactionBlockParam(TypedDict, total=False): - """A compaction block containing summary of previous context. - - Users should round-trip these blocks from responses to subsequent requests - to maintain context across compaction boundaries. - - When content is None, the block represents a failed compaction. The server - treats these as no-ops. Empty string content is not allowed. - """ - - content: Required[Optional[str]] - """Summary of previously compacted content, or null if compaction failed""" - - type: Required[Literal["compaction"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - encrypted_content: Optional[str] - """Opaque metadata from prior compaction, to be round-tripped verbatim""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_content_block_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_content_block_delta.py deleted file mode 100644 index 84887de9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_content_block_delta.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaCompactionContentBlockDelta"] - - -class BetaCompactionContentBlockDelta(BaseModel): - content: Optional[str] = None - - encrypted_content: Optional[str] = None - """Opaque metadata from prior compaction, to be round-tripped verbatim""" - - type: Literal["compaction_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_iteration_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_iteration_usage.py deleted file mode 100644 index 1bbca20f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_compaction_iteration_usage.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_cache_creation import BetaCacheCreation - -__all__ = ["BetaCompactionIterationUsage"] - - -class BetaCompactionIterationUsage(BaseModel): - """Token usage for a compaction iteration.""" - - cache_creation: Optional[BetaCacheCreation] = None - """Breakdown of cached tokens by TTL""" - - cache_creation_input_tokens: int - """The number of input tokens used to create the cache entry.""" - - cache_read_input_tokens: int - """The number of input tokens read from the cache.""" - - input_tokens: int - """The number of input tokens which were used.""" - - output_tokens: int - """The number of output tokens which were used.""" - - type: Literal["compaction"] - """Usage for a compaction iteration""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container.py deleted file mode 100644 index 47116abf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from ..._models import BaseModel -from .beta_skill import BetaSkill - -__all__ = ["BetaContainer"] - - -class BetaContainer(BaseModel): - """ - Information about the container used in the request (for the code execution tool) - """ - - id: str - """Identifier for the container used in this request""" - - expires_at: datetime - """The time at which the container will expire.""" - - skills: Optional[List[BetaSkill]] = None - """Skills loaded in the container""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container_params.py deleted file mode 100644 index 58c40dd1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable, Optional -from typing_extensions import TypedDict - -from .beta_skill_params import BetaSkillParams - -__all__ = ["BetaContainerParams"] - - -class BetaContainerParams(TypedDict, total=False): - """Container parameters with skills to be loaded.""" - - id: Optional[str] - """Container id""" - - skills: Optional[Iterable[BetaSkillParams]] - """List of skills to load in the container""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container_upload_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container_upload_block.py deleted file mode 100644 index fe2aa222..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container_upload_block.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaContainerUploadBlock"] - - -class BetaContainerUploadBlock(BaseModel): - """Response model for a file uploaded to the container.""" - - file_id: str - - type: Literal["container_upload"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container_upload_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container_upload_block_param.py deleted file mode 100644 index b857e11a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_container_upload_block_param.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaContainerUploadBlockParam"] - - -class BetaContainerUploadBlockParam(TypedDict, total=False): - """ - A content block that represents a file to be uploaded to the container - Files uploaded via this block will be available in the container's input directory. - """ - - file_id: Required[str] - - type: Required[Literal["container_upload"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block.py deleted file mode 100644 index 5b6b627e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block.py +++ /dev/null @@ -1,46 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from .beta_text_block import BetaTextBlock -from .beta_thinking_block import BetaThinkingBlock -from .beta_tool_use_block import BetaToolUseBlock -from .beta_compaction_block import BetaCompactionBlock -from .beta_mcp_tool_use_block import BetaMCPToolUseBlock -from .beta_mcp_tool_result_block import BetaMCPToolResultBlock -from .beta_server_tool_use_block import BetaServerToolUseBlock -from .beta_container_upload_block import BetaContainerUploadBlock -from .beta_redacted_thinking_block import BetaRedactedThinkingBlock -from .beta_advisor_tool_result_block import BetaAdvisorToolResultBlock -from .beta_web_fetch_tool_result_block import BetaWebFetchToolResultBlock -from .beta_web_search_tool_result_block import BetaWebSearchToolResultBlock -from .beta_tool_search_tool_result_block import BetaToolSearchToolResultBlock -from .beta_code_execution_tool_result_block import BetaCodeExecutionToolResultBlock -from .beta_bash_code_execution_tool_result_block import BetaBashCodeExecutionToolResultBlock -from .beta_text_editor_code_execution_tool_result_block import BetaTextEditorCodeExecutionToolResultBlock - -__all__ = ["BetaContentBlock"] - -BetaContentBlock: TypeAlias = Annotated[ - Union[ - BetaTextBlock, - BetaThinkingBlock, - BetaRedactedThinkingBlock, - BetaToolUseBlock, - BetaServerToolUseBlock, - BetaWebSearchToolResultBlock, - BetaWebFetchToolResultBlock, - BetaAdvisorToolResultBlock, - BetaCodeExecutionToolResultBlock, - BetaBashCodeExecutionToolResultBlock, - BetaTextEditorCodeExecutionToolResultBlock, - BetaToolSearchToolResultBlock, - BetaMCPToolUseBlock, - BetaMCPToolResultBlock, - BetaContainerUploadBlock, - BetaCompactionBlock, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block_param.py deleted file mode 100644 index 9b3f982c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block_param.py +++ /dev/null @@ -1,54 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .beta_content_block import BetaContentBlock -from .beta_text_block_param import BetaTextBlockParam -from .beta_image_block_param import BetaImageBlockParam -from .beta_thinking_block_param import BetaThinkingBlockParam -from .beta_tool_use_block_param import BetaToolUseBlockParam -from .beta_compaction_block_param import BetaCompactionBlockParam -from .beta_tool_result_block_param import BetaToolResultBlockParam -from .beta_mcp_tool_use_block_param import BetaMCPToolUseBlockParam -from .beta_search_result_block_param import BetaSearchResultBlockParam -from .beta_server_tool_use_block_param import BetaServerToolUseBlockParam -from .beta_container_upload_block_param import BetaContainerUploadBlockParam -from .beta_request_document_block_param import BetaRequestDocumentBlockParam -from .beta_redacted_thinking_block_param import BetaRedactedThinkingBlockParam -from .beta_advisor_tool_result_block_param import BetaAdvisorToolResultBlockParam -from .beta_web_fetch_tool_result_block_param import BetaWebFetchToolResultBlockParam -from .beta_web_search_tool_result_block_param import BetaWebSearchToolResultBlockParam -from .beta_request_mcp_tool_result_block_param import BetaRequestMCPToolResultBlockParam -from .beta_tool_search_tool_result_block_param import BetaToolSearchToolResultBlockParam -from .beta_code_execution_tool_result_block_param import BetaCodeExecutionToolResultBlockParam -from .beta_bash_code_execution_tool_result_block_param import BetaBashCodeExecutionToolResultBlockParam -from .beta_text_editor_code_execution_tool_result_block_param import BetaTextEditorCodeExecutionToolResultBlockParam - -__all__ = ["BetaContentBlockParam"] - -BetaContentBlockParam: TypeAlias = Union[ - BetaTextBlockParam, - BetaImageBlockParam, - BetaRequestDocumentBlockParam, - BetaSearchResultBlockParam, - BetaThinkingBlockParam, - BetaRedactedThinkingBlockParam, - BetaToolUseBlockParam, - BetaToolResultBlockParam, - BetaServerToolUseBlockParam, - BetaWebSearchToolResultBlockParam, - BetaWebFetchToolResultBlockParam, - BetaAdvisorToolResultBlockParam, - BetaCodeExecutionToolResultBlockParam, - BetaBashCodeExecutionToolResultBlockParam, - BetaTextEditorCodeExecutionToolResultBlockParam, - BetaToolSearchToolResultBlockParam, - BetaMCPToolUseBlockParam, - BetaRequestMCPToolResultBlockParam, - BetaContainerUploadBlockParam, - BetaCompactionBlockParam, - BetaContentBlock, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block_source_content_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block_source_content_param.py deleted file mode 100644 index bc13b146..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block_source_content_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .beta_text_block_param import BetaTextBlockParam -from .beta_image_block_param import BetaImageBlockParam - -__all__ = ["BetaContentBlockSourceContentParam"] - -BetaContentBlockSourceContentParam: TypeAlias = Union[BetaTextBlockParam, BetaImageBlockParam] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block_source_param.py deleted file mode 100644 index 512cf0db..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_content_block_source_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable -from typing_extensions import Literal, Required, TypedDict - -from .beta_content_block_source_content_param import BetaContentBlockSourceContentParam - -__all__ = ["BetaContentBlockSourceParam"] - - -class BetaContentBlockSourceParam(TypedDict, total=False): - content: Required[Union[str, Iterable[BetaContentBlockSourceContentParam]]] - - type: Required[Literal["content"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_context_management_capability.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_context_management_capability.py deleted file mode 100644 index 07ae8fc4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_context_management_capability.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel -from .beta_capability_support import BetaCapabilitySupport - -__all__ = ["BetaContextManagementCapability"] - - -class BetaContextManagementCapability(BaseModel): - """Context management capability details.""" - - clear_thinking_20251015: Optional[BetaCapabilitySupport] = None - """Indicates whether a capability is supported.""" - - clear_tool_uses_20250919: Optional[BetaCapabilitySupport] = None - """Indicates whether a capability is supported.""" - - compact_20260112: Optional[BetaCapabilitySupport] = None - """Indicates whether a capability is supported.""" - - supported: bool - """Whether this capability is supported by the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_context_management_config_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_context_management_config_param.py deleted file mode 100644 index dec68955..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_context_management_config_param.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable -from typing_extensions import TypeAlias, TypedDict - -from .beta_compact_20260112_edit_param import BetaCompact20260112EditParam -from .beta_clear_thinking_20251015_edit_param import BetaClearThinking20251015EditParam -from .beta_clear_tool_uses_20250919_edit_param import BetaClearToolUses20250919EditParam - -__all__ = ["BetaContextManagementConfigParam", "Edit"] - -Edit: TypeAlias = Union[ - BetaClearToolUses20250919EditParam, BetaClearThinking20251015EditParam, BetaCompact20260112EditParam -] - - -class BetaContextManagementConfigParam(TypedDict, total=False): - edits: Iterable[Edit] - """List of context management edits to apply""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_context_management_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_context_management_response.py deleted file mode 100644 index 278c437d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_context_management_response.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_clear_thinking_20251015_edit_response import BetaClearThinking20251015EditResponse -from .beta_clear_tool_uses_20250919_edit_response import BetaClearToolUses20250919EditResponse - -__all__ = ["BetaContextManagementResponse", "AppliedEdit"] - -AppliedEdit: TypeAlias = Annotated[ - Union[BetaClearToolUses20250919EditResponse, BetaClearThinking20251015EditResponse], - PropertyInfo(discriminator="type"), -] - - -class BetaContextManagementResponse(BaseModel): - applied_edits: List[AppliedEdit] - """List of context management edits that were applied.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_count_tokens_context_management_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_count_tokens_context_management_response.py deleted file mode 100644 index 15dd44fc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_count_tokens_context_management_response.py +++ /dev/null @@ -1,10 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel - -__all__ = ["BetaCountTokensContextManagementResponse"] - - -class BetaCountTokensContextManagementResponse(BaseModel): - original_input_tokens: int - """The original token count before context management was applied""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_direct_caller.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_direct_caller.py deleted file mode 100644 index 6e7474d1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_direct_caller.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaDirectCaller"] - - -class BetaDirectCaller(BaseModel): - """Tool invocation directly from the model.""" - - type: Literal["direct"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_direct_caller_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_direct_caller_param.py deleted file mode 100644 index 701ff6cf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_direct_caller_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaDirectCallerParam"] - - -class BetaDirectCallerParam(TypedDict, total=False): - """Tool invocation directly from the model.""" - - type: Required[Literal["direct"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_document_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_document_block.py deleted file mode 100644 index 5ed2800e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_document_block.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_citation_config import BetaCitationConfig -from .beta_base64_pdf_source import BetaBase64PDFSource -from .beta_plain_text_source import BetaPlainTextSource - -__all__ = ["BetaDocumentBlock", "Source"] - -Source: TypeAlias = Annotated[Union[BetaBase64PDFSource, BetaPlainTextSource], PropertyInfo(discriminator="type")] - - -class BetaDocumentBlock(BaseModel): - citations: Optional[BetaCitationConfig] = None - """Citation configuration for the document""" - - source: Source - - title: Optional[str] = None - """The title of the document""" - - type: Literal["document"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_effort_capability.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_effort_capability.py deleted file mode 100644 index 6f33062d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_effort_capability.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel -from .beta_capability_support import BetaCapabilitySupport - -__all__ = ["BetaEffortCapability"] - - -class BetaEffortCapability(BaseModel): - """Effort (reasoning_effort) capability details.""" - - high: BetaCapabilitySupport - """Whether the model supports high effort level.""" - - low: BetaCapabilitySupport - """Whether the model supports low effort level.""" - - max: BetaCapabilitySupport - """Whether the model supports max effort level.""" - - medium: BetaCapabilitySupport - """Whether the model supports medium effort level.""" - - supported: bool - """Whether this capability is supported by the model.""" - - xhigh: Optional[BetaCapabilitySupport] = None - """Indicates whether a capability is supported.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_encrypted_code_execution_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_encrypted_code_execution_result_block.py deleted file mode 100644 index b69830e6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_encrypted_code_execution_result_block.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_code_execution_output_block import BetaCodeExecutionOutputBlock - -__all__ = ["BetaEncryptedCodeExecutionResultBlock"] - - -class BetaEncryptedCodeExecutionResultBlock(BaseModel): - """Code execution result with encrypted stdout for PFC + web_search results.""" - - content: List[BetaCodeExecutionOutputBlock] - - encrypted_stdout: str - - return_code: int - - stderr: str - - type: Literal["encrypted_code_execution_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_encrypted_code_execution_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_encrypted_code_execution_result_block_param.py deleted file mode 100644 index e9dc37d6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_encrypted_code_execution_result_block_param.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Literal, Required, TypedDict - -from .beta_code_execution_output_block_param import BetaCodeExecutionOutputBlockParam - -__all__ = ["BetaEncryptedCodeExecutionResultBlockParam"] - - -class BetaEncryptedCodeExecutionResultBlockParam(TypedDict, total=False): - """Code execution result with encrypted stdout for PFC + web_search results.""" - - content: Required[Iterable[BetaCodeExecutionOutputBlockParam]] - - encrypted_stdout: Required[str] - - return_code: Required[int] - - stderr: Required[str] - - type: Required[Literal["encrypted_code_execution_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_environment.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_environment.py deleted file mode 100644 index e109b156..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_environment.py +++ /dev/null @@ -1,40 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_cloud_config import BetaCloudConfig - -__all__ = ["BetaEnvironment"] - - -class BetaEnvironment(BaseModel): - """Unified Environment resource for both cloud and BYOC environments.""" - - id: str - """Environment identifier (e.g., 'env\\__...')""" - - archived_at: Optional[str] = None - """RFC 3339 timestamp when environment was archived, or null if not archived""" - - config: BetaCloudConfig - """`cloud` environment configuration.""" - - created_at: str - """RFC 3339 timestamp when environment was created""" - - description: str - """User-provided description for the environment""" - - metadata: Dict[str, str] - """User-provided metadata key-value pairs""" - - name: str - """Human-readable name for the environment""" - - type: Literal["environment"] - """The type of object (always 'environment')""" - - updated_at: str - """RFC 3339 timestamp when environment was last updated""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_environment_delete_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_environment_delete_response.py deleted file mode 100644 index ebc9e221..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_environment_delete_response.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaEnvironmentDeleteResponse"] - - -class BetaEnvironmentDeleteResponse(BaseModel): - """Response after deleting an environment.""" - - id: str - """Environment identifier""" - - type: Literal["environment_deleted"] - """The type of response""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_file_document_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_file_document_source_param.py deleted file mode 100644 index 9654093c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_file_document_source_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaFileDocumentSourceParam"] - - -class BetaFileDocumentSourceParam(TypedDict, total=False): - file_id: Required[str] - - type: Required[Literal["file"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_file_image_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_file_image_source_param.py deleted file mode 100644 index c98ce7a5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_file_image_source_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaFileImageSourceParam"] - - -class BetaFileImageSourceParam(TypedDict, total=False): - file_id: Required[str] - - type: Required[Literal["file"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_file_scope.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_file_scope.py deleted file mode 100644 index f9292334..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_file_scope.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaFileScope"] - - -class BetaFileScope(BaseModel): - id: str - """The ID of the scoping resource (e.g., the session ID).""" - - type: Literal["session"] - """The type of scope (e.g., `"session"`).""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_image_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_image_block_param.py deleted file mode 100644 index f197c829..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_image_block_param.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_url_image_source_param import BetaURLImageSourceParam -from .beta_file_image_source_param import BetaFileImageSourceParam -from .beta_base64_image_source_param import BetaBase64ImageSourceParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaImageBlockParam", "Source"] - -Source: TypeAlias = Union[BetaBase64ImageSourceParam, BetaURLImageSourceParam, BetaFileImageSourceParam] - - -class BetaImageBlockParam(TypedDict, total=False): - source: Required[Source] - - type: Required[Literal["image"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_input_json_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_input_json_delta.py deleted file mode 100644 index a5f9cbea..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_input_json_delta.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaInputJSONDelta"] - - -class BetaInputJSONDelta(BaseModel): - partial_json: str - - type: Literal["input_json_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_input_tokens_clear_at_least_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_input_tokens_clear_at_least_param.py deleted file mode 100644 index e3a137bd..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_input_tokens_clear_at_least_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaInputTokensClearAtLeastParam"] - - -class BetaInputTokensClearAtLeastParam(TypedDict, total=False): - type: Required[Literal["input_tokens"]] - - value: Required[int] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_input_tokens_trigger_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_input_tokens_trigger_param.py deleted file mode 100644 index 1d5f15c6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_input_tokens_trigger_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaInputTokensTriggerParam"] - - -class BetaInputTokensTriggerParam(TypedDict, total=False): - type: Required[Literal["input_tokens"]] - - value: Required[int] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_iterations_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_iterations_usage.py deleted file mode 100644 index 424c1f49..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_iterations_usage.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from .beta_message_iteration_usage import BetaMessageIterationUsage -from .beta_compaction_iteration_usage import BetaCompactionIterationUsage -from .beta_advisor_message_iteration_usage import BetaAdvisorMessageIterationUsage - -__all__ = ["BetaIterationsUsage", "BetaIterationsUsageItem"] - -BetaIterationsUsageItem: TypeAlias = Annotated[ - Union[BetaMessageIterationUsage, BetaCompactionIterationUsage, BetaAdvisorMessageIterationUsage], - PropertyInfo(discriminator="type"), -] - -BetaIterationsUsage: TypeAlias = List[BetaIterationsUsageItem] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_json_output_format_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_json_output_format_param.py deleted file mode 100644 index 48f9fb0b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_json_output_format_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaJSONOutputFormatParam"] - - -class BetaJSONOutputFormatParam(TypedDict, total=False): - schema: Required[Dict[str, object]] - """The JSON schema of the format""" - - type: Required[Literal["json_schema"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_limited_network.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_limited_network.py deleted file mode 100644 index f933445e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_limited_network.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaLimitedNetwork"] - - -class BetaLimitedNetwork(BaseModel): - """Limited network access.""" - - allow_mcp_servers: bool - """ - Permits outbound access to MCP server endpoints configured on the agent, beyond - those listed in the `allowed_hosts` array. - """ - - allow_package_managers: bool - """ - Permits outbound access to public package registries (PyPI, npm, etc.) beyond - those listed in the `allowed_hosts` array. - """ - - allowed_hosts: List[str] - """Specifies domains the container can reach.""" - - type: Literal["limited"] - """Network policy type""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_limited_network_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_limited_network_params.py deleted file mode 100644 index 8ef074da..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_limited_network_params.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["BetaLimitedNetworkParams"] - - -class BetaLimitedNetworkParams(TypedDict, total=False): - """Limited network request params. - - Fields default to null; on update, omitted fields preserve the - existing value. - """ - - type: Required[Literal["limited"]] - """Network policy type""" - - allow_mcp_servers: Optional[bool] - """ - Permits outbound access to MCP server endpoints configured on the agent, beyond - those listed in the `allowed_hosts` array. Defaults to `false`. - """ - - allow_package_managers: Optional[bool] - """ - Permits outbound access to public package registries (PyPI, npm, etc.) beyond - those listed in the `allowed_hosts` array. Defaults to `false`. - """ - - allowed_hosts: Optional[SequenceNotStr[str]] - """Specifies domains the container can reach.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent.py deleted file mode 100644 index e06dd046..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent.py +++ /dev/null @@ -1,66 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Union, Optional -from datetime import datetime -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_managed_agents_custom_tool import BetaManagedAgentsCustomTool -from .beta_managed_agents_mcp_toolset import BetaManagedAgentsMCPToolset -from .beta_managed_agents_custom_skill import BetaManagedAgentsCustomSkill -from .beta_managed_agents_model_config import BetaManagedAgentsModelConfig -from .beta_managed_agents_anthropic_skill import BetaManagedAgentsAnthropicSkill -from .beta_managed_agents_agent_toolset20260401 import BetaManagedAgentsAgentToolset20260401 -from .beta_managed_agents_mcp_server_url_definition import BetaManagedAgentsMCPServerURLDefinition - -__all__ = ["BetaManagedAgentsAgent", "Skill", "Tool"] - -Skill: TypeAlias = Annotated[ - Union[BetaManagedAgentsAnthropicSkill, BetaManagedAgentsCustomSkill], PropertyInfo(discriminator="type") -] - -Tool: TypeAlias = Annotated[ - Union[BetaManagedAgentsAgentToolset20260401, BetaManagedAgentsMCPToolset, BetaManagedAgentsCustomTool], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsAgent(BaseModel): - """A Managed Agents `agent`.""" - - id: str - - archived_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" - - created_at: datetime - """A timestamp in RFC 3339 format""" - - description: Optional[str] = None - - mcp_servers: List[BetaManagedAgentsMCPServerURLDefinition] - - metadata: Dict[str, str] - - model: BetaManagedAgentsModelConfig - """Model identifier and configuration.""" - - name: str - - skills: List[Skill] - - system: Optional[str] = None - - tools: List[Tool] - - type: Literal["agent"] - - updated_at: datetime - """A timestamp in RFC 3339 format""" - - version: int - """The agent's current version. - - Starts at 1 and increments when the agent is modified. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_params.py deleted file mode 100644 index 364db605..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_params.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsAgentParams"] - - -class BetaManagedAgentsAgentParams(TypedDict, total=False): - """Specification for an Agent. - - Provide a specific `version` or use the short-form `agent="agent_id"` for the most recent version - """ - - id: Required[str] - """The `agent` ID.""" - - type: Required[Literal["agent"]] - - version: int - """The specific `agent` version to use. - - Omit to use the latest version. Must be at least 1 if specified. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_tool_config.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_tool_config.py deleted file mode 100644 index 07ea0532..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_tool_config.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_managed_agents_always_ask_policy import BetaManagedAgentsAlwaysAskPolicy -from .beta_managed_agents_always_allow_policy import BetaManagedAgentsAlwaysAllowPolicy - -__all__ = ["BetaManagedAgentsAgentToolConfig", "PermissionPolicy"] - -PermissionPolicy: TypeAlias = Annotated[ - Union[BetaManagedAgentsAlwaysAllowPolicy, BetaManagedAgentsAlwaysAskPolicy], PropertyInfo(discriminator="type") -] - - -class BetaManagedAgentsAgentToolConfig(BaseModel): - """Configuration for a specific agent tool.""" - - enabled: bool - - name: Literal["bash", "edit", "read", "write", "glob", "grep", "web_fetch", "web_search"] - """Built-in agent tool identifier.""" - - permission_policy: PermissionPolicy - """Permission policy for tool execution.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_tool_config_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_tool_config_params.py deleted file mode 100644 index 63483192..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_tool_config_params.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_managed_agents_always_ask_policy_param import BetaManagedAgentsAlwaysAskPolicyParam -from .beta_managed_agents_always_allow_policy_param import BetaManagedAgentsAlwaysAllowPolicyParam - -__all__ = ["BetaManagedAgentsAgentToolConfigParams", "PermissionPolicy"] - -PermissionPolicy: TypeAlias = Union[BetaManagedAgentsAlwaysAllowPolicyParam, BetaManagedAgentsAlwaysAskPolicyParam] - - -class BetaManagedAgentsAgentToolConfigParams(TypedDict, total=False): - """Configuration override for a specific tool within a toolset.""" - - name: Required[Literal["bash", "edit", "read", "write", "glob", "grep", "web_fetch", "web_search"]] - """Built-in agent tool identifier.""" - - enabled: Optional[bool] - """Whether this tool is enabled and available to Claude. - - Overrides the default_config setting. - """ - - permission_policy: Optional[PermissionPolicy] - """Permission policy for tool execution.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset20260401.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset20260401.py deleted file mode 100644 index fb1b0166..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset20260401.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_managed_agents_agent_tool_config import BetaManagedAgentsAgentToolConfig -from .beta_managed_agents_agent_toolset_default_config import BetaManagedAgentsAgentToolsetDefaultConfig - -__all__ = ["BetaManagedAgentsAgentToolset20260401"] - - -class BetaManagedAgentsAgentToolset20260401(BaseModel): - configs: List[BetaManagedAgentsAgentToolConfig] - - default_config: BetaManagedAgentsAgentToolsetDefaultConfig - """Resolved default configuration for agent tools.""" - - type: Literal["agent_toolset_20260401"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset20260401_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset20260401_params.py deleted file mode 100644 index 5de1c4b4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset20260401_params.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_managed_agents_agent_tool_config_params import BetaManagedAgentsAgentToolConfigParams -from .beta_managed_agents_agent_toolset_default_config_params import BetaManagedAgentsAgentToolsetDefaultConfigParams - -__all__ = ["BetaManagedAgentsAgentToolset20260401Params"] - - -class BetaManagedAgentsAgentToolset20260401Params(TypedDict, total=False): - """Configuration for built-in agent tools. - - Use this to enable or disable groups of tools available to the agent. - """ - - type: Required[Literal["agent_toolset_20260401"]] - - configs: Iterable[BetaManagedAgentsAgentToolConfigParams] - """Per-tool configuration overrides.""" - - default_config: Optional[BetaManagedAgentsAgentToolsetDefaultConfigParams] - """Default configuration for all tools in a toolset.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset_default_config.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset_default_config.py deleted file mode 100644 index e4aa4c8e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset_default_config.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_managed_agents_always_ask_policy import BetaManagedAgentsAlwaysAskPolicy -from .beta_managed_agents_always_allow_policy import BetaManagedAgentsAlwaysAllowPolicy - -__all__ = ["BetaManagedAgentsAgentToolsetDefaultConfig", "PermissionPolicy"] - -PermissionPolicy: TypeAlias = Annotated[ - Union[BetaManagedAgentsAlwaysAllowPolicy, BetaManagedAgentsAlwaysAskPolicy], PropertyInfo(discriminator="type") -] - - -class BetaManagedAgentsAgentToolsetDefaultConfig(BaseModel): - """Resolved default configuration for agent tools.""" - - enabled: bool - - permission_policy: PermissionPolicy - """Permission policy for tool execution.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset_default_config_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset_default_config_params.py deleted file mode 100644 index bdaf1853..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_agent_toolset_default_config_params.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import TypeAlias, TypedDict - -from .beta_managed_agents_always_ask_policy_param import BetaManagedAgentsAlwaysAskPolicyParam -from .beta_managed_agents_always_allow_policy_param import BetaManagedAgentsAlwaysAllowPolicyParam - -__all__ = ["BetaManagedAgentsAgentToolsetDefaultConfigParams", "PermissionPolicy"] - -PermissionPolicy: TypeAlias = Union[BetaManagedAgentsAlwaysAllowPolicyParam, BetaManagedAgentsAlwaysAskPolicyParam] - - -class BetaManagedAgentsAgentToolsetDefaultConfigParams(TypedDict, total=False): - """Default configuration for all tools in a toolset.""" - - enabled: Optional[bool] - """Whether tools are enabled and available to Claude by default. - - Defaults to true if not specified. - """ - - permission_policy: Optional[PermissionPolicy] - """Permission policy for tool execution.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_allow_policy.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_allow_policy.py deleted file mode 100644 index a103ad29..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_allow_policy.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsAlwaysAllowPolicy"] - - -class BetaManagedAgentsAlwaysAllowPolicy(BaseModel): - """Tool calls are automatically approved without user confirmation.""" - - type: Literal["always_allow"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_allow_policy_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_allow_policy_param.py deleted file mode 100644 index 3a7c23dd..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_allow_policy_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsAlwaysAllowPolicyParam"] - - -class BetaManagedAgentsAlwaysAllowPolicyParam(TypedDict, total=False): - """Tool calls are automatically approved without user confirmation.""" - - type: Required[Literal["always_allow"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_ask_policy.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_ask_policy.py deleted file mode 100644 index 2d83eb23..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_ask_policy.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsAlwaysAskPolicy"] - - -class BetaManagedAgentsAlwaysAskPolicy(BaseModel): - """Tool calls require user confirmation before execution.""" - - type: Literal["always_ask"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_ask_policy_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_ask_policy_param.py deleted file mode 100644 index 82c91b41..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_always_ask_policy_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsAlwaysAskPolicyParam"] - - -class BetaManagedAgentsAlwaysAskPolicyParam(TypedDict, total=False): - """Tool calls require user confirmation before execution.""" - - type: Required[Literal["always_ask"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_anthropic_skill.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_anthropic_skill.py deleted file mode 100644 index acb97fda..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_anthropic_skill.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsAnthropicSkill"] - - -class BetaManagedAgentsAnthropicSkill(BaseModel): - """A resolved Anthropic-managed skill.""" - - skill_id: str - - type: Literal["anthropic"] - - version: str diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_anthropic_skill_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_anthropic_skill_params.py deleted file mode 100644 index c235b99e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_anthropic_skill_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsAnthropicSkillParams"] - - -class BetaManagedAgentsAnthropicSkillParams(TypedDict, total=False): - """An Anthropic-managed skill.""" - - skill_id: Required[str] - """Identifier of the Anthropic skill (e.g., "xlsx").""" - - type: Required[Literal["anthropic"]] - - version: Optional[str] - """Version to pin. Defaults to latest if omitted.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_branch_checkout.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_branch_checkout.py deleted file mode 100644 index 520b3eb7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_branch_checkout.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsBranchCheckout"] - - -class BetaManagedAgentsBranchCheckout(BaseModel): - name: str - """Branch name to check out.""" - - type: Literal["branch"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_branch_checkout_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_branch_checkout_param.py deleted file mode 100644 index 312aaca3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_branch_checkout_param.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsBranchCheckoutParam"] - - -class BetaManagedAgentsBranchCheckoutParam(TypedDict, total=False): - name: Required[str] - """Branch name to check out.""" - - type: Required[Literal["branch"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_cache_creation_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_cache_creation_usage.py deleted file mode 100644 index c91e2fd3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_cache_creation_usage.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsCacheCreationUsage"] - - -class BetaManagedAgentsCacheCreationUsage(BaseModel): - """Prompt-cache creation token usage broken down by cache lifetime.""" - - ephemeral_1h_input_tokens: Optional[int] = None - """Tokens used to create 1-hour ephemeral cache entries.""" - - ephemeral_5m_input_tokens: Optional[int] = None - """Tokens used to create 5-minute ephemeral cache entries.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_commit_checkout.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_commit_checkout.py deleted file mode 100644 index 203cf202..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_commit_checkout.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsCommitCheckout"] - - -class BetaManagedAgentsCommitCheckout(BaseModel): - sha: str - """Full commit SHA to check out.""" - - type: Literal["commit"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_commit_checkout_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_commit_checkout_param.py deleted file mode 100644 index a14a7159..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_commit_checkout_param.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsCommitCheckoutParam"] - - -class BetaManagedAgentsCommitCheckoutParam(TypedDict, total=False): - sha: Required[str] - """Full commit SHA to check out.""" - - type: Required[Literal["commit"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_skill.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_skill.py deleted file mode 100644 index 932fc1a9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_skill.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsCustomSkill"] - - -class BetaManagedAgentsCustomSkill(BaseModel): - """A resolved user-created custom skill.""" - - skill_id: str - - type: Literal["custom"] - - version: str diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_skill_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_skill_params.py deleted file mode 100644 index 101426c4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_skill_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsCustomSkillParams"] - - -class BetaManagedAgentsCustomSkillParams(TypedDict, total=False): - """A user-created custom skill.""" - - skill_id: Required[str] - """Tagged ID of the custom skill (e.g., "skill_01XJ5...").""" - - type: Required[Literal["custom"]] - - version: Optional[str] - """Version to pin. Defaults to latest if omitted.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool.py deleted file mode 100644 index efb9c3cd..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_managed_agents_custom_tool_input_schema import BetaManagedAgentsCustomToolInputSchema - -__all__ = ["BetaManagedAgentsCustomTool"] - - -class BetaManagedAgentsCustomTool(BaseModel): - """A custom tool as returned in API responses.""" - - description: str - - input_schema: BetaManagedAgentsCustomToolInputSchema - """JSON Schema for custom tool input parameters.""" - - name: str - - type: Literal["custom"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool_input_schema.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool_input_schema.py deleted file mode 100644 index abf18f78..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool_input_schema.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsCustomToolInputSchema"] - - -class BetaManagedAgentsCustomToolInputSchema(BaseModel): - """JSON Schema for custom tool input parameters.""" - - properties: Optional[Dict[str, object]] = None - """JSON Schema properties defining the tool's input parameters.""" - - required: Optional[List[str]] = None - """List of required property names.""" - - type: Optional[Literal["object"]] = None - """Must be 'object' for tool input schemas.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool_input_schema_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool_input_schema_param.py deleted file mode 100644 index 0df0e187..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool_input_schema_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Optional -from typing_extensions import Literal, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["BetaManagedAgentsCustomToolInputSchemaParam"] - - -class BetaManagedAgentsCustomToolInputSchemaParam(TypedDict, total=False): - """JSON Schema for custom tool input parameters.""" - - properties: Optional[Dict[str, object]] - """JSON Schema properties defining the tool's input parameters.""" - - required: SequenceNotStr[str] - """List of required property names.""" - - type: Literal["object"] - """Must be 'object' for tool input schemas.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool_params.py deleted file mode 100644 index 42a3c2de..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_custom_tool_params.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .beta_managed_agents_custom_tool_input_schema_param import BetaManagedAgentsCustomToolInputSchemaParam - -__all__ = ["BetaManagedAgentsCustomToolParams"] - - -class BetaManagedAgentsCustomToolParams(TypedDict, total=False): - """A custom tool that is executed by the API client rather than the agent. - - When the agent calls this tool, an `agent.custom_tool_use` event is emitted and the session goes idle, waiting for the client to provide the result via a `user.custom_tool_result` event. - """ - - description: Required[str] - """ - Description of what the tool does, shown to the agent to help it decide when to - use the tool. 1-1024 characters. - """ - - input_schema: Required[BetaManagedAgentsCustomToolInputSchemaParam] - """JSON Schema for custom tool input parameters.""" - - name: Required[str] - """Unique name for the tool. - - 1-128 characters; letters, digits, underscores, and hyphens. - """ - - type: Required[Literal["custom"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_deleted_memory_store.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_deleted_memory_store.py deleted file mode 100644 index 03e81c63..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_deleted_memory_store.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsDeletedMemoryStore"] - - -class BetaManagedAgentsDeletedMemoryStore(BaseModel): - id: str - - type: Literal["memory_store_deleted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_deleted_session.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_deleted_session.py deleted file mode 100644 index bd79d823..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_deleted_session.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsDeletedSession"] - - -class BetaManagedAgentsDeletedSession(BaseModel): - """Confirmation that a `session` has been permanently deleted.""" - - id: str - - type: Literal["session_deleted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_deleted_vault.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_deleted_vault.py deleted file mode 100644 index da760f69..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_deleted_vault.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsDeletedVault"] - - -class BetaManagedAgentsDeletedVault(BaseModel): - """Confirmation of a deleted vault.""" - - id: str - """Unique identifier of the deleted vault.""" - - type: Literal["vault_deleted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_file_resource_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_file_resource_params.py deleted file mode 100644 index 7b444b53..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_file_resource_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsFileResourceParams"] - - -class BetaManagedAgentsFileResourceParams(TypedDict, total=False): - """Mount a file uploaded via the Files API into the session.""" - - file_id: Required[str] - """ID of a previously uploaded file.""" - - type: Required[Literal["file"]] - - mount_path: Optional[str] - """Mount path in the container. Defaults to `/mnt/session/uploads/`.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_github_repository_resource_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_github_repository_resource_params.py deleted file mode 100644 index 8cbd8659..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_github_repository_resource_params.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_managed_agents_branch_checkout_param import BetaManagedAgentsBranchCheckoutParam -from .beta_managed_agents_commit_checkout_param import BetaManagedAgentsCommitCheckoutParam - -__all__ = ["BetaManagedAgentsGitHubRepositoryResourceParams", "Checkout"] - -Checkout: TypeAlias = Union[BetaManagedAgentsBranchCheckoutParam, BetaManagedAgentsCommitCheckoutParam] - - -class BetaManagedAgentsGitHubRepositoryResourceParams(TypedDict, total=False): - """Mount a GitHub repository into the session's container.""" - - authorization_token: Required[str] - """GitHub authorization token used to clone the repository.""" - - type: Required[Literal["github_repository"]] - - url: Required[str] - """Github URL of the repository""" - - checkout: Optional[Checkout] - """Branch or commit to check out. Defaults to the repository's default branch.""" - - mount_path: Optional[str] - """Mount path in the container. Defaults to `/workspace/`.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_server_url_definition.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_server_url_definition.py deleted file mode 100644 index 35c6e483..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_server_url_definition.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsMCPServerURLDefinition"] - - -class BetaManagedAgentsMCPServerURLDefinition(BaseModel): - """URL-based MCP server connection as returned in API responses.""" - - name: str - - type: Literal["url"] - - url: str diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_tool_config.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_tool_config.py deleted file mode 100644 index b07734c0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_tool_config.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_managed_agents_always_ask_policy import BetaManagedAgentsAlwaysAskPolicy -from .beta_managed_agents_always_allow_policy import BetaManagedAgentsAlwaysAllowPolicy - -__all__ = ["BetaManagedAgentsMCPToolConfig", "PermissionPolicy"] - -PermissionPolicy: TypeAlias = Annotated[ - Union[BetaManagedAgentsAlwaysAllowPolicy, BetaManagedAgentsAlwaysAskPolicy], PropertyInfo(discriminator="type") -] - - -class BetaManagedAgentsMCPToolConfig(BaseModel): - """Resolved configuration for a specific MCP tool.""" - - enabled: bool - - name: str - - permission_policy: PermissionPolicy - """Permission policy for tool execution.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_tool_config_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_tool_config_params.py deleted file mode 100644 index 899d81c3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_tool_config_params.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Required, TypeAlias, TypedDict - -from .beta_managed_agents_always_ask_policy_param import BetaManagedAgentsAlwaysAskPolicyParam -from .beta_managed_agents_always_allow_policy_param import BetaManagedAgentsAlwaysAllowPolicyParam - -__all__ = ["BetaManagedAgentsMCPToolConfigParams", "PermissionPolicy"] - -PermissionPolicy: TypeAlias = Union[BetaManagedAgentsAlwaysAllowPolicyParam, BetaManagedAgentsAlwaysAskPolicyParam] - - -class BetaManagedAgentsMCPToolConfigParams(TypedDict, total=False): - """Configuration override for a specific MCP tool.""" - - name: Required[str] - """Name of the MCP tool to configure. 1-128 characters.""" - - enabled: Optional[bool] - """Whether this tool is enabled. Overrides the `default_config` setting.""" - - permission_policy: Optional[PermissionPolicy] - """Permission policy for tool execution.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset.py deleted file mode 100644 index bbd56ef4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_managed_agents_mcp_tool_config import BetaManagedAgentsMCPToolConfig -from .beta_managed_agents_mcp_toolset_default_config import BetaManagedAgentsMCPToolsetDefaultConfig - -__all__ = ["BetaManagedAgentsMCPToolset"] - - -class BetaManagedAgentsMCPToolset(BaseModel): - configs: List[BetaManagedAgentsMCPToolConfig] - - default_config: BetaManagedAgentsMCPToolsetDefaultConfig - """Resolved default configuration for all tools from an MCP server.""" - - mcp_server_name: str - - type: Literal["mcp_toolset"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset_default_config.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset_default_config.py deleted file mode 100644 index 8ea69607..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset_default_config.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_managed_agents_always_ask_policy import BetaManagedAgentsAlwaysAskPolicy -from .beta_managed_agents_always_allow_policy import BetaManagedAgentsAlwaysAllowPolicy - -__all__ = ["BetaManagedAgentsMCPToolsetDefaultConfig", "PermissionPolicy"] - -PermissionPolicy: TypeAlias = Annotated[ - Union[BetaManagedAgentsAlwaysAllowPolicy, BetaManagedAgentsAlwaysAskPolicy], PropertyInfo(discriminator="type") -] - - -class BetaManagedAgentsMCPToolsetDefaultConfig(BaseModel): - """Resolved default configuration for all tools from an MCP server.""" - - enabled: bool - - permission_policy: PermissionPolicy - """Permission policy for tool execution.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset_default_config_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset_default_config_params.py deleted file mode 100644 index f29f2958..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset_default_config_params.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import TypeAlias, TypedDict - -from .beta_managed_agents_always_ask_policy_param import BetaManagedAgentsAlwaysAskPolicyParam -from .beta_managed_agents_always_allow_policy_param import BetaManagedAgentsAlwaysAllowPolicyParam - -__all__ = ["BetaManagedAgentsMCPToolsetDefaultConfigParams", "PermissionPolicy"] - -PermissionPolicy: TypeAlias = Union[BetaManagedAgentsAlwaysAllowPolicyParam, BetaManagedAgentsAlwaysAskPolicyParam] - - -class BetaManagedAgentsMCPToolsetDefaultConfigParams(TypedDict, total=False): - """Default configuration for all tools from an MCP server.""" - - enabled: Optional[bool] - """Whether tools are enabled by default. Defaults to true if not specified.""" - - permission_policy: Optional[PermissionPolicy] - """Permission policy for tool execution.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset_params.py deleted file mode 100644 index 14c49091..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_mcp_toolset_params.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_managed_agents_mcp_tool_config_params import BetaManagedAgentsMCPToolConfigParams -from .beta_managed_agents_mcp_toolset_default_config_params import BetaManagedAgentsMCPToolsetDefaultConfigParams - -__all__ = ["BetaManagedAgentsMCPToolsetParams"] - - -class BetaManagedAgentsMCPToolsetParams(TypedDict, total=False): - """Configuration for tools from an MCP server defined in `mcp_servers`.""" - - mcp_server_name: Required[str] - """Name of the MCP server. - - Must match a server name from the mcp_servers array. 1-255 characters. - """ - - type: Required[Literal["mcp_toolset"]] - - configs: Iterable[BetaManagedAgentsMCPToolConfigParams] - """Per-tool configuration overrides.""" - - default_config: Optional[BetaManagedAgentsMCPToolsetDefaultConfigParams] - """Default configuration for all tools from an MCP server.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_memory_store.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_memory_store.py deleted file mode 100644 index 0e3338a3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_memory_store.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsMemoryStore"] - - -class BetaManagedAgentsMemoryStore(BaseModel): - id: str - - created_at: datetime - """A timestamp in RFC 3339 format""" - - name: str - - type: Literal["memory_store"] - - updated_at: datetime - """A timestamp in RFC 3339 format""" - - archived_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" - - description: Optional[str] = None - - metadata: Optional[Dict[str, str]] = None diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_memory_store_resource_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_memory_store_resource_param.py deleted file mode 100644 index 31170e63..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_memory_store_resource_param.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsMemoryStoreResourceParam"] - - -class BetaManagedAgentsMemoryStoreResourceParam(TypedDict, total=False): - """Parameters for attaching a memory store to an agent session.""" - - memory_store_id: Required[str] - """The memory store ID (memstore\\__...). - - Must belong to the caller's organization and workspace. - """ - - type: Required[Literal["memory_store"]] - - access: Optional[Literal["read_write", "read_only"]] - """Access mode for an attached memory store.""" - - instructions: Optional[str] - """Per-attachment guidance for the agent on how to use this store. - - Rendered into the memory section of the system prompt. Max 4096 chars. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model.py deleted file mode 100644 index 66aa606a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, TypeAlias - -__all__ = ["BetaManagedAgentsModel"] - -BetaManagedAgentsModel: TypeAlias = Union[ - Literal[ - "claude-opus-4-7", - "claude-opus-4-6", - "claude-sonnet-4-6", - "claude-haiku-4-5", - "claude-haiku-4-5-20251001", - "claude-opus-4-5", - "claude-opus-4-5-20251101", - "claude-sonnet-4-5", - "claude-sonnet-4-5-20250929", - ], - str, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model_config.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model_config.py deleted file mode 100644 index d8710417..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model_config.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_managed_agents_model import BetaManagedAgentsModel - -__all__ = ["BetaManagedAgentsModelConfig"] - - -class BetaManagedAgentsModelConfig(BaseModel): - """Model identifier and configuration.""" - - id: BetaManagedAgentsModel - """ - The model that will power your agent.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - speed: Optional[Literal["standard", "fast"]] = None - """Inference speed mode. - - `fast` provides significantly faster output token generation at premium pricing. - Not all models support `fast`; invalid combinations are rejected at create time. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model_config_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model_config_params.py deleted file mode 100644 index 2b5886a6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model_config_params.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_managed_agents_model_param import BetaManagedAgentsModelParam - -__all__ = ["BetaManagedAgentsModelConfigParams"] - - -class BetaManagedAgentsModelConfigParams(TypedDict, total=False): - """An object that defines additional configuration control over model use""" - - id: Required[BetaManagedAgentsModelParam] - """ - The model that will power your agent.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - speed: Optional[Literal["standard", "fast"]] - """Inference speed mode. - - `fast` provides significantly faster output token generation at premium pricing. - Not all models support `fast`; invalid combinations are rejected at create time. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model_param.py deleted file mode 100644 index c8cfaf84..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_model_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, TypeAlias - -__all__ = ["BetaManagedAgentsModelParam"] - -BetaManagedAgentsModelParam: TypeAlias = Union[ - Literal[ - "claude-opus-4-7", - "claude-opus-4-6", - "claude-sonnet-4-6", - "claude-haiku-4-5", - "claude-haiku-4-5-20251001", - "claude-opus-4-5", - "claude-opus-4-5-20251101", - "claude-sonnet-4-5", - "claude-sonnet-4-5-20250929", - ], - str, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session.py deleted file mode 100644 index 3203154b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session.py +++ /dev/null @@ -1,59 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, List, Optional -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_managed_agents_session_agent import BetaManagedAgentsSessionAgent -from .beta_managed_agents_session_stats import BetaManagedAgentsSessionStats -from .beta_managed_agents_session_usage import BetaManagedAgentsSessionUsage -from .sessions.beta_managed_agents_session_resource import BetaManagedAgentsSessionResource - -__all__ = ["BetaManagedAgentsSession"] - - -class BetaManagedAgentsSession(BaseModel): - """A Managed Agents `session`.""" - - id: str - - agent: BetaManagedAgentsSessionAgent - """Resolved `agent` definition for a `session`. - - Snapshot of the `agent` at `session` creation time. - """ - - archived_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" - - created_at: datetime - """A timestamp in RFC 3339 format""" - - environment_id: str - - metadata: Dict[str, str] - - resources: List[BetaManagedAgentsSessionResource] - - stats: BetaManagedAgentsSessionStats - """Timing statistics for a session.""" - - status: Literal["rescheduling", "running", "idle", "terminated"] - """SessionStatus enum""" - - title: Optional[str] = None - - type: Literal["session"] - - updated_at: datetime - """A timestamp in RFC 3339 format""" - - usage: BetaManagedAgentsSessionUsage - """Cumulative token usage for a session across all turns.""" - - vault_ids: List[str] - """Vault IDs attached to the session at creation. - - Empty when no vaults were supplied. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session_agent.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session_agent.py deleted file mode 100644 index f21c01b3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session_agent.py +++ /dev/null @@ -1,53 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_managed_agents_custom_tool import BetaManagedAgentsCustomTool -from .beta_managed_agents_mcp_toolset import BetaManagedAgentsMCPToolset -from .beta_managed_agents_custom_skill import BetaManagedAgentsCustomSkill -from .beta_managed_agents_model_config import BetaManagedAgentsModelConfig -from .beta_managed_agents_anthropic_skill import BetaManagedAgentsAnthropicSkill -from .beta_managed_agents_agent_toolset20260401 import BetaManagedAgentsAgentToolset20260401 -from .beta_managed_agents_mcp_server_url_definition import BetaManagedAgentsMCPServerURLDefinition - -__all__ = ["BetaManagedAgentsSessionAgent", "Skill", "Tool"] - -Skill: TypeAlias = Annotated[ - Union[BetaManagedAgentsAnthropicSkill, BetaManagedAgentsCustomSkill], PropertyInfo(discriminator="type") -] - -Tool: TypeAlias = Annotated[ - Union[BetaManagedAgentsAgentToolset20260401, BetaManagedAgentsMCPToolset, BetaManagedAgentsCustomTool], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsSessionAgent(BaseModel): - """Resolved `agent` definition for a `session`. - - Snapshot of the `agent` at `session` creation time. - """ - - id: str - - description: Optional[str] = None - - mcp_servers: List[BetaManagedAgentsMCPServerURLDefinition] - - model: BetaManagedAgentsModelConfig - """Model identifier and configuration.""" - - name: str - - skills: List[Skill] - - system: Optional[str] = None - - tools: List[Tool] - - type: Literal["agent"] - - version: int diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session_stats.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session_stats.py deleted file mode 100644 index df14e0e5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session_stats.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsSessionStats"] - - -class BetaManagedAgentsSessionStats(BaseModel): - """Timing statistics for a session.""" - - active_seconds: Optional[float] = None - """Cumulative time in seconds the session spent in running status. - - Excludes idle time. - """ - - duration_seconds: Optional[float] = None - """Elapsed time since session creation in seconds. - - For terminated sessions, frozen at the final update. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session_usage.py deleted file mode 100644 index 8b81d375..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_session_usage.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel -from .beta_managed_agents_cache_creation_usage import BetaManagedAgentsCacheCreationUsage - -__all__ = ["BetaManagedAgentsSessionUsage"] - - -class BetaManagedAgentsSessionUsage(BaseModel): - """Cumulative token usage for a session across all turns.""" - - cache_creation: Optional[BetaManagedAgentsCacheCreationUsage] = None - """Prompt-cache creation token usage broken down by cache lifetime.""" - - cache_read_input_tokens: Optional[int] = None - """Total tokens read from prompt cache.""" - - input_tokens: Optional[int] = None - """Total input tokens consumed across all turns.""" - - output_tokens: Optional[int] = None - """Total output tokens generated across all turns.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_skill_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_skill_params.py deleted file mode 100644 index 972393a9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_skill_params.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .beta_managed_agents_custom_skill_params import BetaManagedAgentsCustomSkillParams -from .beta_managed_agents_anthropic_skill_params import BetaManagedAgentsAnthropicSkillParams - -__all__ = ["BetaManagedAgentsSkillParams"] - -BetaManagedAgentsSkillParams: TypeAlias = Union[ - BetaManagedAgentsAnthropicSkillParams, BetaManagedAgentsCustomSkillParams -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_url_mcp_server_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_url_mcp_server_params.py deleted file mode 100644 index 8783b06e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_url_mcp_server_params.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsURLMCPServerParams"] - - -class BetaManagedAgentsURLMCPServerParams(TypedDict, total=False): - """URL-based MCP server connection.""" - - name: Required[str] - """Unique name for this server, referenced by mcp_toolset configurations. - - 1-255 characters. - """ - - type: Required[Literal["url"]] - - url: Required[str] - """Endpoint URL for the MCP server.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_vault.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_vault.py deleted file mode 100644 index d95fc2ba..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_managed_agents_vault.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaManagedAgentsVault"] - - -class BetaManagedAgentsVault(BaseModel): - """A vault that stores credentials for use by agents during sessions.""" - - id: str - """Unique identifier for the vault.""" - - archived_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" - - created_at: datetime - """A timestamp in RFC 3339 format""" - - display_name: str - """Human-readable name for the vault.""" - - metadata: Dict[str, str] - """Arbitrary key-value metadata attached to the vault.""" - - type: Literal["vault"] - - updated_at: datetime - """A timestamp in RFC 3339 format""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_config_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_config_param.py deleted file mode 100644 index 088992a1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_config_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["BetaMCPToolConfigParam"] - - -class BetaMCPToolConfigParam(TypedDict, total=False): - """Configuration for a specific tool in an MCP toolset.""" - - defer_loading: bool - - enabled: bool diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_default_config_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_default_config_param.py deleted file mode 100644 index 865416ff..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_default_config_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["BetaMCPToolDefaultConfigParam"] - - -class BetaMCPToolDefaultConfigParam(TypedDict, total=False): - """Default configuration for tools in an MCP toolset.""" - - defer_loading: bool - - enabled: bool diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_result_block.py deleted file mode 100644 index 3be6ffd7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_result_block.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_text_block import BetaTextBlock - -__all__ = ["BetaMCPToolResultBlock"] - - -class BetaMCPToolResultBlock(BaseModel): - content: Union[str, List[BetaTextBlock]] - - is_error: bool - - tool_use_id: str - - type: Literal["mcp_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_use_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_use_block.py deleted file mode 100644 index 32cabdbf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_use_block.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaMCPToolUseBlock"] - - -class BetaMCPToolUseBlock(BaseModel): - id: str - - input: Dict[str, object] - - name: str - """The name of the MCP tool""" - - server_name: str - """The name of the MCP server""" - - type: Literal["mcp_tool_use"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_use_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_use_block_param.py deleted file mode 100644 index 4a67a19f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_tool_use_block_param.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaMCPToolUseBlockParam"] - - -class BetaMCPToolUseBlockParam(TypedDict, total=False): - id: Required[str] - - input: Required[Dict[str, object]] - - name: Required[str] - - server_name: Required[str] - """The name of the MCP server""" - - type: Required[Literal["mcp_tool_use"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_toolset_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_toolset_param.py deleted file mode 100644 index 34fbf23e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_mcp_toolset_param.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_mcp_tool_config_param import BetaMCPToolConfigParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_mcp_tool_default_config_param import BetaMCPToolDefaultConfigParam - -__all__ = ["BetaMCPToolsetParam"] - - -class BetaMCPToolsetParam(TypedDict, total=False): - """Configuration for a group of tools from an MCP server. - - Allows configuring enabled status and defer_loading for all tools - from an MCP server, with optional per-tool overrides. - """ - - mcp_server_name: Required[str] - """Name of the MCP server to configure tools for""" - - type: Required[Literal["mcp_toolset"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - configs: Optional[Dict[str, BetaMCPToolConfigParam]] - """Configuration overrides for specific tools, keyed by tool name""" - - default_config: BetaMCPToolDefaultConfigParam - """Default configuration applied to all tools from this server""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_command.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_command.py deleted file mode 100644 index 7ce68f8e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_command.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from .beta_memory_tool_20250818_view_command import BetaMemoryTool20250818ViewCommand -from .beta_memory_tool_20250818_create_command import BetaMemoryTool20250818CreateCommand -from .beta_memory_tool_20250818_delete_command import BetaMemoryTool20250818DeleteCommand -from .beta_memory_tool_20250818_insert_command import BetaMemoryTool20250818InsertCommand -from .beta_memory_tool_20250818_rename_command import BetaMemoryTool20250818RenameCommand -from .beta_memory_tool_20250818_str_replace_command import BetaMemoryTool20250818StrReplaceCommand - -__all__ = ["BetaMemoryTool20250818Command"] - -BetaMemoryTool20250818Command: TypeAlias = Annotated[ - Union[ - BetaMemoryTool20250818ViewCommand, - BetaMemoryTool20250818CreateCommand, - BetaMemoryTool20250818StrReplaceCommand, - BetaMemoryTool20250818InsertCommand, - BetaMemoryTool20250818DeleteCommand, - BetaMemoryTool20250818RenameCommand, - ], - PropertyInfo(discriminator="command"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_create_command.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_create_command.py deleted file mode 100644 index bd51cdd7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_create_command.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaMemoryTool20250818CreateCommand"] - - -class BetaMemoryTool20250818CreateCommand(BaseModel): - command: Literal["create"] - """Command type identifier""" - - file_text: str - """Content to write to the file""" - - path: str - """Path where the file should be created""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_delete_command.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_delete_command.py deleted file mode 100644 index 044d932f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_delete_command.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaMemoryTool20250818DeleteCommand"] - - -class BetaMemoryTool20250818DeleteCommand(BaseModel): - command: Literal["delete"] - """Command type identifier""" - - path: str - """Path to the file or directory to delete""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_insert_command.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_insert_command.py deleted file mode 100644 index 1970dba0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_insert_command.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaMemoryTool20250818InsertCommand"] - - -class BetaMemoryTool20250818InsertCommand(BaseModel): - command: Literal["insert"] - """Command type identifier""" - - insert_line: int - """Line number where text should be inserted""" - - insert_text: str - """Text to insert at the specified line""" - - path: str - """Path to the file where text should be inserted""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_param.py deleted file mode 100644 index f89a6568..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaMemoryTool20250818Param"] - - -class BetaMemoryTool20250818Param(TypedDict, total=False): - name: Required[Literal["memory"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["memory_20250818"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_rename_command.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_rename_command.py deleted file mode 100644 index 46e27d84..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_rename_command.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaMemoryTool20250818RenameCommand"] - - -class BetaMemoryTool20250818RenameCommand(BaseModel): - command: Literal["rename"] - """Command type identifier""" - - new_path: str - """New path for the file or directory""" - - old_path: str - """Current path of the file or directory""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_str_replace_command.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_str_replace_command.py deleted file mode 100644 index 1d018b1b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_str_replace_command.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaMemoryTool20250818StrReplaceCommand"] - - -class BetaMemoryTool20250818StrReplaceCommand(BaseModel): - command: Literal["str_replace"] - """Command type identifier""" - - new_str: str - """Text to replace with""" - - old_str: str - """Text to search for and replace""" - - path: str - """Path to the file where text should be replaced""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_view_command.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_view_command.py deleted file mode 100644 index 8d540bca..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_memory_tool_20250818_view_command.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaMemoryTool20250818ViewCommand"] - - -class BetaMemoryTool20250818ViewCommand(BaseModel): - command: Literal["view"] - """Command type identifier""" - - path: str - """Path to directory or file to view""" - - view_range: Optional[List[int]] = None - """Optional line range for viewing specific lines""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message.py deleted file mode 100644 index f594bc03..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message.py +++ /dev/null @@ -1,135 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import Literal - -from ..model import Model -from ..._models import BaseModel -from .beta_usage import BetaUsage -from .beta_container import BetaContainer -from .beta_stop_reason import BetaStopReason -from .beta_content_block import BetaContentBlock, BetaContentBlock as BetaContentBlock -from .beta_refusal_stop_details import BetaRefusalStopDetails -from .beta_context_management_response import BetaContextManagementResponse - -__all__ = ["BetaMessage"] - - -class BetaMessage(BaseModel): - id: str - """Unique object identifier. - - The format and length of IDs may change over time. - """ - - container: Optional[BetaContainer] = None - """ - Information about the container used in the request (for the code execution - tool) - """ - - content: List[BetaContentBlock] - """Content generated by the model. - - This is an array of content blocks, each of which has a `type` that determines - its shape. - - Example: - - ```json - [{ "type": "text", "text": "Hi, I'm Claude." }] - ``` - - If the request input `messages` ended with an `assistant` turn, then the - response `content` will continue directly from that last turn. You can use this - to constrain the model's output. - - For example, if the input `messages` were: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Then the response `content` might be: - - ```json - [{ "type": "text", "text": "B)" }] - ``` - """ - - context_management: Optional[BetaContextManagementResponse] = None - """Context management response. - - Information about context management strategies applied during the request. - """ - - model: Model - """ - The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - role: Literal["assistant"] - """Conversational role of the generated message. - - This will always be `"assistant"`. - """ - - stop_details: Optional[BetaRefusalStopDetails] = None - """Structured information about a refusal.""" - - stop_reason: Optional[BetaStopReason] = None - """The reason that we stopped. - - This may be one the following values: - - - `"end_turn"`: the model reached a natural stopping point - - `"max_tokens"`: we exceeded the requested `max_tokens` or the model's maximum - - `"stop_sequence"`: one of your provided custom `stop_sequences` was generated - - `"tool_use"`: the model invoked one or more tools - - `"pause_turn"`: we paused a long-running turn. You may provide the response - back as-is in a subsequent request to let the model continue. - - `"refusal"`: when streaming classifiers intervene to handle potential policy - violations - - In non-streaming mode this value is always non-null. In streaming mode, it is - null in the `message_start` event and non-null otherwise. - """ - - stop_sequence: Optional[str] = None - """Which custom stop sequence was generated, if any. - - This value will be a non-null string if one of your custom stop sequences was - generated. - """ - - type: Literal["message"] - """Object type. - - For Messages, this is always `"message"`. - """ - - usage: BetaUsage - """Billing and rate-limit usage. - - Anthropic's API bills and rate-limits by token counts, as tokens represent the - underlying cost to our systems. - - Under the hood, the API transforms requests into a format suitable for the - model. The model's output then goes through a parsing stage before becoming an - API response. As a result, the token counts in `usage` will not match one-to-one - with the exact visible content of an API request or response. - - For example, `output_tokens` will be non-zero, even for an empty string response - from Claude. - - Total input tokens in a request is the summation of `input_tokens`, - `cache_creation_input_tokens`, and `cache_read_input_tokens`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_delta_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_delta_usage.py deleted file mode 100644 index 3d18dd7a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_delta_usage.py +++ /dev/null @@ -1,37 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel -from .beta_iterations_usage import BetaIterationsUsage -from .beta_server_tool_usage import BetaServerToolUsage - -__all__ = ["BetaMessageDeltaUsage"] - - -class BetaMessageDeltaUsage(BaseModel): - cache_creation_input_tokens: Optional[int] = None - """The cumulative number of input tokens used to create the cache entry.""" - - cache_read_input_tokens: Optional[int] = None - """The cumulative number of input tokens read from the cache.""" - - input_tokens: Optional[int] = None - """The cumulative number of input tokens which were used.""" - - iterations: Optional[BetaIterationsUsage] = None - """Per-iteration token usage breakdown. - - Each entry represents one sampling iteration, with its own input/output token - counts and cache statistics. This allows you to: - - - Determine which iterations exceeded long context thresholds (>=200k tokens) - - Calculate the true context window size from the last iteration - - Understand token accumulation across server-side tool use loops - """ - - output_tokens: int - """The cumulative number of output tokens which were used.""" - - server_tool_use: Optional[BetaServerToolUsage] = None - """The number of server tool requests.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_iteration_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_iteration_usage.py deleted file mode 100644 index 7fd09d16..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_iteration_usage.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_cache_creation import BetaCacheCreation - -__all__ = ["BetaMessageIterationUsage"] - - -class BetaMessageIterationUsage(BaseModel): - """Token usage for a sampling iteration.""" - - cache_creation: Optional[BetaCacheCreation] = None - """Breakdown of cached tokens by TTL""" - - cache_creation_input_tokens: int - """The number of input tokens used to create the cache entry.""" - - cache_read_input_tokens: int - """The number of input tokens read from the cache.""" - - input_tokens: int - """The number of input tokens which were used.""" - - output_tokens: int - """The number of output tokens which were used.""" - - type: Literal["message"] - """Usage for a sampling iteration""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_param.py deleted file mode 100644 index b41e56d3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable -from typing_extensions import Literal, Required, TypedDict - -from .beta_content_block_param import BetaContentBlockParam - -__all__ = ["BetaMessageParam"] - - -class BetaMessageParam(TypedDict, total=False): - content: Required[Union[str, Iterable[BetaContentBlockParam]]] - - role: Required[Literal["user", "assistant"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_tokens_count.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_tokens_count.py deleted file mode 100644 index 6a27a2b3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_message_tokens_count.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel -from .beta_count_tokens_context_management_response import BetaCountTokensContextManagementResponse - -__all__ = ["BetaMessageTokensCount"] - - -class BetaMessageTokensCount(BaseModel): - context_management: Optional[BetaCountTokensContextManagementResponse] = None - """Information about context management applied to the message.""" - - input_tokens: int - """ - The total number of tokens across the provided list of messages, system prompt, - and tools. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_metadata_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_metadata_param.py deleted file mode 100644 index 8ccda216..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_metadata_param.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["BetaMetadataParam"] - - -class BetaMetadataParam(TypedDict, total=False): - user_id: Optional[str] - """An external identifier for the user who is associated with the request. - - This should be a uuid, hash value, or other opaque identifier. Anthropic may use - this id to help detect abuse. Do not include any identifying information such as - name, email address, or phone number. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_model_capabilities.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_model_capabilities.py deleted file mode 100644 index d489a52a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_model_capabilities.py +++ /dev/null @@ -1,40 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel -from .beta_effort_capability import BetaEffortCapability -from .beta_capability_support import BetaCapabilitySupport -from .beta_thinking_capability import BetaThinkingCapability -from .beta_context_management_capability import BetaContextManagementCapability - -__all__ = ["BetaModelCapabilities"] - - -class BetaModelCapabilities(BaseModel): - """Model capability information.""" - - batch: BetaCapabilitySupport - """Whether the model supports the Batch API.""" - - citations: BetaCapabilitySupport - """Whether the model supports citation generation.""" - - code_execution: BetaCapabilitySupport - """Whether the model supports code execution tools.""" - - context_management: BetaContextManagementCapability - """Context management support and available strategies.""" - - effort: BetaEffortCapability - """Effort (reasoning_effort) support and available levels.""" - - image_input: BetaCapabilitySupport - """Whether the model accepts image content blocks.""" - - pdf_input: BetaCapabilitySupport - """Whether the model accepts PDF content blocks.""" - - structured_outputs: BetaCapabilitySupport - """Whether the model supports structured output / JSON mode / strict tool schemas.""" - - thinking: BetaThinkingCapability - """Thinking capability and supported type configurations.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_model_info.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_model_info.py deleted file mode 100644 index 5080d314..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_model_info.py +++ /dev/null @@ -1,39 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_model_capabilities import BetaModelCapabilities - -__all__ = ["BetaModelInfo"] - - -class BetaModelInfo(BaseModel): - id: str - """Unique model identifier.""" - - capabilities: Optional[BetaModelCapabilities] = None - """Model capability information.""" - - created_at: datetime - """RFC 3339 datetime string representing the time at which the model was released. - - May be set to an epoch value if the release date is unknown. - """ - - display_name: str - """A human-readable name for the model.""" - - max_input_tokens: Optional[int] = None - """Maximum input context window size in tokens for this model.""" - - max_tokens: Optional[int] = None - """Maximum value for the `max_tokens` parameter when using this model.""" - - type: Literal["model"] - """Object type. - - For Models, this is always `"model"`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_output_config_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_output_config_param.py deleted file mode 100644 index ec926693..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_output_config_param.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, TypedDict - -from .beta_token_task_budget_param import BetaTokenTaskBudgetParam -from .beta_json_output_format_param import BetaJSONOutputFormatParam - -__all__ = ["BetaOutputConfigParam"] - - -class BetaOutputConfigParam(TypedDict, total=False): - effort: Optional[Literal["low", "medium", "high", "xhigh", "max"]] - """All possible effort levels.""" - - format: Optional[BetaJSONOutputFormatParam] - """A schema to specify Claude's output format in responses. - - See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - """ - - task_budget: Optional[BetaTokenTaskBudgetParam] - """User-configurable total token budget across contexts.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_packages.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_packages.py deleted file mode 100644 index b03ffad6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_packages.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaPackages"] - - -class BetaPackages(BaseModel): - """Packages (and their versions) available in this environment.""" - - apt: List[str] - """Ubuntu/Debian packages to install""" - - cargo: List[str] - """Rust packages to install""" - - gem: List[str] - """Ruby packages to install""" - - go: List[str] - """Go packages to install""" - - npm: List[str] - """Node.js packages to install""" - - pip: List[str] - """Python packages to install""" - - type: Optional[Literal["packages"]] = None - """Package configuration type""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_packages_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_packages_params.py deleted file mode 100644 index 646a210b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_packages_params.py +++ /dev/null @@ -1,38 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["BetaPackagesParams"] - - -class BetaPackagesParams(TypedDict, total=False): - """Specify packages (and optionally their versions) available in this environment. - - When versioning, use the version semantics relevant for the package manager, e.g. for `pip` use `package==1.0.0`. You are responsible for validating the package and version exist. Unversioned installs the latest. - """ - - apt: Optional[SequenceNotStr[str]] - """Ubuntu/Debian packages to install""" - - cargo: Optional[SequenceNotStr[str]] - """Rust packages to install""" - - gem: Optional[SequenceNotStr[str]] - """Ruby packages to install""" - - go: Optional[SequenceNotStr[str]] - """Go packages to install""" - - npm: Optional[SequenceNotStr[str]] - """Node.js packages to install""" - - pip: Optional[SequenceNotStr[str]] - """Python packages to install""" - - type: Literal["packages"] - """Package configuration type""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_plain_text_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_plain_text_source.py deleted file mode 100644 index 17c31684..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_plain_text_source.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaPlainTextSource"] - - -class BetaPlainTextSource(BaseModel): - data: str - - media_type: Literal["text/plain"] - - type: Literal["text"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_plain_text_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_plain_text_source_param.py deleted file mode 100644 index 187a2386..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_plain_text_source_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaPlainTextSourceParam"] - - -class BetaPlainTextSourceParam(TypedDict, total=False): - data: Required[str] - - media_type: Required[Literal["text/plain"]] - - type: Required[Literal["text"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_delta.py deleted file mode 100644 index c7a0691a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_delta.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from .beta_text_delta import BetaTextDelta -from .beta_thinking_delta import BetaThinkingDelta -from .beta_citations_delta import BetaCitationsDelta -from .beta_signature_delta import BetaSignatureDelta -from .beta_input_json_delta import BetaInputJSONDelta -from .beta_compaction_content_block_delta import BetaCompactionContentBlockDelta - -__all__ = ["BetaRawContentBlockDelta"] - -BetaRawContentBlockDelta: TypeAlias = Annotated[ - Union[ - BetaTextDelta, - BetaInputJSONDelta, - BetaCitationsDelta, - BetaThinkingDelta, - BetaSignatureDelta, - BetaCompactionContentBlockDelta, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_delta_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_delta_event.py deleted file mode 100644 index c20eb52c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_delta_event.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_raw_content_block_delta import BetaRawContentBlockDelta - -__all__ = ["BetaRawContentBlockDeltaEvent"] - - -class BetaRawContentBlockDeltaEvent(BaseModel): - delta: BetaRawContentBlockDelta - - index: int - - type: Literal["content_block_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_start_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_start_event.py deleted file mode 100644 index 8cd04883..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_start_event.py +++ /dev/null @@ -1,56 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_text_block import BetaTextBlock -from .beta_thinking_block import BetaThinkingBlock -from .beta_tool_use_block import BetaToolUseBlock -from .beta_compaction_block import BetaCompactionBlock -from .beta_mcp_tool_use_block import BetaMCPToolUseBlock -from .beta_mcp_tool_result_block import BetaMCPToolResultBlock -from .beta_server_tool_use_block import BetaServerToolUseBlock -from .beta_container_upload_block import BetaContainerUploadBlock -from .beta_redacted_thinking_block import BetaRedactedThinkingBlock -from .beta_advisor_tool_result_block import BetaAdvisorToolResultBlock -from .beta_web_fetch_tool_result_block import BetaWebFetchToolResultBlock -from .beta_web_search_tool_result_block import BetaWebSearchToolResultBlock -from .beta_tool_search_tool_result_block import BetaToolSearchToolResultBlock -from .beta_code_execution_tool_result_block import BetaCodeExecutionToolResultBlock -from .beta_bash_code_execution_tool_result_block import BetaBashCodeExecutionToolResultBlock -from .beta_text_editor_code_execution_tool_result_block import BetaTextEditorCodeExecutionToolResultBlock - -__all__ = ["BetaRawContentBlockStartEvent", "ContentBlock"] - -ContentBlock: TypeAlias = Annotated[ - Union[ - BetaTextBlock, - BetaThinkingBlock, - BetaRedactedThinkingBlock, - BetaToolUseBlock, - BetaServerToolUseBlock, - BetaWebSearchToolResultBlock, - BetaWebFetchToolResultBlock, - BetaAdvisorToolResultBlock, - BetaCodeExecutionToolResultBlock, - BetaBashCodeExecutionToolResultBlock, - BetaTextEditorCodeExecutionToolResultBlock, - BetaToolSearchToolResultBlock, - BetaMCPToolUseBlock, - BetaMCPToolResultBlock, - BetaContainerUploadBlock, - BetaCompactionBlock, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaRawContentBlockStartEvent(BaseModel): - content_block: ContentBlock - """Response model for a file uploaded to the container.""" - - index: int - - type: Literal["content_block_start"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_stop_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_stop_event.py deleted file mode 100644 index d8551860..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_content_block_stop_event.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaRawContentBlockStopEvent"] - - -class BetaRawContentBlockStopEvent(BaseModel): - index: int - - type: Literal["content_block_stop"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_delta_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_delta_event.py deleted file mode 100644 index a57b80ae..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_delta_event.py +++ /dev/null @@ -1,55 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_container import BetaContainer -from .beta_stop_reason import BetaStopReason -from .beta_message_delta_usage import BetaMessageDeltaUsage -from .beta_refusal_stop_details import BetaRefusalStopDetails -from .beta_context_management_response import BetaContextManagementResponse - -__all__ = ["BetaRawMessageDeltaEvent", "Delta"] - - -class Delta(BaseModel): - container: Optional[BetaContainer] = None - """ - Information about the container used in the request (for the code execution - tool) - """ - - stop_details: Optional[BetaRefusalStopDetails] = None - """Structured information about a refusal.""" - - stop_reason: Optional[BetaStopReason] = None - - stop_sequence: Optional[str] = None - - -class BetaRawMessageDeltaEvent(BaseModel): - context_management: Optional[BetaContextManagementResponse] = None - """Information about context management strategies applied during the request""" - - delta: Delta - - type: Literal["message_delta"] - - usage: BetaMessageDeltaUsage - """Billing and rate-limit usage. - - Anthropic's API bills and rate-limits by token counts, as tokens represent the - underlying cost to our systems. - - Under the hood, the API transforms requests into a format suitable for the - model. The model's output then goes through a parsing stage before becoming an - API response. As a result, the token counts in `usage` will not match one-to-one - with the exact visible content of an API request or response. - - For example, `output_tokens` will be non-zero, even for an empty string response - from Claude. - - Total input tokens in a request is the summation of `input_tokens`, - `cache_creation_input_tokens`, and `cache_read_input_tokens`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_start_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_start_event.py deleted file mode 100644 index 9bb16f94..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_start_event.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_message import BetaMessage - -__all__ = ["BetaRawMessageStartEvent"] - - -class BetaRawMessageStartEvent(BaseModel): - message: BetaMessage - - type: Literal["message_start"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_stop_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_stop_event.py deleted file mode 100644 index dff33cde..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_stop_event.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaRawMessageStopEvent"] - - -class BetaRawMessageStopEvent(BaseModel): - type: Literal["message_stop"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_stream_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_stream_event.py deleted file mode 100644 index 00ffd7c1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_raw_message_stream_event.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from .beta_raw_message_stop_event import BetaRawMessageStopEvent -from .beta_raw_message_delta_event import BetaRawMessageDeltaEvent -from .beta_raw_message_start_event import BetaRawMessageStartEvent -from .beta_raw_content_block_stop_event import BetaRawContentBlockStopEvent -from .beta_raw_content_block_delta_event import BetaRawContentBlockDeltaEvent -from .beta_raw_content_block_start_event import BetaRawContentBlockStartEvent - -__all__ = ["BetaRawMessageStreamEvent"] - -BetaRawMessageStreamEvent: TypeAlias = Annotated[ - Union[ - BetaRawMessageStartEvent, - BetaRawMessageDeltaEvent, - BetaRawMessageStopEvent, - BetaRawContentBlockStartEvent, - BetaRawContentBlockDeltaEvent, - BetaRawContentBlockStopEvent, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_redacted_thinking_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_redacted_thinking_block.py deleted file mode 100644 index b27bd933..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_redacted_thinking_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaRedactedThinkingBlock"] - - -class BetaRedactedThinkingBlock(BaseModel): - data: str - - type: Literal["redacted_thinking"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_redacted_thinking_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_redacted_thinking_block_param.py deleted file mode 100644 index cc7d870f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_redacted_thinking_block_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaRedactedThinkingBlockParam"] - - -class BetaRedactedThinkingBlockParam(TypedDict, total=False): - data: Required[str] - - type: Required[Literal["redacted_thinking"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_refusal_stop_details.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_refusal_stop_details.py deleted file mode 100644 index d613ea8a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_refusal_stop_details.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaRefusalStopDetails"] - - -class BetaRefusalStopDetails(BaseModel): - """Structured information about a refusal.""" - - category: Optional[Literal["cyber", "bio"]] = None - """The policy category that triggered the refusal. - - `null` when the refusal doesn't map to a named category. - """ - - explanation: Optional[str] = None - """Human-readable explanation of the refusal. - - This text is not guaranteed to be stable. `null` when no explanation is - available for the category. - """ - - type: Literal["refusal"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_document_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_document_block_param.py deleted file mode 100644 index 2b582ecb..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_document_block_param.py +++ /dev/null @@ -1,39 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_url_pdf_source_param import BetaURLPDFSourceParam -from .beta_citations_config_param import BetaCitationsConfigParam -from .beta_base64_pdf_source_param import BetaBase64PDFSourceParam -from .beta_plain_text_source_param import BetaPlainTextSourceParam -from .beta_content_block_source_param import BetaContentBlockSourceParam -from .beta_file_document_source_param import BetaFileDocumentSourceParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaRequestDocumentBlockParam", "Source"] - -Source: TypeAlias = Union[ - BetaBase64PDFSourceParam, - BetaPlainTextSourceParam, - BetaContentBlockSourceParam, - BetaURLPDFSourceParam, - BetaFileDocumentSourceParam, -] - - -class BetaRequestDocumentBlockParam(TypedDict, total=False): - source: Required[Source] - - type: Required[Literal["document"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: Optional[BetaCitationsConfigParam] - - context: Optional[str] - - title: Optional[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_mcp_server_tool_configuration_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_mcp_server_tool_configuration_param.py deleted file mode 100644 index 4edd8125..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_mcp_server_tool_configuration_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["BetaRequestMCPServerToolConfigurationParam"] - - -class BetaRequestMCPServerToolConfigurationParam(TypedDict, total=False): - allowed_tools: Optional[SequenceNotStr[str]] - - enabled: Optional[bool] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_mcp_server_url_definition_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_mcp_server_url_definition_param.py deleted file mode 100644 index d897c857..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_mcp_server_url_definition_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_request_mcp_server_tool_configuration_param import BetaRequestMCPServerToolConfigurationParam - -__all__ = ["BetaRequestMCPServerURLDefinitionParam"] - - -class BetaRequestMCPServerURLDefinitionParam(TypedDict, total=False): - name: Required[str] - - type: Required[Literal["url"]] - - url: Required[str] - - authorization_token: Optional[str] - - tool_configuration: Optional[BetaRequestMCPServerToolConfigurationParam] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_mcp_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_mcp_tool_result_block_param.py deleted file mode 100644 index c32d2c5e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_request_mcp_tool_result_block_param.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_text_block_param import BetaTextBlockParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaRequestMCPToolResultBlockParam"] - - -class BetaRequestMCPToolResultBlockParam(TypedDict, total=False): - tool_use_id: Required[str] - - type: Required[Literal["mcp_tool_result"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - content: Union[str, Iterable[BetaTextBlockParam]] - - is_error: bool diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_search_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_search_result_block_param.py deleted file mode 100644 index 448d9e2d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_search_result_block_param.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_text_block_param import BetaTextBlockParam -from .beta_citations_config_param import BetaCitationsConfigParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaSearchResultBlockParam"] - - -class BetaSearchResultBlockParam(TypedDict, total=False): - content: Required[Iterable[BetaTextBlockParam]] - - source: Required[str] - - title: Required[str] - - type: Required[Literal["search_result"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: BetaCitationsConfigParam diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller.py deleted file mode 100644 index ecd4bdeb..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaServerToolCaller"] - - -class BetaServerToolCaller(BaseModel): - """Tool invocation generated by a server-side tool.""" - - tool_id: str - - type: Literal["code_execution_20250825"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller_20260120.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller_20260120.py deleted file mode 100644 index 44d40ae3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller_20260120.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaServerToolCaller20260120"] - - -class BetaServerToolCaller20260120(BaseModel): - tool_id: str - - type: Literal["code_execution_20260120"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller_20260120_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller_20260120_param.py deleted file mode 100644 index c46f43e6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller_20260120_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaServerToolCaller20260120Param"] - - -class BetaServerToolCaller20260120Param(TypedDict, total=False): - tool_id: Required[str] - - type: Required[Literal["code_execution_20260120"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller_param.py deleted file mode 100644 index ab834437..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_caller_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaServerToolCallerParam"] - - -class BetaServerToolCallerParam(TypedDict, total=False): - """Tool invocation generated by a server-side tool.""" - - tool_id: Required[str] - - type: Required[Literal["code_execution_20250825"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_usage.py deleted file mode 100644 index 2c645880..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_usage.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel - -__all__ = ["BetaServerToolUsage"] - - -class BetaServerToolUsage(BaseModel): - web_fetch_requests: int - """The number of web fetch tool requests.""" - - web_search_requests: int - """The number of web search tool requests.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_use_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_use_block.py deleted file mode 100644 index e4d14f63..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_use_block.py +++ /dev/null @@ -1,38 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_direct_caller import BetaDirectCaller -from .beta_server_tool_caller import BetaServerToolCaller -from .beta_server_tool_caller_20260120 import BetaServerToolCaller20260120 - -__all__ = ["BetaServerToolUseBlock", "Caller"] - -Caller: TypeAlias = Annotated[ - Union[BetaDirectCaller, BetaServerToolCaller, BetaServerToolCaller20260120], PropertyInfo(discriminator="type") -] - - -class BetaServerToolUseBlock(BaseModel): - id: str - - input: Dict[str, object] - - name: Literal[ - "advisor", - "web_search", - "web_fetch", - "code_execution", - "bash_code_execution", - "text_editor_code_execution", - "tool_search_tool_regex", - "tool_search_tool_bm25", - ] - - type: Literal["server_tool_use"] - - caller: Optional[Caller] = None - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_use_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_use_block_param.py deleted file mode 100644 index 7ea08340..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_server_tool_use_block_param.py +++ /dev/null @@ -1,42 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_direct_caller_param import BetaDirectCallerParam -from .beta_server_tool_caller_param import BetaServerToolCallerParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_server_tool_caller_20260120_param import BetaServerToolCaller20260120Param - -__all__ = ["BetaServerToolUseBlockParam", "Caller"] - -Caller: TypeAlias = Union[BetaDirectCallerParam, BetaServerToolCallerParam, BetaServerToolCaller20260120Param] - - -class BetaServerToolUseBlockParam(TypedDict, total=False): - id: Required[str] - - input: Required[Dict[str, object]] - - name: Required[ - Literal[ - "advisor", - "web_search", - "web_fetch", - "code_execution", - "bash_code_execution", - "text_editor_code_execution", - "tool_search_tool_regex", - "tool_search_tool_bm25", - ] - ] - - type: Required[Literal["server_tool_use"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - caller: Caller - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_signature_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_signature_delta.py deleted file mode 100644 index a3586826..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_signature_delta.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaSignatureDelta"] - - -class BetaSignatureDelta(BaseModel): - signature: str - - type: Literal["signature_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_skill.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_skill.py deleted file mode 100644 index d8952290..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_skill.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaSkill"] - - -class BetaSkill(BaseModel): - """A skill that was loaded in a container (response model).""" - - skill_id: str - """Skill ID""" - - type: Literal["anthropic", "custom"] - """Type of skill - either 'anthropic' (built-in) or 'custom' (user-defined)""" - - version: str - """Skill version or 'latest' for most recent version""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_skill_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_skill_params.py deleted file mode 100644 index b33f7bf5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_skill_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaSkillParams"] - - -class BetaSkillParams(TypedDict, total=False): - """Specification for a skill to be loaded in a container (request model).""" - - skill_id: Required[str] - """Skill ID""" - - type: Required[Literal["anthropic", "custom"]] - """Type of skill - either 'anthropic' (built-in) or 'custom' (user-defined)""" - - version: str - """Skill version or 'latest' for most recent version""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_stop_reason.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_stop_reason.py deleted file mode 100644 index 910b2682..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_stop_reason.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["BetaStopReason"] - -BetaStopReason: TypeAlias = Literal[ - "end_turn", - "max_tokens", - "stop_sequence", - "tool_use", - "pause_turn", - "compaction", - "refusal", - "model_context_window_exceeded", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_block.py deleted file mode 100644 index f6374b41..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_block.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_text_citation import BetaTextCitation - -__all__ = ["BetaTextBlock"] - - -class BetaTextBlock(BaseModel): - citations: Optional[List[BetaTextCitation]] = None - """Citations supporting the text block. - - The type of citation returned will depend on the type of document being cited. - Citing a PDF results in `page_location`, plain text results in `char_location`, - and content document results in `content_block_location`. - """ - - text: str - - type: Literal["text"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_block_param.py deleted file mode 100644 index 066f5615..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_block_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_text_citation_param import BetaTextCitationParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaTextBlockParam"] - - -class BetaTextBlockParam(TypedDict, total=False): - text: Required[str] - - type: Required[Literal["text"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: Optional[Iterable[BetaTextCitationParam]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_citation.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_citation.py deleted file mode 100644 index 96b98ec4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_citation.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from .beta_citation_char_location import BetaCitationCharLocation -from .beta_citation_page_location import BetaCitationPageLocation -from .beta_citation_content_block_location import BetaCitationContentBlockLocation -from .beta_citation_search_result_location import BetaCitationSearchResultLocation -from .beta_citations_web_search_result_location import BetaCitationsWebSearchResultLocation - -__all__ = ["BetaTextCitation"] - -BetaTextCitation: TypeAlias = Annotated[ - Union[ - BetaCitationCharLocation, - BetaCitationPageLocation, - BetaCitationContentBlockLocation, - BetaCitationsWebSearchResultLocation, - BetaCitationSearchResultLocation, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_citation_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_citation_param.py deleted file mode 100644 index 03fdac02..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_citation_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .beta_citation_char_location_param import BetaCitationCharLocationParam -from .beta_citation_page_location_param import BetaCitationPageLocationParam -from .beta_citation_content_block_location_param import BetaCitationContentBlockLocationParam -from .beta_citation_search_result_location_param import BetaCitationSearchResultLocationParam -from .beta_citation_web_search_result_location_param import BetaCitationWebSearchResultLocationParam - -__all__ = ["BetaTextCitationParam"] - -BetaTextCitationParam: TypeAlias = Union[ - BetaCitationCharLocationParam, - BetaCitationPageLocationParam, - BetaCitationContentBlockLocationParam, - BetaCitationWebSearchResultLocationParam, - BetaCitationSearchResultLocationParam, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_delta.py deleted file mode 100644 index b94ba5ea..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_delta.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaTextDelta"] - - -class BetaTextDelta(BaseModel): - text: str - - type: Literal["text_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_create_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_create_result_block.py deleted file mode 100644 index 5ba58d9b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_create_result_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaTextEditorCodeExecutionCreateResultBlock"] - - -class BetaTextEditorCodeExecutionCreateResultBlock(BaseModel): - is_file_update: bool - - type: Literal["text_editor_code_execution_create_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_create_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_create_result_block_param.py deleted file mode 100644 index b7837d88..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_create_result_block_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaTextEditorCodeExecutionCreateResultBlockParam"] - - -class BetaTextEditorCodeExecutionCreateResultBlockParam(TypedDict, total=False): - is_file_update: Required[bool] - - type: Required[Literal["text_editor_code_execution_create_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_str_replace_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_str_replace_result_block.py deleted file mode 100644 index 770475bf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_str_replace_result_block.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaTextEditorCodeExecutionStrReplaceResultBlock"] - - -class BetaTextEditorCodeExecutionStrReplaceResultBlock(BaseModel): - lines: Optional[List[str]] = None - - new_lines: Optional[int] = None - - new_start: Optional[int] = None - - old_lines: Optional[int] = None - - old_start: Optional[int] = None - - type: Literal["text_editor_code_execution_str_replace_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_str_replace_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_str_replace_result_block_param.py deleted file mode 100644 index a2daccc5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_str_replace_result_block_param.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["BetaTextEditorCodeExecutionStrReplaceResultBlockParam"] - - -class BetaTextEditorCodeExecutionStrReplaceResultBlockParam(TypedDict, total=False): - type: Required[Literal["text_editor_code_execution_str_replace_result"]] - - lines: Optional[SequenceNotStr[str]] - - new_lines: Optional[int] - - new_start: Optional[int] - - old_lines: Optional[int] - - old_start: Optional[int] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_block.py deleted file mode 100644 index 326c093b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_block.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, TypeAlias - -from ..._models import BaseModel -from .beta_text_editor_code_execution_tool_result_error import BetaTextEditorCodeExecutionToolResultError -from .beta_text_editor_code_execution_view_result_block import BetaTextEditorCodeExecutionViewResultBlock -from .beta_text_editor_code_execution_create_result_block import BetaTextEditorCodeExecutionCreateResultBlock -from .beta_text_editor_code_execution_str_replace_result_block import BetaTextEditorCodeExecutionStrReplaceResultBlock - -__all__ = ["BetaTextEditorCodeExecutionToolResultBlock", "Content"] - -Content: TypeAlias = Union[ - BetaTextEditorCodeExecutionToolResultError, - BetaTextEditorCodeExecutionViewResultBlock, - BetaTextEditorCodeExecutionCreateResultBlock, - BetaTextEditorCodeExecutionStrReplaceResultBlock, -] - - -class BetaTextEditorCodeExecutionToolResultBlock(BaseModel): - content: Content - - tool_use_id: str - - type: Literal["text_editor_code_execution_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_block_param.py deleted file mode 100644 index c56709ca..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_block_param.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_text_editor_code_execution_tool_result_error_param import BetaTextEditorCodeExecutionToolResultErrorParam -from .beta_text_editor_code_execution_view_result_block_param import BetaTextEditorCodeExecutionViewResultBlockParam -from .beta_text_editor_code_execution_create_result_block_param import BetaTextEditorCodeExecutionCreateResultBlockParam -from .beta_text_editor_code_execution_str_replace_result_block_param import ( - BetaTextEditorCodeExecutionStrReplaceResultBlockParam, -) - -__all__ = ["BetaTextEditorCodeExecutionToolResultBlockParam", "Content"] - -Content: TypeAlias = Union[ - BetaTextEditorCodeExecutionToolResultErrorParam, - BetaTextEditorCodeExecutionViewResultBlockParam, - BetaTextEditorCodeExecutionCreateResultBlockParam, - BetaTextEditorCodeExecutionStrReplaceResultBlockParam, -] - - -class BetaTextEditorCodeExecutionToolResultBlockParam(TypedDict, total=False): - content: Required[Content] - - tool_use_id: Required[str] - - type: Required[Literal["text_editor_code_execution_tool_result"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_error.py deleted file mode 100644 index 303ff15f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_error.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaTextEditorCodeExecutionToolResultError"] - - -class BetaTextEditorCodeExecutionToolResultError(BaseModel): - error_code: Literal[ - "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "file_not_found" - ] - - error_message: Optional[str] = None - - type: Literal["text_editor_code_execution_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_error_param.py deleted file mode 100644 index 5b26ca9f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_tool_result_error_param.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaTextEditorCodeExecutionToolResultErrorParam"] - - -class BetaTextEditorCodeExecutionToolResultErrorParam(TypedDict, total=False): - error_code: Required[ - Literal["invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "file_not_found"] - ] - - type: Required[Literal["text_editor_code_execution_tool_result_error"]] - - error_message: Optional[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_view_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_view_result_block.py deleted file mode 100644 index 8eb22bab..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_view_result_block.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaTextEditorCodeExecutionViewResultBlock"] - - -class BetaTextEditorCodeExecutionViewResultBlock(BaseModel): - content: str - - file_type: Literal["text", "image", "pdf"] - - num_lines: Optional[int] = None - - start_line: Optional[int] = None - - total_lines: Optional[int] = None - - type: Literal["text_editor_code_execution_view_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_view_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_view_result_block_param.py deleted file mode 100644 index ff8f0b79..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_text_editor_code_execution_view_result_block_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaTextEditorCodeExecutionViewResultBlockParam"] - - -class BetaTextEditorCodeExecutionViewResultBlockParam(TypedDict, total=False): - content: Required[str] - - file_type: Required[Literal["text", "image", "pdf"]] - - type: Required[Literal["text_editor_code_execution_view_result"]] - - num_lines: Optional[int] - - start_line: Optional[int] - - total_lines: Optional[int] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_block.py deleted file mode 100644 index 9a9c1df8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_block.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaThinkingBlock"] - - -class BetaThinkingBlock(BaseModel): - signature: str - - thinking: str - - type: Literal["thinking"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_block_param.py deleted file mode 100644 index 5bd43180..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_block_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaThinkingBlockParam"] - - -class BetaThinkingBlockParam(TypedDict, total=False): - signature: Required[str] - - thinking: Required[str] - - type: Required[Literal["thinking"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_capability.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_capability.py deleted file mode 100644 index c1bf545b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_capability.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel -from .beta_thinking_types import BetaThinkingTypes - -__all__ = ["BetaThinkingCapability"] - - -class BetaThinkingCapability(BaseModel): - """Thinking capability details.""" - - supported: bool - """Whether this capability is supported by the model.""" - - types: BetaThinkingTypes - """Supported thinking type configurations.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_adaptive_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_adaptive_param.py deleted file mode 100644 index bd755e05..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_adaptive_param.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaThinkingConfigAdaptiveParam"] - - -class BetaThinkingConfigAdaptiveParam(TypedDict, total=False): - type: Required[Literal["adaptive"]] - - display: Optional[Literal["summarized", "omitted"]] - """Controls how thinking content appears in the response. - - When set to `summarized`, thinking is returned normally. When set to `omitted`, - thinking content is redacted but a signature is returned for multi-turn - continuity. Defaults to `summarized`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_disabled_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_disabled_param.py deleted file mode 100644 index e7c4a2a0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_disabled_param.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaThinkingConfigDisabledParam"] - - -class BetaThinkingConfigDisabledParam(TypedDict, total=False): - type: Required[Literal["disabled"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_enabled_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_enabled_param.py deleted file mode 100644 index 6ba9397c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_enabled_param.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaThinkingConfigEnabledParam"] - - -class BetaThinkingConfigEnabledParam(TypedDict, total=False): - budget_tokens: Required[int] - """Determines how many tokens Claude can use for its internal reasoning process. - - Larger budgets can enable more thorough analysis for complex problems, improving - response quality. - - Must be ≥1024 and less than `max_tokens`. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - """ - - type: Required[Literal["enabled"]] - - display: Optional[Literal["summarized", "omitted"]] - """Controls how thinking content appears in the response. - - When set to `summarized`, thinking is returned normally. When set to `omitted`, - thinking content is redacted but a signature is returned for multi-turn - continuity. Defaults to `summarized`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_param.py deleted file mode 100644 index aaf1fa2c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_config_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .beta_thinking_config_enabled_param import BetaThinkingConfigEnabledParam -from .beta_thinking_config_adaptive_param import BetaThinkingConfigAdaptiveParam -from .beta_thinking_config_disabled_param import BetaThinkingConfigDisabledParam - -__all__ = ["BetaThinkingConfigParam"] - -BetaThinkingConfigParam: TypeAlias = Union[ - BetaThinkingConfigEnabledParam, BetaThinkingConfigDisabledParam, BetaThinkingConfigAdaptiveParam -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_delta.py deleted file mode 100644 index 790a304e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_delta.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaThinkingDelta"] - - -class BetaThinkingDelta(BaseModel): - thinking: str - - type: Literal["thinking_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_turns_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_turns_param.py deleted file mode 100644 index 7dd54507..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_turns_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaThinkingTurnsParam"] - - -class BetaThinkingTurnsParam(TypedDict, total=False): - type: Required[Literal["thinking_turns"]] - - value: Required[int] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_types.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_types.py deleted file mode 100644 index 3246681a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_thinking_types.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel -from .beta_capability_support import BetaCapabilitySupport - -__all__ = ["BetaThinkingTypes"] - - -class BetaThinkingTypes(BaseModel): - """Supported thinking type configurations.""" - - adaptive: BetaCapabilitySupport - """Whether the model supports thinking with type 'adaptive' (auto).""" - - enabled: BetaCapabilitySupport - """Whether the model supports thinking with type 'enabled'.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_token_task_budget_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_token_task_budget_param.py deleted file mode 100644 index ec9f5869..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_token_task_budget_param.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaTokenTaskBudgetParam"] - - -class BetaTokenTaskBudgetParam(TypedDict, total=False): - """User-configurable total token budget across contexts.""" - - total: Required[int] - """Total token budget across all contexts in the session.""" - - type: Required[Literal["tokens"]] - """The budget type. Currently only 'tokens' is supported.""" - - remaining: Optional[int] - """Remaining tokens in the budget. - - Use this to track usage across contexts when implementing compaction - client-side. Defaults to total if not provided. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_bash_20241022_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_bash_20241022_param.py deleted file mode 100644 index ef342f63..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_bash_20241022_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolBash20241022Param"] - - -class BetaToolBash20241022Param(TypedDict, total=False): - name: Required[Literal["bash"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["bash_20241022"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_bash_20250124_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_bash_20250124_param.py deleted file mode 100644 index 6394a1ca..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_bash_20250124_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolBash20250124Param"] - - -class BetaToolBash20250124Param(TypedDict, total=False): - name: Required[Literal["bash"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["bash_20250124"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_any_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_any_param.py deleted file mode 100644 index 4d3513ef..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_any_param.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaToolChoiceAnyParam"] - - -class BetaToolChoiceAnyParam(TypedDict, total=False): - """The model will use any available tools.""" - - type: Required[Literal["any"]] - - disable_parallel_tool_use: bool - """Whether to disable parallel tool use. - - Defaults to `false`. If set to `true`, the model will output exactly one tool - use. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_auto_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_auto_param.py deleted file mode 100644 index 97a52ade..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_auto_param.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaToolChoiceAutoParam"] - - -class BetaToolChoiceAutoParam(TypedDict, total=False): - """The model will automatically decide whether to use tools.""" - - type: Required[Literal["auto"]] - - disable_parallel_tool_use: bool - """Whether to disable parallel tool use. - - Defaults to `false`. If set to `true`, the model will output at most one tool - use. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_none_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_none_param.py deleted file mode 100644 index 84612db7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_none_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaToolChoiceNoneParam"] - - -class BetaToolChoiceNoneParam(TypedDict, total=False): - """The model will not be allowed to use tools.""" - - type: Required[Literal["none"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_param.py deleted file mode 100644 index ff6a51aa..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_param.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .beta_tool_choice_any_param import BetaToolChoiceAnyParam -from .beta_tool_choice_auto_param import BetaToolChoiceAutoParam -from .beta_tool_choice_none_param import BetaToolChoiceNoneParam -from .beta_tool_choice_tool_param import BetaToolChoiceToolParam - -__all__ = ["BetaToolChoiceParam"] - -BetaToolChoiceParam: TypeAlias = Union[ - BetaToolChoiceAutoParam, BetaToolChoiceAnyParam, BetaToolChoiceToolParam, BetaToolChoiceNoneParam -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_tool_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_tool_param.py deleted file mode 100644 index f8419582..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_choice_tool_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaToolChoiceToolParam"] - - -class BetaToolChoiceToolParam(TypedDict, total=False): - """The model will use the specified tool with `tool_choice.name`.""" - - name: Required[str] - """The name of the tool to use.""" - - type: Required[Literal["tool"]] - - disable_parallel_tool_use: bool - """Whether to disable parallel tool use. - - Defaults to `false`. If set to `true`, the model will output exactly one tool - use. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_computer_use_20241022_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_computer_use_20241022_param.py deleted file mode 100644 index 177e8dd7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_computer_use_20241022_param.py +++ /dev/null @@ -1,45 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolComputerUse20241022Param"] - - -class BetaToolComputerUse20241022Param(TypedDict, total=False): - display_height_px: Required[int] - """The height of the display in pixels.""" - - display_width_px: Required[int] - """The width of the display in pixels.""" - - name: Required[Literal["computer"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["computer_20241022"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - display_number: Optional[int] - """The X11 display number (e.g. 0, 1) for the display.""" - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_computer_use_20250124_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_computer_use_20250124_param.py deleted file mode 100644 index 3622b5b0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_computer_use_20250124_param.py +++ /dev/null @@ -1,45 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolComputerUse20250124Param"] - - -class BetaToolComputerUse20250124Param(TypedDict, total=False): - display_height_px: Required[int] - """The height of the display in pixels.""" - - display_width_px: Required[int] - """The width of the display in pixels.""" - - name: Required[Literal["computer"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["computer_20250124"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - display_number: Optional[int] - """The X11 display number (e.g. 0, 1) for the display.""" - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_computer_use_20251124_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_computer_use_20251124_param.py deleted file mode 100644 index f4c8eb29..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_computer_use_20251124_param.py +++ /dev/null @@ -1,48 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolComputerUse20251124Param"] - - -class BetaToolComputerUse20251124Param(TypedDict, total=False): - display_height_px: Required[int] - """The height of the display in pixels.""" - - display_width_px: Required[int] - """The width of the display in pixels.""" - - name: Required[Literal["computer"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["computer_20251124"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - display_number: Optional[int] - """The X11 display number (e.g. 0, 1) for the display.""" - - enable_zoom: bool - """Whether to enable an action to take a zoomed-in screenshot of the screen.""" - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_param.py deleted file mode 100644 index e64c26d2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_param.py +++ /dev/null @@ -1,79 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from ..._types import SequenceNotStr -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolParam", "InputSchema"] - - -class InputSchemaTyped(TypedDict, total=False): - """[JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. - - This defines the shape of the `input` that your tool accepts and that the model will produce. - """ - - type: Required[Literal["object"]] - - properties: Optional[Dict[str, object]] - - required: Optional[SequenceNotStr[str]] - - -InputSchema: TypeAlias = Union[InputSchemaTyped, Dict[str, object]] - - -class BetaToolParam(TypedDict, total=False): - input_schema: Required[InputSchema] - """[JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. - - This defines the shape of the `input` that your tool accepts and that the model - will produce. - """ - - name: Required[str] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - description: str - """Description of what this tool does. - - Tool descriptions should be as detailed as possible. The more information that - the model has about what the tool is and how to use it, the better it will - perform. You can use natural language descriptions to reinforce important - aspects of the tool input JSON schema. - """ - - eager_input_streaming: Optional[bool] - """Enable eager input streaming for this tool. - - When true, tool input parameters will be streamed incrementally as they are - generated, and types will be inferred on-the-fly rather than buffering the full - JSON output. When false, streaming is disabled for this tool even if the - fine-grained-tool-streaming beta is active. When null (default), uses the - default behavior based on beta headers. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" - - type: Optional[Literal["custom"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_reference_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_reference_block.py deleted file mode 100644 index 7ae70cd9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_reference_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaToolReferenceBlock"] - - -class BetaToolReferenceBlock(BaseModel): - tool_name: str - - type: Literal["tool_reference"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_reference_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_reference_block_param.py deleted file mode 100644 index 3f6be14b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_reference_block_param.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolReferenceBlockParam"] - - -class BetaToolReferenceBlockParam(TypedDict, total=False): - """Tool reference block that can be included in tool_result content.""" - - tool_name: Required[str] - - type: Required[Literal["tool_reference"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_result_block_param.py deleted file mode 100644 index 6b213c84..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_result_block_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_text_block_param import BetaTextBlockParam -from .beta_image_block_param import BetaImageBlockParam -from .beta_search_result_block_param import BetaSearchResultBlockParam -from .beta_tool_reference_block_param import BetaToolReferenceBlockParam -from .beta_request_document_block_param import BetaRequestDocumentBlockParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolResultBlockParam", "Content"] - -Content: TypeAlias = Union[ - BetaTextBlockParam, - BetaImageBlockParam, - BetaSearchResultBlockParam, - BetaRequestDocumentBlockParam, - BetaToolReferenceBlockParam, -] - - -class BetaToolResultBlockParam(TypedDict, total=False): - tool_use_id: Required[str] - - type: Required[Literal["tool_result"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - content: Union[str, Iterable[Content]] - - is_error: bool diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_bm25_20251119_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_bm25_20251119_param.py deleted file mode 100644 index 41e973a2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_bm25_20251119_param.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolSearchToolBm25_20251119Param"] - - -class BetaToolSearchToolBm25_20251119Param(TypedDict, total=False): - name: Required[Literal["tool_search_tool_bm25"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["tool_search_tool_bm25_20251119", "tool_search_tool_bm25"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_regex_20251119_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_regex_20251119_param.py deleted file mode 100644 index fef4bb43..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_regex_20251119_param.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolSearchToolRegex20251119Param"] - - -class BetaToolSearchToolRegex20251119Param(TypedDict, total=False): - name: Required[Literal["tool_search_tool_regex"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["tool_search_tool_regex_20251119", "tool_search_tool_regex"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_block.py deleted file mode 100644 index 9b957163..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_block.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, TypeAlias - -from ..._models import BaseModel -from .beta_tool_search_tool_result_error import BetaToolSearchToolResultError -from .beta_tool_search_tool_search_result_block import BetaToolSearchToolSearchResultBlock - -__all__ = ["BetaToolSearchToolResultBlock", "Content"] - -Content: TypeAlias = Union[BetaToolSearchToolResultError, BetaToolSearchToolSearchResultBlock] - - -class BetaToolSearchToolResultBlock(BaseModel): - content: Content - - tool_use_id: str - - type: Literal["tool_search_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_block_param.py deleted file mode 100644 index 2a74a254..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_block_param.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_tool_search_tool_result_error_param import BetaToolSearchToolResultErrorParam -from .beta_tool_search_tool_search_result_block_param import BetaToolSearchToolSearchResultBlockParam - -__all__ = ["BetaToolSearchToolResultBlockParam", "Content"] - -Content: TypeAlias = Union[BetaToolSearchToolResultErrorParam, BetaToolSearchToolSearchResultBlockParam] - - -class BetaToolSearchToolResultBlockParam(TypedDict, total=False): - content: Required[Content] - - tool_use_id: Required[str] - - type: Required[Literal["tool_search_tool_result"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_error.py deleted file mode 100644 index c8d93673..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_error.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaToolSearchToolResultError"] - - -class BetaToolSearchToolResultError(BaseModel): - error_code: Literal["invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded"] - - error_message: Optional[str] = None - - type: Literal["tool_search_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_error_param.py deleted file mode 100644 index 6a4a2a88..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_result_error_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaToolSearchToolResultErrorParam"] - - -class BetaToolSearchToolResultErrorParam(TypedDict, total=False): - error_code: Required[Literal["invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded"]] - - type: Required[Literal["tool_search_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_search_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_search_result_block.py deleted file mode 100644 index 9b6265e1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_search_result_block.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_tool_reference_block import BetaToolReferenceBlock - -__all__ = ["BetaToolSearchToolSearchResultBlock"] - - -class BetaToolSearchToolSearchResultBlock(BaseModel): - tool_references: List[BetaToolReferenceBlock] - - type: Literal["tool_search_tool_search_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_search_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_search_result_block_param.py deleted file mode 100644 index 3185f405..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_search_tool_search_result_block_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Literal, Required, TypedDict - -from .beta_tool_reference_block_param import BetaToolReferenceBlockParam - -__all__ = ["BetaToolSearchToolSearchResultBlockParam"] - - -class BetaToolSearchToolSearchResultBlockParam(TypedDict, total=False): - tool_references: Required[Iterable[BetaToolReferenceBlockParam]] - - type: Required[Literal["tool_search_tool_search_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20241022_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20241022_param.py deleted file mode 100644 index 577f0af5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20241022_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolTextEditor20241022Param"] - - -class BetaToolTextEditor20241022Param(TypedDict, total=False): - name: Required[Literal["str_replace_editor"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["text_editor_20241022"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20250124_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20250124_param.py deleted file mode 100644 index fd6462ad..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20250124_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolTextEditor20250124Param"] - - -class BetaToolTextEditor20250124Param(TypedDict, total=False): - name: Required[Literal["str_replace_editor"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["text_editor_20250124"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20250429_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20250429_param.py deleted file mode 100644 index d9caa053..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20250429_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolTextEditor20250429Param"] - - -class BetaToolTextEditor20250429Param(TypedDict, total=False): - name: Required[Literal["str_replace_based_edit_tool"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["text_editor_20250429"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20250728_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20250728_param.py deleted file mode 100644 index 3172e0bb..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_text_editor_20250728_param.py +++ /dev/null @@ -1,42 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaToolTextEditor20250728Param"] - - -class BetaToolTextEditor20250728Param(TypedDict, total=False): - name: Required[Literal["str_replace_based_edit_tool"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["text_editor_20250728"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - max_characters: Optional[int] - """Maximum number of characters to display when viewing a file. - - If not specified, defaults to displaying the full file. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_union_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_union_param.py deleted file mode 100644 index 1899c7da..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_union_param.py +++ /dev/null @@ -1,58 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .beta_tool_param import BetaToolParam -from .beta_mcp_toolset_param import BetaMCPToolsetParam -from .beta_tool_bash_20241022_param import BetaToolBash20241022Param -from .beta_tool_bash_20250124_param import BetaToolBash20250124Param -from .beta_memory_tool_20250818_param import BetaMemoryTool20250818Param -from .beta_advisor_tool_20260301_param import BetaAdvisorTool20260301Param -from .beta_web_fetch_tool_20250910_param import BetaWebFetchTool20250910Param -from .beta_web_fetch_tool_20260209_param import BetaWebFetchTool20260209Param -from .beta_web_fetch_tool_20260309_param import BetaWebFetchTool20260309Param -from .beta_web_search_tool_20250305_param import BetaWebSearchTool20250305Param -from .beta_web_search_tool_20260209_param import BetaWebSearchTool20260209Param -from .beta_tool_text_editor_20241022_param import BetaToolTextEditor20241022Param -from .beta_tool_text_editor_20250124_param import BetaToolTextEditor20250124Param -from .beta_tool_text_editor_20250429_param import BetaToolTextEditor20250429Param -from .beta_tool_text_editor_20250728_param import BetaToolTextEditor20250728Param -from .beta_tool_computer_use_20241022_param import BetaToolComputerUse20241022Param -from .beta_tool_computer_use_20250124_param import BetaToolComputerUse20250124Param -from .beta_tool_computer_use_20251124_param import BetaToolComputerUse20251124Param -from .beta_code_execution_tool_20250522_param import BetaCodeExecutionTool20250522Param -from .beta_code_execution_tool_20250825_param import BetaCodeExecutionTool20250825Param -from .beta_code_execution_tool_20260120_param import BetaCodeExecutionTool20260120Param -from .beta_tool_search_tool_bm25_20251119_param import BetaToolSearchToolBm25_20251119Param -from .beta_tool_search_tool_regex_20251119_param import BetaToolSearchToolRegex20251119Param - -__all__ = ["BetaToolUnionParam"] - -BetaToolUnionParam: TypeAlias = Union[ - BetaToolParam, - BetaToolBash20241022Param, - BetaToolBash20250124Param, - BetaCodeExecutionTool20250522Param, - BetaCodeExecutionTool20250825Param, - BetaCodeExecutionTool20260120Param, - BetaToolComputerUse20241022Param, - BetaMemoryTool20250818Param, - BetaToolComputerUse20250124Param, - BetaToolTextEditor20241022Param, - BetaToolComputerUse20251124Param, - BetaToolTextEditor20250124Param, - BetaToolTextEditor20250429Param, - BetaToolTextEditor20250728Param, - BetaWebSearchTool20250305Param, - BetaWebFetchTool20250910Param, - BetaWebSearchTool20260209Param, - BetaWebFetchTool20260209Param, - BetaWebFetchTool20260309Param, - BetaAdvisorTool20260301Param, - BetaToolSearchToolBm25_20251119Param, - BetaToolSearchToolRegex20251119Param, - BetaMCPToolsetParam, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_use_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_use_block.py deleted file mode 100644 index b53d8f4f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_use_block.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_direct_caller import BetaDirectCaller -from .beta_server_tool_caller import BetaServerToolCaller -from .beta_server_tool_caller_20260120 import BetaServerToolCaller20260120 - -__all__ = ["BetaToolUseBlock", "Caller"] - -Caller: TypeAlias = Annotated[ - Union[BetaDirectCaller, BetaServerToolCaller, BetaServerToolCaller20260120], PropertyInfo(discriminator="type") -] - - -class BetaToolUseBlock(BaseModel): - id: str - - input: Dict[str, object] - - name: str - - type: Literal["tool_use"] - - caller: Optional[Caller] = None - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_use_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_use_block_param.py deleted file mode 100644 index c28e25e0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_use_block_param.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_direct_caller_param import BetaDirectCallerParam -from .beta_server_tool_caller_param import BetaServerToolCallerParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_server_tool_caller_20260120_param import BetaServerToolCaller20260120Param - -__all__ = ["BetaToolUseBlockParam", "Caller"] - -Caller: TypeAlias = Union[BetaDirectCallerParam, BetaServerToolCallerParam, BetaServerToolCaller20260120Param] - - -class BetaToolUseBlockParam(TypedDict, total=False): - id: Required[str] - - input: Required[Dict[str, object]] - - name: Required[str] - - type: Required[Literal["tool_use"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - caller: Caller - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_uses_keep_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_uses_keep_param.py deleted file mode 100644 index 3c67ea67..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_uses_keep_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaToolUsesKeepParam"] - - -class BetaToolUsesKeepParam(TypedDict, total=False): - type: Required[Literal["tool_uses"]] - - value: Required[int] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_uses_trigger_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_uses_trigger_param.py deleted file mode 100644 index 15eafd45..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_tool_uses_trigger_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaToolUsesTriggerParam"] - - -class BetaToolUsesTriggerParam(TypedDict, total=False): - type: Required[Literal["tool_uses"]] - - value: Required[int] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_unrestricted_network.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_unrestricted_network.py deleted file mode 100644 index 2c2fabee..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_unrestricted_network.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaUnrestrictedNetwork"] - - -class BetaUnrestrictedNetwork(BaseModel): - """Unrestricted network access.""" - - type: Literal["unrestricted"] - """Network policy type""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_unrestricted_network_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_unrestricted_network_param.py deleted file mode 100644 index 20cd7109..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_unrestricted_network_param.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaUnrestrictedNetworkParam"] - - -class BetaUnrestrictedNetworkParam(TypedDict, total=False): - """Unrestricted network access.""" - - type: Required[Literal["unrestricted"]] - """Network policy type""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_url_image_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_url_image_source_param.py deleted file mode 100644 index a094a433..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_url_image_source_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaURLImageSourceParam"] - - -class BetaURLImageSourceParam(TypedDict, total=False): - type: Required[Literal["url"]] - - url: Required[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_url_pdf_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_url_pdf_source_param.py deleted file mode 100644 index acc1eabf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_url_pdf_source_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaURLPDFSourceParam"] - - -class BetaURLPDFSourceParam(TypedDict, total=False): - type: Required[Literal["url"]] - - url: Required[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_usage.py deleted file mode 100644 index 897cc1e9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_usage.py +++ /dev/null @@ -1,51 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_cache_creation import BetaCacheCreation -from .beta_iterations_usage import BetaIterationsUsage -from .beta_server_tool_usage import BetaServerToolUsage - -__all__ = ["BetaUsage"] - - -class BetaUsage(BaseModel): - cache_creation: Optional[BetaCacheCreation] = None - """Breakdown of cached tokens by TTL""" - - cache_creation_input_tokens: Optional[int] = None - """The number of input tokens used to create the cache entry.""" - - cache_read_input_tokens: Optional[int] = None - """The number of input tokens read from the cache.""" - - inference_geo: Optional[str] = None - """The geographic region where inference was performed for this request.""" - - input_tokens: int - """The number of input tokens which were used.""" - - iterations: Optional[BetaIterationsUsage] = None - """Per-iteration token usage breakdown. - - Each entry represents one sampling iteration, with its own input/output token - counts and cache statistics. This allows you to: - - - Determine which iterations exceeded long context thresholds (>=200k tokens) - - Calculate the true context window size from the last iteration - - Understand token accumulation across server-side tool use loops - """ - - output_tokens: int - """The number of output tokens which were used.""" - - server_tool_use: Optional[BetaServerToolUsage] = None - """The number of server tool requests.""" - - service_tier: Optional[Literal["standard", "priority", "batch"]] = None - """If the request used the priority, standard, or batch tier.""" - - speed: Optional[Literal["standard", "fast"]] = None - """The inference speed mode used for this request.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_location_param.py deleted file mode 100644 index df7383ec..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_location_param.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaUserLocationParam"] - - -class BetaUserLocationParam(TypedDict, total=False): - type: Required[Literal["approximate"]] - - city: Optional[str] - """The city of the user.""" - - country: Optional[str] - """ - The two letter - [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the - user. - """ - - region: Optional[str] - """The region of the user.""" - - timezone: Optional[str] - """The [IANA timezone](https://nodatime.org/TimeZones) of the user.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_profile.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_profile.py deleted file mode 100644 index b3805720..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_profile.py +++ /dev/null @@ -1,39 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_user_profile_trust_grant import BetaUserProfileTrustGrant - -__all__ = ["BetaUserProfile"] - - -class BetaUserProfile(BaseModel): - id: str - """Unique identifier for this user profile, prefixed `uprof_`.""" - - created_at: datetime - """A timestamp in RFC 3339 format""" - - metadata: Dict[str, str] - """Arbitrary key-value metadata. - - Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. - """ - - trust_grants: Dict[str, BetaUserProfileTrustGrant] - """Trust grants for this profile, keyed by grant name. - - Key omitted when no grant is active or in flight. - """ - - type: Literal["user_profile"] - """Object type. Always `user_profile`.""" - - updated_at: datetime - """A timestamp in RFC 3339 format""" - - external_id: Optional[str] = None - """Platform's own identifier for this user. Not enforced unique.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_profile_enrollment_url.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_profile_enrollment_url.py deleted file mode 100644 index 41fd555a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_profile_enrollment_url.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaUserProfileEnrollmentURL"] - - -class BetaUserProfileEnrollmentURL(BaseModel): - expires_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["enrollment_url"] - """Object type. Always `enrollment_url`.""" - - url: str - """Enrollment URL to send to the end user. Valid until `expires_at`.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_profile_trust_grant.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_profile_trust_grant.py deleted file mode 100644 index 6d590232..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_user_profile_trust_grant.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaUserProfileTrustGrant"] - - -class BetaUserProfileTrustGrant(BaseModel): - status: Literal["active", "pending", "rejected"] - """Status of the trust grant.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_block.py deleted file mode 100644 index 223e494f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_block.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_document_block import BetaDocumentBlock - -__all__ = ["BetaWebFetchBlock"] - - -class BetaWebFetchBlock(BaseModel): - content: BetaDocumentBlock - - retrieved_at: Optional[str] = None - """ISO 8601 timestamp when the content was retrieved""" - - type: Literal["web_fetch_result"] - - url: str - """Fetched content URL""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_block_param.py deleted file mode 100644 index 49f6b41e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_block_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .beta_request_document_block_param import BetaRequestDocumentBlockParam - -__all__ = ["BetaWebFetchBlockParam"] - - -class BetaWebFetchBlockParam(TypedDict, total=False): - content: Required[BetaRequestDocumentBlockParam] - - type: Required[Literal["web_fetch_result"]] - - url: Required[str] - """Fetched content URL""" - - retrieved_at: Optional[str] - """ISO 8601 timestamp when the content was retrieved""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_20250910_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_20250910_param.py deleted file mode 100644 index d323c05a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_20250910_param.py +++ /dev/null @@ -1,57 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr -from .beta_citations_config_param import BetaCitationsConfigParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaWebFetchTool20250910Param"] - - -class BetaWebFetchTool20250910Param(TypedDict, total=False): - name: Required[Literal["web_fetch"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["web_fetch_20250910"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - allowed_domains: Optional[SequenceNotStr[str]] - """List of domains to allow fetching from""" - - blocked_domains: Optional[SequenceNotStr[str]] - """List of domains to block fetching from""" - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: Optional[BetaCitationsConfigParam] - """Citations configuration for fetched documents. - - Citations are disabled by default. - """ - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_content_tokens: Optional[int] - """Maximum number of tokens used by including web page text content in the context. - - The limit is approximate and does not apply to binary content such as PDFs. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_20260209_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_20260209_param.py deleted file mode 100644 index e1b9f045..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_20260209_param.py +++ /dev/null @@ -1,57 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr -from .beta_citations_config_param import BetaCitationsConfigParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaWebFetchTool20260209Param"] - - -class BetaWebFetchTool20260209Param(TypedDict, total=False): - name: Required[Literal["web_fetch"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["web_fetch_20260209"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - allowed_domains: Optional[SequenceNotStr[str]] - """List of domains to allow fetching from""" - - blocked_domains: Optional[SequenceNotStr[str]] - """List of domains to block fetching from""" - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: Optional[BetaCitationsConfigParam] - """Citations configuration for fetched documents. - - Citations are disabled by default. - """ - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_content_tokens: Optional[int] - """Maximum number of tokens used by including web page text content in the context. - - The limit is approximate and does not apply to binary content such as PDFs. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_20260309_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_20260309_param.py deleted file mode 100644 index 10db1e93..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_20260309_param.py +++ /dev/null @@ -1,67 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr -from .beta_citations_config_param import BetaCitationsConfigParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaWebFetchTool20260309Param"] - - -class BetaWebFetchTool20260309Param(TypedDict, total=False): - """Web fetch tool with use_cache parameter for bypassing cached content.""" - - name: Required[Literal["web_fetch"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["web_fetch_20260309"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - allowed_domains: Optional[SequenceNotStr[str]] - """List of domains to allow fetching from""" - - blocked_domains: Optional[SequenceNotStr[str]] - """List of domains to block fetching from""" - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: Optional[BetaCitationsConfigParam] - """Citations configuration for fetched documents. - - Citations are disabled by default. - """ - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_content_tokens: Optional[int] - """Maximum number of tokens used by including web page text content in the context. - - The limit is approximate and does not apply to binary content such as PDFs. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" - - use_cache: bool - """Whether to use cached content. - - Set to false to bypass the cache and fetch fresh content. Only set to false when - the user explicitly requests fresh content or when fetching rapidly-changing - sources. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_block.py deleted file mode 100644 index f0bc96be..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_block.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_direct_caller import BetaDirectCaller -from .beta_web_fetch_block import BetaWebFetchBlock -from .beta_server_tool_caller import BetaServerToolCaller -from .beta_server_tool_caller_20260120 import BetaServerToolCaller20260120 -from .beta_web_fetch_tool_result_error_block import BetaWebFetchToolResultErrorBlock - -__all__ = ["BetaWebFetchToolResultBlock", "Content", "Caller"] - -Content: TypeAlias = Union[BetaWebFetchToolResultErrorBlock, BetaWebFetchBlock] - -Caller: TypeAlias = Annotated[ - Union[BetaDirectCaller, BetaServerToolCaller, BetaServerToolCaller20260120], PropertyInfo(discriminator="type") -] - - -class BetaWebFetchToolResultBlock(BaseModel): - content: Content - - tool_use_id: str - - type: Literal["web_fetch_tool_result"] - - caller: Optional[Caller] = None - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_block_param.py deleted file mode 100644 index ca3c2f3f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_block_param.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_direct_caller_param import BetaDirectCallerParam -from .beta_web_fetch_block_param import BetaWebFetchBlockParam -from .beta_server_tool_caller_param import BetaServerToolCallerParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_server_tool_caller_20260120_param import BetaServerToolCaller20260120Param -from .beta_web_fetch_tool_result_error_block_param import BetaWebFetchToolResultErrorBlockParam - -__all__ = ["BetaWebFetchToolResultBlockParam", "Content", "Caller"] - -Content: TypeAlias = Union[BetaWebFetchToolResultErrorBlockParam, BetaWebFetchBlockParam] - -Caller: TypeAlias = Union[BetaDirectCallerParam, BetaServerToolCallerParam, BetaServerToolCaller20260120Param] - - -class BetaWebFetchToolResultBlockParam(TypedDict, total=False): - content: Required[Content] - - tool_use_id: Required[str] - - type: Required[Literal["web_fetch_tool_result"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - caller: Caller - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_error_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_error_block.py deleted file mode 100644 index d44555a8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_error_block.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_web_fetch_tool_result_error_code import BetaWebFetchToolResultErrorCode - -__all__ = ["BetaWebFetchToolResultErrorBlock"] - - -class BetaWebFetchToolResultErrorBlock(BaseModel): - error_code: BetaWebFetchToolResultErrorCode - - type: Literal["web_fetch_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_error_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_error_block_param.py deleted file mode 100644 index 12f8cf8a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_error_block_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .beta_web_fetch_tool_result_error_code import BetaWebFetchToolResultErrorCode - -__all__ = ["BetaWebFetchToolResultErrorBlockParam"] - - -class BetaWebFetchToolResultErrorBlockParam(TypedDict, total=False): - error_code: Required[BetaWebFetchToolResultErrorCode] - - type: Required[Literal["web_fetch_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_error_code.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_error_code.py deleted file mode 100644 index 7f526093..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_fetch_tool_result_error_code.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["BetaWebFetchToolResultErrorCode"] - -BetaWebFetchToolResultErrorCode: TypeAlias = Literal[ - "invalid_tool_input", - "url_too_long", - "url_not_allowed", - "url_not_accessible", - "unsupported_content_type", - "too_many_requests", - "max_uses_exceeded", - "unavailable", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_result_block.py deleted file mode 100644 index 35ed87ce..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_result_block.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BetaWebSearchResultBlock"] - - -class BetaWebSearchResultBlock(BaseModel): - encrypted_content: str - - page_age: Optional[str] = None - - title: str - - type: Literal["web_search_result"] - - url: str diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_result_block_param.py deleted file mode 100644 index 4a829ddb..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_result_block_param.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaWebSearchResultBlockParam"] - - -class BetaWebSearchResultBlockParam(TypedDict, total=False): - encrypted_content: Required[str] - - title: Required[str] - - type: Required[Literal["web_search_result"]] - - url: Required[str] - - page_age: Optional[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_20250305_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_20250305_param.py deleted file mode 100644 index 5b5c2a44..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_20250305_param.py +++ /dev/null @@ -1,57 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr -from .beta_user_location_param import BetaUserLocationParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaWebSearchTool20250305Param"] - - -class BetaWebSearchTool20250305Param(TypedDict, total=False): - name: Required[Literal["web_search"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["web_search_20250305"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - allowed_domains: Optional[SequenceNotStr[str]] - """If provided, only these domains will be included in results. - - Cannot be used alongside `blocked_domains`. - """ - - blocked_domains: Optional[SequenceNotStr[str]] - """If provided, these domains will never appear in results. - - Cannot be used alongside `allowed_domains`. - """ - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" - - user_location: Optional[BetaUserLocationParam] - """Parameters for the user's location. - - Used to provide more relevant search results. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_20260209_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_20260209_param.py deleted file mode 100644 index 9fd5caec..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_20260209_param.py +++ /dev/null @@ -1,57 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from ..._types import SequenceNotStr -from .beta_user_location_param import BetaUserLocationParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam - -__all__ = ["BetaWebSearchTool20260209Param"] - - -class BetaWebSearchTool20260209Param(TypedDict, total=False): - name: Required[Literal["web_search"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["web_search_20260209"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - allowed_domains: Optional[SequenceNotStr[str]] - """If provided, only these domains will be included in results. - - Cannot be used alongside `blocked_domains`. - """ - - blocked_domains: Optional[SequenceNotStr[str]] - """If provided, these domains will never appear in results. - - Cannot be used alongside `allowed_domains`. - """ - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" - - user_location: Optional[BetaUserLocationParam] - """Parameters for the user's location. - - Used to provide more relevant search results. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_request_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_request_error_param.py deleted file mode 100644 index 7583fb12..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_request_error_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .beta_web_search_tool_result_error_code import BetaWebSearchToolResultErrorCode - -__all__ = ["BetaWebSearchToolRequestErrorParam"] - - -class BetaWebSearchToolRequestErrorParam(TypedDict, total=False): - error_code: Required[BetaWebSearchToolResultErrorCode] - - type: Required[Literal["web_search_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block.py deleted file mode 100644 index dbbbce75..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from ..._models import BaseModel -from .beta_direct_caller import BetaDirectCaller -from .beta_server_tool_caller import BetaServerToolCaller -from .beta_server_tool_caller_20260120 import BetaServerToolCaller20260120 -from .beta_web_search_tool_result_block_content import BetaWebSearchToolResultBlockContent - -__all__ = ["BetaWebSearchToolResultBlock", "Caller"] - -Caller: TypeAlias = Annotated[ - Union[BetaDirectCaller, BetaServerToolCaller, BetaServerToolCaller20260120], PropertyInfo(discriminator="type") -] - - -class BetaWebSearchToolResultBlock(BaseModel): - content: BetaWebSearchToolResultBlockContent - - tool_use_id: str - - type: Literal["web_search_tool_result"] - - caller: Optional[Caller] = None - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block_content.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block_content.py deleted file mode 100644 index 2a03429f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block_content.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union -from typing_extensions import TypeAlias - -from .beta_web_search_result_block import BetaWebSearchResultBlock -from .beta_web_search_tool_result_error import BetaWebSearchToolResultError - -__all__ = ["BetaWebSearchToolResultBlockContent"] - -BetaWebSearchToolResultBlockContent: TypeAlias = Union[BetaWebSearchToolResultError, List[BetaWebSearchResultBlock]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block_param.py deleted file mode 100644 index 8be33a30..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block_param.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_direct_caller_param import BetaDirectCallerParam -from .beta_server_tool_caller_param import BetaServerToolCallerParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_server_tool_caller_20260120_param import BetaServerToolCaller20260120Param -from .beta_web_search_tool_result_block_param_content_param import BetaWebSearchToolResultBlockParamContentParam - -__all__ = ["BetaWebSearchToolResultBlockParam", "Caller"] - -Caller: TypeAlias = Union[BetaDirectCallerParam, BetaServerToolCallerParam, BetaServerToolCaller20260120Param] - - -class BetaWebSearchToolResultBlockParam(TypedDict, total=False): - content: Required[BetaWebSearchToolResultBlockParamContentParam] - - tool_use_id: Required[str] - - type: Required[Literal["web_search_tool_result"]] - - cache_control: Optional[BetaCacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - caller: Caller - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block_param_content_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block_param_content_param.py deleted file mode 100644 index 17edadb3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_block_param_content_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable -from typing_extensions import TypeAlias - -from .beta_web_search_result_block_param import BetaWebSearchResultBlockParam -from .beta_web_search_tool_request_error_param import BetaWebSearchToolRequestErrorParam - -__all__ = ["BetaWebSearchToolResultBlockParamContentParam"] - -BetaWebSearchToolResultBlockParamContentParam: TypeAlias = Union[ - Iterable[BetaWebSearchResultBlockParam], BetaWebSearchToolRequestErrorParam -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_error.py deleted file mode 100644 index 65bd3d5c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_error.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_web_search_tool_result_error_code import BetaWebSearchToolResultErrorCode - -__all__ = ["BetaWebSearchToolResultError"] - - -class BetaWebSearchToolResultError(BaseModel): - error_code: BetaWebSearchToolResultErrorCode - - type: Literal["web_search_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_error_code.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_error_code.py deleted file mode 100644 index 388eb102..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/beta_web_search_tool_result_error_code.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["BetaWebSearchToolResultErrorCode"] - -BetaWebSearchToolResultErrorCode: TypeAlias = Literal[ - "invalid_tool_input", "unavailable", "max_uses_exceeded", "too_many_requests", "query_too_long", "request_too_large" -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/deleted_file.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/deleted_file.py deleted file mode 100644 index 22f8bdfc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/deleted_file.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["DeletedFile"] - - -class DeletedFile(BaseModel): - id: str - """ID of the deleted file.""" - - type: Optional[Literal["file_deleted"]] = None - """Deleted object type. - - For file deletion, this is always `"file_deleted"`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/environment_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/environment_create_params.py deleted file mode 100644 index 446aba0f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/environment_create_params.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam -from .beta_cloud_config_params import BetaCloudConfigParams - -__all__ = ["EnvironmentCreateParams"] - - -class EnvironmentCreateParams(TypedDict, total=False): - name: Required[str] - """Human-readable name for the environment""" - - config: Optional[BetaCloudConfigParams] - """Request params for `cloud` environment configuration. - - Fields default to null; on update, omitted fields preserve the existing value. - """ - - description: Optional[str] - """Optional description of the environment""" - - metadata: Dict[str, str] - """User-provided metadata key-value pairs""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/environment_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/environment_list_params.py deleted file mode 100644 index c077d3c6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/environment_list_params.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["EnvironmentListParams"] - - -class EnvironmentListParams(TypedDict, total=False): - include_archived: bool - """Include archived environments in the response""" - - limit: int - """Maximum number of environments to return""" - - page: Optional[str] - """Opaque cursor from previous response for pagination. - - Pass the `next_page` value from the previous response. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/environment_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/environment_update_params.py deleted file mode 100644 index 1c212f53..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/environment_update_params.py +++ /dev/null @@ -1,35 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam -from .beta_cloud_config_params import BetaCloudConfigParams - -__all__ = ["EnvironmentUpdateParams"] - - -class EnvironmentUpdateParams(TypedDict, total=False): - config: Optional[BetaCloudConfigParams] - """Request params for `cloud` environment configuration. - - Fields default to null; on update, omitted fields preserve the existing value. - """ - - description: Optional[str] - """Updated description of the environment""" - - metadata: Dict[str, Optional[str]] - """User-provided metadata key-value pairs. - - Set a value to null or empty string to delete the key. - """ - - name: Optional[str] - """Updated name for the environment""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/file_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/file_list_params.py deleted file mode 100644 index f9fe4f8f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/file_list_params.py +++ /dev/null @@ -1,40 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["FileListParams"] - - -class FileListParams(TypedDict, total=False): - after_id: str - """ID of the object to use as a cursor for pagination. - - When provided, returns the page of results immediately after this object. - """ - - before_id: str - """ID of the object to use as a cursor for pagination. - - When provided, returns the page of results immediately before this object. - """ - - limit: int - """Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - """ - - scope_id: str - """Filter by scope ID. - - Only returns files associated with the specified scope (e.g., a session ID). - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/file_metadata.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/file_metadata.py deleted file mode 100644 index 9faf8243..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/file_metadata.py +++ /dev/null @@ -1,45 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel -from .beta_file_scope import BetaFileScope - -__all__ = ["FileMetadata"] - - -class FileMetadata(BaseModel): - id: str - """Unique object identifier. - - The format and length of IDs may change over time. - """ - - created_at: datetime - """RFC 3339 datetime string representing when the file was created.""" - - filename: str - """Original filename of the uploaded file.""" - - mime_type: str - """MIME type of the file.""" - - size_bytes: int - """Size of the file in bytes.""" - - type: Literal["file"] - """Object type. - - For files, this is always `"file"`. - """ - - downloadable: Optional[bool] = None - """Whether the file can be downloaded.""" - - scope: Optional[BetaFileScope] = None - """ - The scope of this file, indicating the context in which it was created (e.g., a - session). - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/file_upload_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/file_upload_params.py deleted file mode 100644 index a26c00bf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/file_upload_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, Annotated, TypedDict - -from ..._types import FileTypes -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["FileUploadParams"] - - -class FileUploadParams(TypedDict, total=False): - file: Required[FileTypes] - """The file to upload""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_store_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_store_create_params.py deleted file mode 100644 index e95e325f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_store_create_params.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["MemoryStoreCreateParams"] - - -class MemoryStoreCreateParams(TypedDict, total=False): - name: Required[str] - - description: str - - metadata: Dict[str, str] - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_store_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_store_list_params.py deleted file mode 100644 index c546bbc9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_store_list_params.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Union -from datetime import datetime -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["MemoryStoreListParams"] - - -class MemoryStoreListParams(TypedDict, total=False): - created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] - """Return stores created at or after this time (inclusive).""" - - created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] - """Return stores created at or before this time (inclusive).""" - - include_archived: bool - """Query parameter for include_archived""" - - limit: int - """Query parameter for limit""" - - page: str - """Query parameter for page""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_store_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_store_update_params.py deleted file mode 100644 index 43ba4076..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_store_update_params.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["MemoryStoreUpdateParams"] - - -class MemoryStoreUpdateParams(TypedDict, total=False): - description: Optional[str] - - metadata: Optional[Dict[str, Optional[str]]] - """Metadata patch. - - Set a key to a string to upsert it, or to null to delete it. Omit the field to - preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values - up to 512 chars. - """ - - name: Optional[str] - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/__init__.py deleted file mode 100644 index 536a7693..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .memory_list_params import MemoryListParams as MemoryListParams -from .memory_create_params import MemoryCreateParams as MemoryCreateParams -from .memory_delete_params import MemoryDeleteParams as MemoryDeleteParams -from .memory_update_params import MemoryUpdateParams as MemoryUpdateParams -from .memory_retrieve_params import MemoryRetrieveParams as MemoryRetrieveParams -from .beta_managed_agents_actor import BetaManagedAgentsActor as BetaManagedAgentsActor -from .beta_managed_agents_memory import BetaManagedAgentsMemory as BetaManagedAgentsMemory -from .memory_version_list_params import MemoryVersionListParams as MemoryVersionListParams -from .beta_managed_agents_api_actor import BetaManagedAgentsAPIActor as BetaManagedAgentsAPIActor -from .beta_managed_agents_user_actor import BetaManagedAgentsUserActor as BetaManagedAgentsUserActor -from .memory_version_retrieve_params import MemoryVersionRetrieveParams as MemoryVersionRetrieveParams -from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView as BetaManagedAgentsMemoryView -from .beta_managed_agents_memory_prefix import BetaManagedAgentsMemoryPrefix as BetaManagedAgentsMemoryPrefix -from .beta_managed_agents_session_actor import BetaManagedAgentsSessionActor as BetaManagedAgentsSessionActor -from .beta_managed_agents_deleted_memory import BetaManagedAgentsDeletedMemory as BetaManagedAgentsDeletedMemory -from .beta_managed_agents_memory_version import BetaManagedAgentsMemoryVersion as BetaManagedAgentsMemoryVersion -from .beta_managed_agents_memory_list_item import BetaManagedAgentsMemoryListItem as BetaManagedAgentsMemoryListItem -from .beta_managed_agents_precondition_param import ( - BetaManagedAgentsPreconditionParam as BetaManagedAgentsPreconditionParam, -) -from .beta_managed_agents_memory_version_operation import ( - BetaManagedAgentsMemoryVersionOperation as BetaManagedAgentsMemoryVersionOperation, -) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_actor.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_actor.py deleted file mode 100644 index 06edaa7e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_actor.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ...._utils import PropertyInfo -from .beta_managed_agents_api_actor import BetaManagedAgentsAPIActor -from .beta_managed_agents_user_actor import BetaManagedAgentsUserActor -from .beta_managed_agents_session_actor import BetaManagedAgentsSessionActor - -__all__ = ["BetaManagedAgentsActor"] - -BetaManagedAgentsActor: TypeAlias = Annotated[ - Union[BetaManagedAgentsSessionActor, BetaManagedAgentsAPIActor, BetaManagedAgentsUserActor], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_api_actor.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_api_actor.py deleted file mode 100644 index 5b0b17e5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_api_actor.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsAPIActor"] - - -class BetaManagedAgentsAPIActor(BaseModel): - api_key_id: str - - type: Literal["api_actor"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_deleted_memory.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_deleted_memory.py deleted file mode 100644 index 8d56a703..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_deleted_memory.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsDeletedMemory"] - - -class BetaManagedAgentsDeletedMemory(BaseModel): - id: str - - type: Literal["memory_deleted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory.py deleted file mode 100644 index c36955ef..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsMemory"] - - -class BetaManagedAgentsMemory(BaseModel): - id: str - - content_sha256: str - - content_size_bytes: int - - created_at: datetime - """A timestamp in RFC 3339 format""" - - memory_store_id: str - - memory_version_id: str - - path: str - - type: Literal["memory"] - - updated_at: datetime - """A timestamp in RFC 3339 format""" - - content: Optional[str] = None diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_list_item.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_list_item.py deleted file mode 100644 index eb849ec0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_list_item.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ...._utils import PropertyInfo -from .beta_managed_agents_memory import BetaManagedAgentsMemory -from .beta_managed_agents_memory_prefix import BetaManagedAgentsMemoryPrefix - -__all__ = ["BetaManagedAgentsMemoryListItem"] - -BetaManagedAgentsMemoryListItem: TypeAlias = Annotated[ - Union[BetaManagedAgentsMemory, BetaManagedAgentsMemoryPrefix], PropertyInfo(discriminator="type") -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_prefix.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_prefix.py deleted file mode 100644 index e160e5ef..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_prefix.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsMemoryPrefix"] - - -class BetaManagedAgentsMemoryPrefix(BaseModel): - path: str - - type: Literal["memory_prefix"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_version.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_version.py deleted file mode 100644 index c6539c43..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_version.py +++ /dev/null @@ -1,42 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel -from .beta_managed_agents_actor import BetaManagedAgentsActor -from .beta_managed_agents_memory_version_operation import BetaManagedAgentsMemoryVersionOperation - -__all__ = ["BetaManagedAgentsMemoryVersion"] - - -class BetaManagedAgentsMemoryVersion(BaseModel): - id: str - - created_at: datetime - """A timestamp in RFC 3339 format""" - - memory_id: str - - memory_store_id: str - - operation: BetaManagedAgentsMemoryVersionOperation - """MemoryVersionOperation enum""" - - type: Literal["memory_version"] - - content: Optional[str] = None - - content_sha256: Optional[str] = None - - content_size_bytes: Optional[int] = None - - created_by: Optional[BetaManagedAgentsActor] = None - - path: Optional[str] = None - - redacted_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" - - redacted_by: Optional[BetaManagedAgentsActor] = None diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_version_operation.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_version_operation.py deleted file mode 100644 index 611b136c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_version_operation.py +++ /dev/null @@ -1,7 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["BetaManagedAgentsMemoryVersionOperation"] - -BetaManagedAgentsMemoryVersionOperation: TypeAlias = Literal["created", "modified", "deleted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_view.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_view.py deleted file mode 100644 index b869ebdc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_memory_view.py +++ /dev/null @@ -1,7 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["BetaManagedAgentsMemoryView"] - -BetaManagedAgentsMemoryView: TypeAlias = Literal["basic", "full"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_precondition_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_precondition_param.py deleted file mode 100644 index 2b38bec1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_precondition_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsPreconditionParam"] - - -class BetaManagedAgentsPreconditionParam(TypedDict, total=False): - type: Required[Literal["content_sha256"]] - - content_sha256: str diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_session_actor.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_session_actor.py deleted file mode 100644 index 45839671..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_session_actor.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsSessionActor"] - - -class BetaManagedAgentsSessionActor(BaseModel): - session_id: str - - type: Literal["session_actor"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_user_actor.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_user_actor.py deleted file mode 100644 index 6730a28e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/beta_managed_agents_user_actor.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsUserActor"] - - -class BetaManagedAgentsUserActor(BaseModel): - type: Literal["user_actor"] - - user_id: str diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_create_params.py deleted file mode 100644 index 093784d0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_create_params.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView - -__all__ = ["MemoryCreateParams"] - - -class MemoryCreateParams(TypedDict, total=False): - content: Required[Optional[str]] - - path: Required[str] - - view: BetaManagedAgentsMemoryView - """Query parameter for view""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_delete_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_delete_params.py deleted file mode 100644 index fdf5f320..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_delete_params.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam - -__all__ = ["MemoryDeleteParams"] - - -class MemoryDeleteParams(TypedDict, total=False): - memory_store_id: Required[str] - - expected_content_sha256: str - """Query parameter for expected_content_sha256""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_list_params.py deleted file mode 100644 index 39c7650c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_list_params.py +++ /dev/null @@ -1,42 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Literal, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView - -__all__ = ["MemoryListParams"] - - -class MemoryListParams(TypedDict, total=False): - depth: int - """Query parameter for depth""" - - limit: int - """Query parameter for limit""" - - order: Literal["asc", "desc"] - """Query parameter for order""" - - order_by: str - """Query parameter for order_by""" - - page: str - """Query parameter for page""" - - path_prefix: str - """ - Optional path prefix filter (raw string-prefix match; include a trailing slash - for directory-scoped lists). This value appears in request URLs. Do not include - secrets or personally identifiable information. - """ - - view: BetaManagedAgentsMemoryView - """Query parameter for view""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_retrieve_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_retrieve_params.py deleted file mode 100644 index 5408384b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_retrieve_params.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView - -__all__ = ["MemoryRetrieveParams"] - - -class MemoryRetrieveParams(TypedDict, total=False): - memory_store_id: Required[str] - - view: BetaManagedAgentsMemoryView - """Query parameter for view""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_update_params.py deleted file mode 100644 index ec2a4183..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_update_params.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView -from .beta_managed_agents_precondition_param import BetaManagedAgentsPreconditionParam - -__all__ = ["MemoryUpdateParams"] - - -class MemoryUpdateParams(TypedDict, total=False): - memory_store_id: Required[str] - - view: BetaManagedAgentsMemoryView - """Query parameter for view""" - - content: Optional[str] - - path: Optional[str] - - precondition: BetaManagedAgentsPreconditionParam - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_version_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_version_list_params.py deleted file mode 100644 index dbcb6f45..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_version_list_params.py +++ /dev/null @@ -1,46 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Union -from datetime import datetime -from typing_extensions import Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView -from .beta_managed_agents_memory_version_operation import BetaManagedAgentsMemoryVersionOperation - -__all__ = ["MemoryVersionListParams"] - - -class MemoryVersionListParams(TypedDict, total=False): - api_key_id: str - """Query parameter for api_key_id""" - - created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] - """Return versions created at or after this time (inclusive).""" - - created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] - """Return versions created at or before this time (inclusive).""" - - limit: int - """Query parameter for limit""" - - memory_id: str - """Query parameter for memory_id""" - - operation: BetaManagedAgentsMemoryVersionOperation - """Query parameter for operation""" - - page: str - """Query parameter for page""" - - session_id: str - """Query parameter for session_id""" - - view: BetaManagedAgentsMemoryView - """Query parameter for view""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_version_retrieve_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_version_retrieve_params.py deleted file mode 100644 index 0eb20891..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/memory_stores/memory_version_retrieve_params.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView - -__all__ = ["MemoryVersionRetrieveParams"] - - -class MemoryVersionRetrieveParams(TypedDict, total=False): - memory_store_id: Required[str] - - view: BetaManagedAgentsMemoryView - """Query parameter for view""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/message_count_tokens_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/message_count_tokens_params.py deleted file mode 100644 index 17842a7c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/message_count_tokens_params.py +++ /dev/null @@ -1,291 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Union, Iterable, Optional -from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict - -from ..._utils import PropertyInfo -from ..model_param import ModelParam -from .beta_tool_param import BetaToolParam -from .beta_message_param import BetaMessageParam -from ..anthropic_beta_param import AnthropicBetaParam -from .beta_text_block_param import BetaTextBlockParam -from .beta_mcp_toolset_param import BetaMCPToolsetParam -from .beta_tool_choice_param import BetaToolChoiceParam -from .beta_output_config_param import BetaOutputConfigParam -from .beta_thinking_config_param import BetaThinkingConfigParam -from .beta_json_output_format_param import BetaJSONOutputFormatParam -from .beta_tool_bash_20241022_param import BetaToolBash20241022Param -from .beta_tool_bash_20250124_param import BetaToolBash20250124Param -from .beta_memory_tool_20250818_param import BetaMemoryTool20250818Param -from .beta_advisor_tool_20260301_param import BetaAdvisorTool20260301Param -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_web_fetch_tool_20250910_param import BetaWebFetchTool20250910Param -from .beta_web_fetch_tool_20260209_param import BetaWebFetchTool20260209Param -from .beta_web_fetch_tool_20260309_param import BetaWebFetchTool20260309Param -from .beta_web_search_tool_20250305_param import BetaWebSearchTool20250305Param -from .beta_web_search_tool_20260209_param import BetaWebSearchTool20260209Param -from .beta_context_management_config_param import BetaContextManagementConfigParam -from .beta_tool_text_editor_20241022_param import BetaToolTextEditor20241022Param -from .beta_tool_text_editor_20250124_param import BetaToolTextEditor20250124Param -from .beta_tool_text_editor_20250429_param import BetaToolTextEditor20250429Param -from .beta_tool_text_editor_20250728_param import BetaToolTextEditor20250728Param -from .beta_tool_computer_use_20241022_param import BetaToolComputerUse20241022Param -from .beta_tool_computer_use_20250124_param import BetaToolComputerUse20250124Param -from .beta_tool_computer_use_20251124_param import BetaToolComputerUse20251124Param -from .beta_code_execution_tool_20250522_param import BetaCodeExecutionTool20250522Param -from .beta_code_execution_tool_20250825_param import BetaCodeExecutionTool20250825Param -from .beta_code_execution_tool_20260120_param import BetaCodeExecutionTool20260120Param -from .beta_tool_search_tool_bm25_20251119_param import BetaToolSearchToolBm25_20251119Param -from .beta_tool_search_tool_regex_20251119_param import BetaToolSearchToolRegex20251119Param -from .beta_request_mcp_server_url_definition_param import BetaRequestMCPServerURLDefinitionParam - -__all__ = ["MessageCountTokensParams", "Tool"] - - -class MessageCountTokensParams(TypedDict, total=False): - messages: Required[Iterable[BetaMessageParam]] - """Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - """ - - model: Required[ModelParam] - """ - The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - cache_control: Optional[BetaCacheControlEphemeralParam] - """ - Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - """ - - context_management: Optional[BetaContextManagementConfigParam] - """Context management configuration. - - This allows you to control how Claude manages context across multiple requests, - such as whether to clear function results or not. - """ - - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] - """MCP servers to be utilized in this request""" - - output_config: BetaOutputConfigParam - """Configuration options for the model's output, such as the output format.""" - - output_format: Optional[BetaJSONOutputFormatParam] - """Deprecated: Use `output_config.format` instead. - - See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - - A schema to specify Claude's output format in responses. This parameter will be - removed in a future release. - """ - - speed: Optional[Literal["standard", "fast"]] - """The inference speed mode for this request. - - `"fast"` enables high output-tokens-per-second inference. - """ - - system: Union[str, Iterable[BetaTextBlockParam]] - """System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - """ - - thinking: BetaThinkingConfigParam - """Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - """ - - tool_choice: BetaToolChoiceParam - """How the model should use the provided tools. - - The model can use a specific tool, any available tool, decide by itself, or not - use tools at all. - """ - - tools: Iterable[Tool] - """Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" - - -Tool: TypeAlias = Union[ - BetaToolParam, - BetaToolBash20241022Param, - BetaToolBash20250124Param, - BetaCodeExecutionTool20250522Param, - BetaCodeExecutionTool20250825Param, - BetaCodeExecutionTool20260120Param, - BetaToolComputerUse20241022Param, - BetaMemoryTool20250818Param, - BetaToolComputerUse20250124Param, - BetaToolTextEditor20241022Param, - BetaToolComputerUse20251124Param, - BetaToolTextEditor20250124Param, - BetaToolTextEditor20250429Param, - BetaToolTextEditor20250728Param, - BetaWebSearchTool20250305Param, - BetaWebFetchTool20250910Param, - BetaWebSearchTool20260209Param, - BetaWebFetchTool20260209Param, - BetaWebFetchTool20260309Param, - BetaAdvisorTool20260301Param, - BetaToolSearchToolBm25_20251119Param, - BetaToolSearchToolRegex20251119Param, - BetaMCPToolsetParam, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/message_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/message_create_params.py deleted file mode 100644 index 419e921b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/message_create_params.py +++ /dev/null @@ -1,364 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Union, Generic, Iterable, Optional -from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict - -from ..._types import SequenceNotStr -from ..._utils import PropertyInfo -from ..model_param import ModelParam -from .beta_message_param import BetaMessageParam -from .beta_metadata_param import BetaMetadataParam -from .parsed_beta_message import ResponseFormatT -from ..anthropic_beta_param import AnthropicBetaParam -from .beta_container_params import BetaContainerParams -from .beta_text_block_param import BetaTextBlockParam -from .beta_tool_union_param import BetaToolUnionParam -from .beta_tool_choice_param import BetaToolChoiceParam -from .beta_output_config_param import BetaOutputConfigParam -from .beta_thinking_config_param import BetaThinkingConfigParam -from .beta_json_output_format_param import BetaJSONOutputFormatParam -from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam -from .beta_context_management_config_param import BetaContextManagementConfigParam -from .beta_request_mcp_server_url_definition_param import BetaRequestMCPServerURLDefinitionParam - -__all__ = [ - "MessageCreateParamsBase", - "Container", - "MessageCreateParamsNonStreaming", - "MessageCreateParamsStreaming", - "OutputFormat", -] - - -class MessageCreateParamsBase(TypedDict, total=False): - max_tokens: Required[int] - """The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - """ - - messages: Required[Iterable[BetaMessageParam]] - """Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - """ - - model: Required[ModelParam] - """ - The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - cache_control: Optional[BetaCacheControlEphemeralParam] - """ - Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - """ - - container: Optional[Container] - """Container identifier for reuse across requests.""" - - context_management: Optional[BetaContextManagementConfigParam] - """Context management configuration. - - This allows you to control how Claude manages context across multiple requests, - such as whether to clear function results or not. - """ - - inference_geo: Optional[str] - """Specifies the geographic region for inference processing. - - If not specified, the workspace's `default_inference_geo` is used. - """ - - mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] - """MCP servers to be utilized in this request""" - - metadata: BetaMetadataParam - """An object describing metadata about the request.""" - - output_config: BetaOutputConfigParam - """Configuration options for the model's output, such as the output format.""" - - output_format: Optional[BetaJSONOutputFormatParam] - """Deprecated: Use `output_config.format` instead. - - See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - - A schema to specify Claude's output format in responses. This parameter will be - removed in a future release. - """ - - service_tier: Literal["auto", "standard_only"] - """ - Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - """ - - speed: Optional[Literal["standard", "fast"]] - """The inference speed mode for this request. - - `"fast"` enables high output-tokens-per-second inference. - """ - - stop_sequences: SequenceNotStr[str] - """Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - """ - - system: Union[str, Iterable[BetaTextBlockParam]] - """System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - """ - - temperature: float - """Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - """ - - thinking: BetaThinkingConfigParam - """Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - """ - - tool_choice: BetaToolChoiceParam - """How the model should use the provided tools. - - The model can use a specific tool, any available tool, decide by itself, or not - use tools at all. - """ - - tools: Iterable[BetaToolUnionParam] - """Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - """ - - top_k: int - """Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - """ - - top_p: float - """Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - """ - - user_profile_id: Optional[str] - """The user profile ID to attribute this request to. - - Use when acting on behalf of a party other than your organization. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" - - -Container: TypeAlias = Union[BetaContainerParams, str] - - -class ParseMessageCreateParamsBase(MessageCreateParamsBase, Generic[ResponseFormatT]): - output_format: type[ResponseFormatT] # type: ignore[misc] - - -class OutputFormat(TypedDict, total=False): - schema: Required[object] - """The JSON schema of the format""" - - type: Required[Literal["json_schema"]] - - -class MessageCreateParamsNonStreaming(MessageCreateParamsBase, total=False): - stream: Literal[False] - """Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - """ - - -class MessageCreateParamsStreaming(MessageCreateParamsBase): - stream: Required[Literal[True]] - """Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - """ - - -MessageCreateParams = Union[MessageCreateParamsNonStreaming, MessageCreateParamsStreaming] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/__init__.py deleted file mode 100644 index fef14dd1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .batch_list_params import BatchListParams as BatchListParams -from .beta_message_batch import BetaMessageBatch as BetaMessageBatch -from .batch_create_params import BatchCreateParams as BatchCreateParams -from .beta_message_batch_result import BetaMessageBatchResult as BetaMessageBatchResult -from .beta_deleted_message_batch import BetaDeletedMessageBatch as BetaDeletedMessageBatch -from .beta_message_batch_errored_result import BetaMessageBatchErroredResult as BetaMessageBatchErroredResult -from .beta_message_batch_expired_result import BetaMessageBatchExpiredResult as BetaMessageBatchExpiredResult -from .beta_message_batch_request_counts import BetaMessageBatchRequestCounts as BetaMessageBatchRequestCounts -from .beta_message_batch_canceled_result import BetaMessageBatchCanceledResult as BetaMessageBatchCanceledResult -from .beta_message_batch_succeeded_result import BetaMessageBatchSucceededResult as BetaMessageBatchSucceededResult -from .beta_message_batch_individual_response import ( - BetaMessageBatchIndividualResponse as BetaMessageBatchIndividualResponse, -) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/batch_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/batch_create_params.py deleted file mode 100644 index a24181f4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/batch_create_params.py +++ /dev/null @@ -1,49 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Iterable, Optional -from typing_extensions import Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam -from ..message_create_params import MessageCreateParamsNonStreaming - -__all__ = ["BatchCreateParams", "Request"] - - -class BatchCreateParams(TypedDict, total=False): - requests: Required[Iterable[Request]] - """List of requests for prompt completion. - - Each is an individual request to create a Message. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" - - - - user_profile_id: Optional[str] - """The user profile ID to attribute this request to. - - Use when acting on behalf of a party other than your organization. - """ - - -class Request(TypedDict, total=False): - custom_id: Required[str] - """Developer-provided ID created for each request in a Message Batch. - - Useful for matching results to requests, as results may be given out of request - order. - - Must be unique for each request within the Message Batch. - """ - - params: Required[MessageCreateParamsNonStreaming] - """Messages API creation parameters for the individual request. - - See the [Messages API reference](https://docs.claude.com/en/api/messages) for - full documentation on available parameters. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/batch_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/batch_list_params.py deleted file mode 100644 index 3f406251..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/batch_list_params.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam - -__all__ = ["BatchListParams"] - - -class BatchListParams(TypedDict, total=False): - after_id: str - """ID of the object to use as a cursor for pagination. - - When provided, returns the page of results immediately after this object. - """ - - before_id: str - """ID of the object to use as a cursor for pagination. - - When provided, returns the page of results immediately before this object. - """ - - limit: int - """Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_deleted_message_batch.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_deleted_message_batch.py deleted file mode 100644 index f7dd1d52..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_deleted_message_batch.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaDeletedMessageBatch"] - - -class BetaDeletedMessageBatch(BaseModel): - id: str - """ID of the Message Batch.""" - - type: Literal["message_batch_deleted"] - """Deleted object type. - - For Message Batches, this is always `"message_batch_deleted"`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch.py deleted file mode 100644 index 1ea92c3a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch.py +++ /dev/null @@ -1,77 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel -from .beta_message_batch_request_counts import BetaMessageBatchRequestCounts - -__all__ = ["BetaMessageBatch"] - - -class BetaMessageBatch(BaseModel): - id: str - """Unique object identifier. - - The format and length of IDs may change over time. - """ - - archived_at: Optional[datetime] = None - """ - RFC 3339 datetime string representing the time at which the Message Batch was - archived and its results became unavailable. - """ - - cancel_initiated_at: Optional[datetime] = None - """ - RFC 3339 datetime string representing the time at which cancellation was - initiated for the Message Batch. Specified only if cancellation was initiated. - """ - - created_at: datetime - """ - RFC 3339 datetime string representing the time at which the Message Batch was - created. - """ - - ended_at: Optional[datetime] = None - """ - RFC 3339 datetime string representing the time at which processing for the - Message Batch ended. Specified only once processing ends. - - Processing ends when every request in a Message Batch has either succeeded, - errored, canceled, or expired. - """ - - expires_at: datetime - """ - RFC 3339 datetime string representing the time at which the Message Batch will - expire and end processing, which is 24 hours after creation. - """ - - processing_status: Literal["in_progress", "canceling", "ended"] - """Processing status of the Message Batch.""" - - request_counts: BetaMessageBatchRequestCounts - """Tallies requests within the Message Batch, categorized by their status. - - Requests start as `processing` and move to one of the other statuses only once - processing of the entire batch ends. The sum of all values always matches the - total number of requests in the batch. - """ - - results_url: Optional[str] = None - """URL to a `.jsonl` file containing the results of the Message Batch requests. - - Specified only once processing ends. - - Results in the file are not guaranteed to be in the same order as requests. Use - the `custom_id` field to match results to requests. - """ - - type: Literal["message_batch"] - """Object type. - - For Message Batches, this is always `"message_batch"`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_canceled_result.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_canceled_result.py deleted file mode 100644 index e5dae348..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_canceled_result.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaMessageBatchCanceledResult"] - - -class BetaMessageBatchCanceledResult(BaseModel): - type: Literal["canceled"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_errored_result.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_errored_result.py deleted file mode 100644 index 44ea9027..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_errored_result.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel -from ...beta_error_response import BetaErrorResponse - -__all__ = ["BetaMessageBatchErroredResult"] - - -class BetaMessageBatchErroredResult(BaseModel): - error: BetaErrorResponse - - type: Literal["errored"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_expired_result.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_expired_result.py deleted file mode 100644 index 0dbfde41..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_expired_result.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaMessageBatchExpiredResult"] - - -class BetaMessageBatchExpiredResult(BaseModel): - type: Literal["expired"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_individual_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_individual_response.py deleted file mode 100644 index b13e92bc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_individual_response.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ...._models import BaseModel -from .beta_message_batch_result import BetaMessageBatchResult - -__all__ = ["BetaMessageBatchIndividualResponse"] - - -class BetaMessageBatchIndividualResponse(BaseModel): - """ - This is a single line in the response `.jsonl` file and does not represent the response as a whole. - """ - - custom_id: str - """Developer-provided ID created for each request in a Message Batch. - - Useful for matching results to requests, as results may be given out of request - order. - - Must be unique for each request within the Message Batch. - """ - - result: BetaMessageBatchResult - """Processing result for this request. - - Contains a Message output if processing was successful, an error response if - processing failed, or the reason why processing was not attempted, such as - cancellation or expiration. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_request_counts.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_request_counts.py deleted file mode 100644 index 8b5750f6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_request_counts.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ...._models import BaseModel - -__all__ = ["BetaMessageBatchRequestCounts"] - - -class BetaMessageBatchRequestCounts(BaseModel): - canceled: int - """Number of requests in the Message Batch that have been canceled. - - This is zero until processing of the entire Message Batch has ended. - """ - - errored: int - """Number of requests in the Message Batch that encountered an error. - - This is zero until processing of the entire Message Batch has ended. - """ - - expired: int - """Number of requests in the Message Batch that have expired. - - This is zero until processing of the entire Message Batch has ended. - """ - - processing: int - """Number of requests in the Message Batch that are processing.""" - - succeeded: int - """Number of requests in the Message Batch that have completed successfully. - - This is zero until processing of the entire Message Batch has ended. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_result.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_result.py deleted file mode 100644 index 78ca7317..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_result.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ...._utils import PropertyInfo -from .beta_message_batch_errored_result import BetaMessageBatchErroredResult -from .beta_message_batch_expired_result import BetaMessageBatchExpiredResult -from .beta_message_batch_canceled_result import BetaMessageBatchCanceledResult -from .beta_message_batch_succeeded_result import BetaMessageBatchSucceededResult - -__all__ = ["BetaMessageBatchResult"] - -BetaMessageBatchResult: TypeAlias = Annotated[ - Union[ - BetaMessageBatchSucceededResult, - BetaMessageBatchErroredResult, - BetaMessageBatchCanceledResult, - BetaMessageBatchExpiredResult, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_succeeded_result.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_succeeded_result.py deleted file mode 100644 index 94389d60..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/messages/beta_message_batch_succeeded_result.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel -from ..beta_message import BetaMessage - -__all__ = ["BetaMessageBatchSucceededResult"] - - -class BetaMessageBatchSucceededResult(BaseModel): - message: BetaMessage - - type: Literal["succeeded"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/model_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/model_list_params.py deleted file mode 100644 index f353e077..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/model_list_params.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["ModelListParams"] - - -class ModelListParams(TypedDict, total=False): - after_id: str - """ID of the object to use as a cursor for pagination. - - When provided, returns the page of results immediately after this object. - """ - - before_id: str - """ID of the object to use as a cursor for pagination. - - When provided, returns the page of results immediately before this object. - """ - - limit: int - """Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/parsed_beta_message.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/parsed_beta_message.py deleted file mode 100644 index 0fca18f8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/parsed_beta_message.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, List, Union, Generic, Optional -from typing_extensions import TypeVar, Annotated, TypeAlias - -from ..._utils import PropertyInfo -from .beta_message import BetaMessage -from .beta_text_block import BetaTextBlock -from .beta_thinking_block import BetaThinkingBlock -from .beta_tool_use_block import BetaToolUseBlock -from .beta_compaction_block import BetaCompactionBlock -from .beta_mcp_tool_use_block import BetaMCPToolUseBlock -from .beta_mcp_tool_result_block import BetaMCPToolResultBlock -from .beta_server_tool_use_block import BetaServerToolUseBlock -from .beta_container_upload_block import BetaContainerUploadBlock -from .beta_redacted_thinking_block import BetaRedactedThinkingBlock -from .beta_web_search_tool_result_block import BetaWebSearchToolResultBlock -from .beta_code_execution_tool_result_block import BetaCodeExecutionToolResultBlock -from .beta_bash_code_execution_tool_result_block import BetaBashCodeExecutionToolResultBlock -from .beta_text_editor_code_execution_tool_result_block import BetaTextEditorCodeExecutionToolResultBlock - -ResponseFormatT = TypeVar("ResponseFormatT", default=None) - - -__all__ = [ - "ParsedBetaTextBlock", - "ParsedBetaContentBlock", - "ParsedBetaMessage", -] - - -class ParsedBetaTextBlock(BetaTextBlock, Generic[ResponseFormatT]): - parsed_output: Optional[ResponseFormatT] = None - - __api_exclude__ = {"parsed_output"} - - -# Note that generic unions are not valid for pydantic at runtime -ParsedBetaContentBlock: TypeAlias = Annotated[ - Union[ - ParsedBetaTextBlock[ResponseFormatT], - BetaThinkingBlock, - BetaRedactedThinkingBlock, - BetaToolUseBlock, - BetaServerToolUseBlock, - BetaWebSearchToolResultBlock, - BetaCodeExecutionToolResultBlock, - BetaBashCodeExecutionToolResultBlock, - BetaTextEditorCodeExecutionToolResultBlock, - BetaMCPToolUseBlock, - BetaMCPToolResultBlock, - BetaContainerUploadBlock, - BetaCompactionBlock, - ], - PropertyInfo(discriminator="type"), -] - - -class ParsedBetaMessage(BetaMessage, Generic[ResponseFormatT]): - if TYPE_CHECKING: - content: List[ParsedBetaContentBlock[ResponseFormatT]] # type: ignore[assignment] - else: - content: List[ParsedBetaContentBlock] - - @property - def parsed_output(self) -> Optional[ResponseFormatT]: - for content in self.content: - if content.type == "text" and content.parsed_output: - return content.parsed_output - return None diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/session_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/session_create_params.py deleted file mode 100644 index 4156a5b3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/session_create_params.py +++ /dev/null @@ -1,55 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Union, Iterable, Optional -from typing_extensions import Required, Annotated, TypeAlias, TypedDict - -from ..._types import SequenceNotStr -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_agent_params import BetaManagedAgentsAgentParams -from .beta_managed_agents_file_resource_params import BetaManagedAgentsFileResourceParams -from .beta_managed_agents_memory_store_resource_param import BetaManagedAgentsMemoryStoreResourceParam -from .beta_managed_agents_github_repository_resource_params import BetaManagedAgentsGitHubRepositoryResourceParams - -__all__ = ["SessionCreateParams", "Agent", "Resource"] - - -class SessionCreateParams(TypedDict, total=False): - agent: Required[Agent] - """Agent identifier. - - Accepts the `agent` ID string, which pins the latest version for the session, or - an `agent` object with both id and version specified. - """ - - environment_id: Required[str] - """ID of the `environment` defining the container configuration for this session.""" - - metadata: Dict[str, str] - """Arbitrary key-value metadata attached to the session. - - Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. - """ - - resources: Iterable[Resource] - """Resources (e.g. repositories, files) to mount into the session's container.""" - - title: Optional[str] - """Human-readable session title.""" - - vault_ids: SequenceNotStr[str] - """Vault IDs for stored credentials the agent can use during the session.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" - - -Agent: TypeAlias = Union[str, BetaManagedAgentsAgentParams] - -Resource: TypeAlias = Union[ - BetaManagedAgentsGitHubRepositoryResourceParams, - BetaManagedAgentsFileResourceParams, - BetaManagedAgentsMemoryStoreResourceParam, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/session_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/session_list_params.py deleted file mode 100644 index 7a665ecb..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/session_list_params.py +++ /dev/null @@ -1,50 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Union -from datetime import datetime -from typing_extensions import Literal, Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["SessionListParams"] - - -class SessionListParams(TypedDict, total=False): - agent_id: str - """Filter sessions created with this agent ID.""" - - agent_version: int - """Filter by agent version. Only applies when agent_id is also set.""" - - created_at_gt: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gt]", format="iso8601")] - """Return sessions created after this time (exclusive).""" - - created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] - """Return sessions created at or after this time (inclusive).""" - - created_at_lt: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lt]", format="iso8601")] - """Return sessions created before this time (exclusive).""" - - created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] - """Return sessions created at or before this time (inclusive).""" - - include_archived: bool - """When true, includes archived sessions. Default: false (exclude archived).""" - - limit: int - """Maximum number of results to return.""" - - order: Literal["asc", "desc"] - """Sort direction for results, ordered by created_at. - - Defaults to desc (newest first). - """ - - page: str - """Opaque pagination cursor from a previous response's next_page.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/session_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/session_update_params.py deleted file mode 100644 index 9dd23fca..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/session_update_params.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from typing_extensions import Annotated, TypedDict - -from ..._types import SequenceNotStr -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["SessionUpdateParams"] - - -class SessionUpdateParams(TypedDict, total=False): - metadata: Optional[Dict[str, Optional[str]]] - """Metadata patch. - - Set a key to a string to upsert it, or to null to delete it. Omit the field to - preserve. - """ - - title: Optional[str] - """Human-readable session title.""" - - vault_ids: SequenceNotStr[str] - """Vault IDs (`vlt_*`) to attach to the session. - - Not yet supported; requests setting this field are rejected. Reserved for future - use. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/__init__.py deleted file mode 100644 index 2fe6257d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/__init__.py +++ /dev/null @@ -1,182 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .event_list_params import EventListParams as EventListParams -from .event_send_params import EventSendParams as EventSendParams -from .resource_add_params import ResourceAddParams as ResourceAddParams -from .resource_list_params import ResourceListParams as ResourceListParams -from .resource_update_params import ResourceUpdateParams as ResourceUpdateParams -from .resource_update_response import ResourceUpdateResponse as ResourceUpdateResponse -from .resource_retrieve_response import ResourceRetrieveResponse as ResourceRetrieveResponse -from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock as BetaManagedAgentsTextBlock -from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock as BetaManagedAgentsImageBlock -from .beta_managed_agents_event_params import BetaManagedAgentsEventParams as BetaManagedAgentsEventParams -from .beta_managed_agents_billing_error import BetaManagedAgentsBillingError as BetaManagedAgentsBillingError -from .beta_managed_agents_file_resource import BetaManagedAgentsFileResource as BetaManagedAgentsFileResource -from .beta_managed_agents_session_event import BetaManagedAgentsSessionEvent as BetaManagedAgentsSessionEvent -from .beta_managed_agents_unknown_error import BetaManagedAgentsUnknownError as BetaManagedAgentsUnknownError -from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock as BetaManagedAgentsDocumentBlock -from .beta_managed_agents_session_end_turn import BetaManagedAgentsSessionEndTurn as BetaManagedAgentsSessionEndTurn -from .beta_managed_agents_session_resource import BetaManagedAgentsSessionResource as BetaManagedAgentsSessionResource -from .beta_managed_agents_span_model_usage import BetaManagedAgentsSpanModelUsage as BetaManagedAgentsSpanModelUsage -from .beta_managed_agents_text_block_param import BetaManagedAgentsTextBlockParam as BetaManagedAgentsTextBlockParam -from .beta_managed_agents_url_image_source import BetaManagedAgentsURLImageSource as BetaManagedAgentsURLImageSource -from .beta_managed_agents_file_image_source import BetaManagedAgentsFileImageSource as BetaManagedAgentsFileImageSource -from .beta_managed_agents_image_block_param import BetaManagedAgentsImageBlockParam as BetaManagedAgentsImageBlockParam -from .beta_managed_agents_user_message_event import ( - BetaManagedAgentsUserMessageEvent as BetaManagedAgentsUserMessageEvent, -) -from .beta_managed_agents_agent_message_event import ( - BetaManagedAgentsAgentMessageEvent as BetaManagedAgentsAgentMessageEvent, -) -from .beta_managed_agents_base64_image_source import ( - BetaManagedAgentsBase64ImageSource as BetaManagedAgentsBase64ImageSource, -) -from .beta_managed_agents_send_session_events import ( - BetaManagedAgentsSendSessionEvents as BetaManagedAgentsSendSessionEvents, -) -from .beta_managed_agents_session_error_event import ( - BetaManagedAgentsSessionErrorEvent as BetaManagedAgentsSessionErrorEvent, -) -from .beta_managed_agents_url_document_source import ( - BetaManagedAgentsURLDocumentSource as BetaManagedAgentsURLDocumentSource, -) -from .beta_managed_agents_agent_thinking_event import ( - BetaManagedAgentsAgentThinkingEvent as BetaManagedAgentsAgentThinkingEvent, -) -from .beta_managed_agents_agent_tool_use_event import ( - BetaManagedAgentsAgentToolUseEvent as BetaManagedAgentsAgentToolUseEvent, -) -from .beta_managed_agents_document_block_param import ( - BetaManagedAgentsDocumentBlockParam as BetaManagedAgentsDocumentBlockParam, -) -from .beta_managed_agents_file_document_source import ( - BetaManagedAgentsFileDocumentSource as BetaManagedAgentsFileDocumentSource, -) -from .beta_managed_agents_user_interrupt_event import ( - BetaManagedAgentsUserInterruptEvent as BetaManagedAgentsUserInterruptEvent, -) -from .beta_managed_agents_memory_store_resource import ( - BetaManagedAgentsMemoryStoreResource as BetaManagedAgentsMemoryStoreResource, -) -from .beta_managed_agents_retry_status_retrying import ( - BetaManagedAgentsRetryStatusRetrying as BetaManagedAgentsRetryStatusRetrying, -) -from .beta_managed_agents_retry_status_terminal import ( - BetaManagedAgentsRetryStatusTerminal as BetaManagedAgentsRetryStatusTerminal, -) -from .beta_managed_agents_session_deleted_event import ( - BetaManagedAgentsSessionDeletedEvent as BetaManagedAgentsSessionDeletedEvent, -) -from .beta_managed_agents_stream_session_events import ( - BetaManagedAgentsStreamSessionEvents as BetaManagedAgentsStreamSessionEvents, -) -from .beta_managed_agents_base64_document_source import ( - BetaManagedAgentsBase64DocumentSource as BetaManagedAgentsBase64DocumentSource, -) -from .beta_managed_agents_model_overloaded_error import ( - BetaManagedAgentsModelOverloadedError as BetaManagedAgentsModelOverloadedError, -) -from .beta_managed_agents_retry_status_exhausted import ( - BetaManagedAgentsRetryStatusExhausted as BetaManagedAgentsRetryStatusExhausted, -) -from .beta_managed_agents_url_image_source_param import ( - BetaManagedAgentsURLImageSourceParam as BetaManagedAgentsURLImageSourceParam, -) -from .beta_managed_agents_agent_tool_result_event import ( - BetaManagedAgentsAgentToolResultEvent as BetaManagedAgentsAgentToolResultEvent, -) -from .beta_managed_agents_delete_session_resource import ( - BetaManagedAgentsDeleteSessionResource as BetaManagedAgentsDeleteSessionResource, -) -from .beta_managed_agents_file_image_source_param import ( - BetaManagedAgentsFileImageSourceParam as BetaManagedAgentsFileImageSourceParam, -) -from .beta_managed_agents_session_requires_action import ( - BetaManagedAgentsSessionRequiresAction as BetaManagedAgentsSessionRequiresAction, -) -from .beta_managed_agents_agent_mcp_tool_use_event import ( - BetaManagedAgentsAgentMCPToolUseEvent as BetaManagedAgentsAgentMCPToolUseEvent, -) -from .beta_managed_agents_model_rate_limited_error import ( - BetaManagedAgentsModelRateLimitedError as BetaManagedAgentsModelRateLimitedError, -) -from .beta_managed_agents_base64_image_source_param import ( - BetaManagedAgentsBase64ImageSourceParam as BetaManagedAgentsBase64ImageSourceParam, -) -from .beta_managed_agents_session_retries_exhausted import ( - BetaManagedAgentsSessionRetriesExhausted as BetaManagedAgentsSessionRetriesExhausted, -) -from .beta_managed_agents_session_status_idle_event import ( - BetaManagedAgentsSessionStatusIdleEvent as BetaManagedAgentsSessionStatusIdleEvent, -) -from .beta_managed_agents_url_document_source_param import ( - BetaManagedAgentsURLDocumentSourceParam as BetaManagedAgentsURLDocumentSourceParam, -) -from .beta_managed_agents_user_message_event_params import ( - BetaManagedAgentsUserMessageEventParams as BetaManagedAgentsUserMessageEventParams, -) -from .beta_managed_agents_file_document_source_param import ( - BetaManagedAgentsFileDocumentSourceParam as BetaManagedAgentsFileDocumentSourceParam, -) -from .beta_managed_agents_github_repository_resource import ( - BetaManagedAgentsGitHubRepositoryResource as BetaManagedAgentsGitHubRepositoryResource, -) -from .beta_managed_agents_model_request_failed_error import ( - BetaManagedAgentsModelRequestFailedError as BetaManagedAgentsModelRequestFailedError, -) -from .beta_managed_agents_plain_text_document_source import ( - BetaManagedAgentsPlainTextDocumentSource as BetaManagedAgentsPlainTextDocumentSource, -) -from .beta_managed_agents_agent_custom_tool_use_event import ( - BetaManagedAgentsAgentCustomToolUseEvent as BetaManagedAgentsAgentCustomToolUseEvent, -) -from .beta_managed_agents_agent_mcp_tool_result_event import ( - BetaManagedAgentsAgentMCPToolResultEvent as BetaManagedAgentsAgentMCPToolResultEvent, -) -from .beta_managed_agents_mcp_connection_failed_error import ( - BetaManagedAgentsMCPConnectionFailedError as BetaManagedAgentsMCPConnectionFailedError, -) -from .beta_managed_agents_user_interrupt_event_params import ( - BetaManagedAgentsUserInterruptEventParams as BetaManagedAgentsUserInterruptEventParams, -) -from .beta_managed_agents_base64_document_source_param import ( - BetaManagedAgentsBase64DocumentSourceParam as BetaManagedAgentsBase64DocumentSourceParam, -) -from .beta_managed_agents_session_status_running_event import ( - BetaManagedAgentsSessionStatusRunningEvent as BetaManagedAgentsSessionStatusRunningEvent, -) -from .beta_managed_agents_span_model_request_end_event import ( - BetaManagedAgentsSpanModelRequestEndEvent as BetaManagedAgentsSpanModelRequestEndEvent, -) -from .beta_managed_agents_user_tool_confirmation_event import ( - BetaManagedAgentsUserToolConfirmationEvent as BetaManagedAgentsUserToolConfirmationEvent, -) -from .beta_managed_agents_user_custom_tool_result_event import ( - BetaManagedAgentsUserCustomToolResultEvent as BetaManagedAgentsUserCustomToolResultEvent, -) -from .beta_managed_agents_span_model_request_start_event import ( - BetaManagedAgentsSpanModelRequestStartEvent as BetaManagedAgentsSpanModelRequestStartEvent, -) -from .beta_managed_agents_mcp_authentication_failed_error import ( - BetaManagedAgentsMCPAuthenticationFailedError as BetaManagedAgentsMCPAuthenticationFailedError, -) -from .beta_managed_agents_session_status_terminated_event import ( - BetaManagedAgentsSessionStatusTerminatedEvent as BetaManagedAgentsSessionStatusTerminatedEvent, -) -from .beta_managed_agents_plain_text_document_source_param import ( - BetaManagedAgentsPlainTextDocumentSourceParam as BetaManagedAgentsPlainTextDocumentSourceParam, -) -from .beta_managed_agents_session_status_rescheduled_event import ( - BetaManagedAgentsSessionStatusRescheduledEvent as BetaManagedAgentsSessionStatusRescheduledEvent, -) -from .beta_managed_agents_user_tool_confirmation_event_params import ( - BetaManagedAgentsUserToolConfirmationEventParams as BetaManagedAgentsUserToolConfirmationEventParams, -) -from .beta_managed_agents_agent_thread_context_compacted_event import ( - BetaManagedAgentsAgentThreadContextCompactedEvent as BetaManagedAgentsAgentThreadContextCompactedEvent, -) -from .beta_managed_agents_user_custom_tool_result_event_params import ( - BetaManagedAgentsUserCustomToolResultEventParams as BetaManagedAgentsUserCustomToolResultEventParams, -) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_custom_tool_use_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_custom_tool_use_event.py deleted file mode 100644 index fd8458fe..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_custom_tool_use_event.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsAgentCustomToolUseEvent"] - - -class BetaManagedAgentsAgentCustomToolUseEvent(BaseModel): - """Event emitted when the agent calls a custom tool. - - The session goes idle until the client sends a `user.custom_tool_result` event with the result. - """ - - id: str - """Unique identifier for this event.""" - - input: Dict[str, object] - """Input parameters for the tool call.""" - - name: str - """Name of the custom tool being called.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["agent.custom_tool_use"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_mcp_tool_result_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_mcp_tool_result_event.py deleted file mode 100644 index 3ce0277e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_mcp_tool_result_event.py +++ /dev/null @@ -1,39 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union, Optional -from datetime import datetime -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock -from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock -from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock - -__all__ = ["BetaManagedAgentsAgentMCPToolResultEvent", "Content"] - -Content: TypeAlias = Annotated[ - Union[BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsAgentMCPToolResultEvent(BaseModel): - """Event representing the result of an MCP tool execution.""" - - id: str - """Unique identifier for this event.""" - - mcp_tool_use_id: str - """The id of the `agent.mcp_tool_use` event this result corresponds to.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["agent.mcp_tool_result"] - - content: Optional[List[Content]] = None - """The result content returned by the tool.""" - - is_error: Optional[bool] = None - """Whether the tool execution resulted in an error.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_mcp_tool_use_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_mcp_tool_use_event.py deleted file mode 100644 index f98797cf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_mcp_tool_use_event.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsAgentMCPToolUseEvent"] - - -class BetaManagedAgentsAgentMCPToolUseEvent(BaseModel): - """Event emitted when the agent invokes a tool provided by an MCP server.""" - - id: str - """Unique identifier for this event.""" - - input: Dict[str, object] - """Input parameters for the tool call.""" - - mcp_server_name: str - """Name of the MCP server providing the tool.""" - - name: str - """Name of the MCP tool being used.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["agent.mcp_tool_use"] - - evaluated_permission: Optional[Literal["allow", "ask", "deny"]] = None - """AgentEvaluatedPermission enum""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_message_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_message_event.py deleted file mode 100644 index 3db547b4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_message_event.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel -from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock - -__all__ = ["BetaManagedAgentsAgentMessageEvent"] - - -class BetaManagedAgentsAgentMessageEvent(BaseModel): - """An agent response event in the session conversation.""" - - id: str - """Unique identifier for this event.""" - - content: List[BetaManagedAgentsTextBlock] - """Array of text blocks comprising the agent response.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["agent.message"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_thinking_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_thinking_event.py deleted file mode 100644 index 4da73264..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_thinking_event.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsAgentThinkingEvent"] - - -class BetaManagedAgentsAgentThinkingEvent(BaseModel): - """Indicates the agent is making forward progress via extended thinking. - - A progress signal, not a content carrier. - """ - - id: str - """Unique identifier for this event.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["agent.thinking"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_thread_context_compacted_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_thread_context_compacted_event.py deleted file mode 100644 index 6db0444e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_thread_context_compacted_event.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsAgentThreadContextCompactedEvent"] - - -class BetaManagedAgentsAgentThreadContextCompactedEvent(BaseModel): - """Indicates that context compaction (summarization) occurred during the session.""" - - id: str - """Unique identifier for this event.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["agent.thread_context_compacted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_tool_result_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_tool_result_event.py deleted file mode 100644 index f61925d1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_tool_result_event.py +++ /dev/null @@ -1,39 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union, Optional -from datetime import datetime -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock -from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock -from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock - -__all__ = ["BetaManagedAgentsAgentToolResultEvent", "Content"] - -Content: TypeAlias = Annotated[ - Union[BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsAgentToolResultEvent(BaseModel): - """Event representing the result of an agent tool execution.""" - - id: str - """Unique identifier for this event.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - tool_use_id: str - """The id of the `agent.tool_use` event this result corresponds to.""" - - type: Literal["agent.tool_result"] - - content: Optional[List[Content]] = None - """The result content returned by the tool.""" - - is_error: Optional[bool] = None - """Whether the tool execution resulted in an error.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_tool_use_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_tool_use_event.py deleted file mode 100644 index 688bf2ae..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_agent_tool_use_event.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsAgentToolUseEvent"] - - -class BetaManagedAgentsAgentToolUseEvent(BaseModel): - """Event emitted when the agent invokes a built-in agent tool.""" - - id: str - """Unique identifier for this event.""" - - input: Dict[str, object] - """Input parameters for the tool call.""" - - name: str - """Name of the agent tool being used.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["agent.tool_use"] - - evaluated_permission: Optional[Literal["allow", "ask", "deny"]] = None - """AgentEvaluatedPermission enum""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_document_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_document_source.py deleted file mode 100644 index 45e488fc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_document_source.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsBase64DocumentSource"] - - -class BetaManagedAgentsBase64DocumentSource(BaseModel): - """Base64-encoded document data.""" - - data: str - """Base64-encoded document data.""" - - media_type: str - """MIME type of the document (e.g., "application/pdf").""" - - type: Literal["base64"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_document_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_document_source_param.py deleted file mode 100644 index 7526fc29..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_document_source_param.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsBase64DocumentSourceParam"] - - -class BetaManagedAgentsBase64DocumentSourceParam(TypedDict, total=False): - """Base64-encoded document data.""" - - data: Required[str] - """Base64-encoded document data.""" - - media_type: Required[str] - """MIME type of the document (e.g., "application/pdf").""" - - type: Required[Literal["base64"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_image_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_image_source.py deleted file mode 100644 index 92562752..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_image_source.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsBase64ImageSource"] - - -class BetaManagedAgentsBase64ImageSource(BaseModel): - """Base64-encoded image data.""" - - data: str - """Base64-encoded image data.""" - - media_type: str - """ - MIME type of the image (e.g., "image/png", "image/jpeg", "image/gif", - "image/webp"). - """ - - type: Literal["base64"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_image_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_image_source_param.py deleted file mode 100644 index 46a55510..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_base64_image_source_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsBase64ImageSourceParam"] - - -class BetaManagedAgentsBase64ImageSourceParam(TypedDict, total=False): - """Base64-encoded image data.""" - - data: Required[str] - """Base64-encoded image data.""" - - media_type: Required[str] - """ - MIME type of the image (e.g., "image/png", "image/jpeg", "image/gif", - "image/webp"). - """ - - type: Required[Literal["base64"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_billing_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_billing_error.py deleted file mode 100644 index 325c3aa6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_billing_error.py +++ /dev/null @@ -1,35 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying -from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal -from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted - -__all__ = ["BetaManagedAgentsBillingError", "RetryStatus"] - -RetryStatus: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsRetryStatusRetrying, - BetaManagedAgentsRetryStatusExhausted, - BetaManagedAgentsRetryStatusTerminal, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsBillingError(BaseModel): - """ - The caller's organization or workspace cannot make model requests — out of credits or spend limit reached. Retrying with the same credentials will not succeed; the caller must resolve the billing state. - """ - - message: str - """Human-readable error description.""" - - retry_status: RetryStatus - """What the client should do next in response to this error.""" - - type: Literal["billing_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_delete_session_resource.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_delete_session_resource.py deleted file mode 100644 index 54d6a590..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_delete_session_resource.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsDeleteSessionResource"] - - -class BetaManagedAgentsDeleteSessionResource(BaseModel): - """Confirmation of resource deletion.""" - - id: str - - type: Literal["session_resource_deleted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_document_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_document_block.py deleted file mode 100644 index 3e4ddb9e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_document_block.py +++ /dev/null @@ -1,40 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_url_document_source import BetaManagedAgentsURLDocumentSource -from .beta_managed_agents_file_document_source import BetaManagedAgentsFileDocumentSource -from .beta_managed_agents_base64_document_source import BetaManagedAgentsBase64DocumentSource -from .beta_managed_agents_plain_text_document_source import BetaManagedAgentsPlainTextDocumentSource - -__all__ = ["BetaManagedAgentsDocumentBlock", "Source"] - -Source: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsBase64DocumentSource, - BetaManagedAgentsPlainTextDocumentSource, - BetaManagedAgentsURLDocumentSource, - BetaManagedAgentsFileDocumentSource, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsDocumentBlock(BaseModel): - """ - Document content, either specified directly as base64 data, as text, or as a reference via a URL. - """ - - source: Source - """Union type for document source variants.""" - - type: Literal["document"] - - context: Optional[str] = None - """Additional context about the document for the model.""" - - title: Optional[str] = None - """The title of the document.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_document_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_document_block_param.py deleted file mode 100644 index 00d863f6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_document_block_param.py +++ /dev/null @@ -1,37 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_managed_agents_url_document_source_param import BetaManagedAgentsURLDocumentSourceParam -from .beta_managed_agents_file_document_source_param import BetaManagedAgentsFileDocumentSourceParam -from .beta_managed_agents_base64_document_source_param import BetaManagedAgentsBase64DocumentSourceParam -from .beta_managed_agents_plain_text_document_source_param import BetaManagedAgentsPlainTextDocumentSourceParam - -__all__ = ["BetaManagedAgentsDocumentBlockParam", "Source"] - -Source: TypeAlias = Union[ - BetaManagedAgentsBase64DocumentSourceParam, - BetaManagedAgentsPlainTextDocumentSourceParam, - BetaManagedAgentsURLDocumentSourceParam, - BetaManagedAgentsFileDocumentSourceParam, -] - - -class BetaManagedAgentsDocumentBlockParam(TypedDict, total=False): - """ - Document content, either specified directly as base64 data, as text, or as a reference via a URL. - """ - - source: Required[Source] - """Union type for document source variants.""" - - type: Required[Literal["document"]] - - context: Optional[str] - """Additional context about the document for the model.""" - - title: Optional[str] - """The title of the document.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_event_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_event_params.py deleted file mode 100644 index d93165d4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_event_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .beta_managed_agents_user_message_event_params import BetaManagedAgentsUserMessageEventParams -from .beta_managed_agents_user_interrupt_event_params import BetaManagedAgentsUserInterruptEventParams -from .beta_managed_agents_user_tool_confirmation_event_params import BetaManagedAgentsUserToolConfirmationEventParams -from .beta_managed_agents_user_custom_tool_result_event_params import BetaManagedAgentsUserCustomToolResultEventParams - -__all__ = ["BetaManagedAgentsEventParams"] - -BetaManagedAgentsEventParams: TypeAlias = Union[ - BetaManagedAgentsUserMessageEventParams, - BetaManagedAgentsUserInterruptEventParams, - BetaManagedAgentsUserToolConfirmationEventParams, - BetaManagedAgentsUserCustomToolResultEventParams, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_document_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_document_source.py deleted file mode 100644 index 5564f9fc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_document_source.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsFileDocumentSource"] - - -class BetaManagedAgentsFileDocumentSource(BaseModel): - """Document referenced by file ID.""" - - file_id: str - """ID of a previously uploaded file.""" - - type: Literal["file"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_document_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_document_source_param.py deleted file mode 100644 index 5ad8d427..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_document_source_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsFileDocumentSourceParam"] - - -class BetaManagedAgentsFileDocumentSourceParam(TypedDict, total=False): - """Document referenced by file ID.""" - - file_id: Required[str] - """ID of a previously uploaded file.""" - - type: Required[Literal["file"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_image_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_image_source.py deleted file mode 100644 index 8a25c250..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_image_source.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsFileImageSource"] - - -class BetaManagedAgentsFileImageSource(BaseModel): - """Image referenced by file ID.""" - - file_id: str - """ID of a previously uploaded file.""" - - type: Literal["file"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_image_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_image_source_param.py deleted file mode 100644 index 34eb9878..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_image_source_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsFileImageSourceParam"] - - -class BetaManagedAgentsFileImageSourceParam(TypedDict, total=False): - """Image referenced by file ID.""" - - file_id: Required[str] - """ID of a previously uploaded file.""" - - type: Required[Literal["file"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_resource.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_resource.py deleted file mode 100644 index dfbb95ec..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_file_resource.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsFileResource"] - - -class BetaManagedAgentsFileResource(BaseModel): - id: str - - created_at: datetime - """A timestamp in RFC 3339 format""" - - file_id: str - - mount_path: str - - type: Literal["file"] - - updated_at: datetime - """A timestamp in RFC 3339 format""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_github_repository_resource.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_github_repository_resource.py deleted file mode 100644 index b5ef525a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_github_repository_resource.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional -from datetime import datetime -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from ..beta_managed_agents_branch_checkout import BetaManagedAgentsBranchCheckout -from ..beta_managed_agents_commit_checkout import BetaManagedAgentsCommitCheckout - -__all__ = ["BetaManagedAgentsGitHubRepositoryResource", "Checkout"] - -Checkout: TypeAlias = Annotated[ - Union[BetaManagedAgentsBranchCheckout, BetaManagedAgentsCommitCheckout, None], PropertyInfo(discriminator="type") -] - - -class BetaManagedAgentsGitHubRepositoryResource(BaseModel): - id: str - - created_at: datetime - """A timestamp in RFC 3339 format""" - - mount_path: str - - type: Literal["github_repository"] - - updated_at: datetime - """A timestamp in RFC 3339 format""" - - url: str - - checkout: Optional[Checkout] = None diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_image_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_image_block.py deleted file mode 100644 index 8bccf87c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_image_block.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_url_image_source import BetaManagedAgentsURLImageSource -from .beta_managed_agents_file_image_source import BetaManagedAgentsFileImageSource -from .beta_managed_agents_base64_image_source import BetaManagedAgentsBase64ImageSource - -__all__ = ["BetaManagedAgentsImageBlock", "Source"] - -Source: TypeAlias = Annotated[ - Union[BetaManagedAgentsBase64ImageSource, BetaManagedAgentsURLImageSource, BetaManagedAgentsFileImageSource], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsImageBlock(BaseModel): - """Image content specified directly as base64 data or as a reference via a URL.""" - - source: Source - """Union type for image source variants.""" - - type: Literal["image"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_image_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_image_block_param.py deleted file mode 100644 index 7f4d012a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_image_block_param.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_managed_agents_url_image_source_param import BetaManagedAgentsURLImageSourceParam -from .beta_managed_agents_file_image_source_param import BetaManagedAgentsFileImageSourceParam -from .beta_managed_agents_base64_image_source_param import BetaManagedAgentsBase64ImageSourceParam - -__all__ = ["BetaManagedAgentsImageBlockParam", "Source"] - -Source: TypeAlias = Union[ - BetaManagedAgentsBase64ImageSourceParam, BetaManagedAgentsURLImageSourceParam, BetaManagedAgentsFileImageSourceParam -] - - -class BetaManagedAgentsImageBlockParam(TypedDict, total=False): - """Image content specified directly as base64 data or as a reference via a URL.""" - - source: Required[Source] - """Union type for image source variants.""" - - type: Required[Literal["image"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_mcp_authentication_failed_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_mcp_authentication_failed_error.py deleted file mode 100644 index bedd174b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_mcp_authentication_failed_error.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying -from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal -from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted - -__all__ = ["BetaManagedAgentsMCPAuthenticationFailedError", "RetryStatus"] - -RetryStatus: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsRetryStatusRetrying, - BetaManagedAgentsRetryStatusExhausted, - BetaManagedAgentsRetryStatusTerminal, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsMCPAuthenticationFailedError(BaseModel): - """Authentication to an MCP server failed.""" - - mcp_server_name: str - """Name of the MCP server that failed authentication.""" - - message: str - """Human-readable error description.""" - - retry_status: RetryStatus - """What the client should do next in response to this error.""" - - type: Literal["mcp_authentication_failed_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_mcp_connection_failed_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_mcp_connection_failed_error.py deleted file mode 100644 index 85c6a7a3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_mcp_connection_failed_error.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying -from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal -from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted - -__all__ = ["BetaManagedAgentsMCPConnectionFailedError", "RetryStatus"] - -RetryStatus: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsRetryStatusRetrying, - BetaManagedAgentsRetryStatusExhausted, - BetaManagedAgentsRetryStatusTerminal, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsMCPConnectionFailedError(BaseModel): - """Failed to connect to an MCP server.""" - - mcp_server_name: str - """Name of the MCP server that failed to connect.""" - - message: str - """Human-readable error description.""" - - retry_status: RetryStatus - """What the client should do next in response to this error.""" - - type: Literal["mcp_connection_failed_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_memory_store_resource.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_memory_store_resource.py deleted file mode 100644 index 8740d6e1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_memory_store_resource.py +++ /dev/null @@ -1,48 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsMemoryStoreResource"] - - -class BetaManagedAgentsMemoryStoreResource(BaseModel): - """A memory store attached to an agent session.""" - - memory_store_id: str - """The memory store ID (memstore\\__...). - - Must belong to the caller's organization and workspace. - """ - - type: Literal["memory_store"] - - access: Optional[Literal["read_write", "read_only"]] = None - """Access mode for an attached memory store.""" - - description: Optional[str] = None - """Description of the memory store, snapshotted at attach time. - - Rendered into the agent's system prompt. Empty string when the store has no - description. - """ - - instructions: Optional[str] = None - """Per-attachment guidance for the agent on how to use this store. - - Rendered into the memory section of the system prompt. Max 4096 chars. - """ - - mount_path: Optional[str] = None - """Filesystem path where the store is mounted in the session container, e.g. - - /mnt/memory/user-preferences. Derived from the store's name. Output-only. - """ - - name: Optional[str] = None - """Display name of the memory store, snapshotted at attach time. - - Later edits to the store's name do not propagate to this resource. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_model_overloaded_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_model_overloaded_error.py deleted file mode 100644 index fb2b5073..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_model_overloaded_error.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying -from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal -from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted - -__all__ = ["BetaManagedAgentsModelOverloadedError", "RetryStatus"] - -RetryStatus: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsRetryStatusRetrying, - BetaManagedAgentsRetryStatusExhausted, - BetaManagedAgentsRetryStatusTerminal, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsModelOverloadedError(BaseModel): - """The model is currently overloaded. - - Emitted after automatic retries are exhausted. - """ - - message: str - """Human-readable error description.""" - - retry_status: RetryStatus - """What the client should do next in response to this error.""" - - type: Literal["model_overloaded_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_model_rate_limited_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_model_rate_limited_error.py deleted file mode 100644 index 3beadfaf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_model_rate_limited_error.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying -from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal -from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted - -__all__ = ["BetaManagedAgentsModelRateLimitedError", "RetryStatus"] - -RetryStatus: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsRetryStatusRetrying, - BetaManagedAgentsRetryStatusExhausted, - BetaManagedAgentsRetryStatusTerminal, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsModelRateLimitedError(BaseModel): - """The model request was rate-limited.""" - - message: str - """Human-readable error description.""" - - retry_status: RetryStatus - """What the client should do next in response to this error.""" - - type: Literal["model_rate_limited_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_model_request_failed_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_model_request_failed_error.py deleted file mode 100644 index 835791e2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_model_request_failed_error.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying -from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal -from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted - -__all__ = ["BetaManagedAgentsModelRequestFailedError", "RetryStatus"] - -RetryStatus: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsRetryStatusRetrying, - BetaManagedAgentsRetryStatusExhausted, - BetaManagedAgentsRetryStatusTerminal, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsModelRequestFailedError(BaseModel): - """A model request failed for a reason other than overload or rate-limiting.""" - - message: str - """Human-readable error description.""" - - retry_status: RetryStatus - """What the client should do next in response to this error.""" - - type: Literal["model_request_failed_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_plain_text_document_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_plain_text_document_source.py deleted file mode 100644 index 38c882dc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_plain_text_document_source.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsPlainTextDocumentSource"] - - -class BetaManagedAgentsPlainTextDocumentSource(BaseModel): - """Plain text document content.""" - - data: str - """The plain text content.""" - - media_type: Literal["text/plain"] - """MIME type of the text content. Must be "text/plain".""" - - type: Literal["text"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_plain_text_document_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_plain_text_document_source_param.py deleted file mode 100644 index cb3953b0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_plain_text_document_source_param.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsPlainTextDocumentSourceParam"] - - -class BetaManagedAgentsPlainTextDocumentSourceParam(TypedDict, total=False): - """Plain text document content.""" - - data: Required[str] - """The plain text content.""" - - media_type: Required[Literal["text/plain"]] - """MIME type of the text content. Must be "text/plain".""" - - type: Required[Literal["text"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_retry_status_exhausted.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_retry_status_exhausted.py deleted file mode 100644 index dcafbbd2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_retry_status_exhausted.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsRetryStatusExhausted"] - - -class BetaManagedAgentsRetryStatusExhausted(BaseModel): - """This turn is dead; queued inputs are flushed and the session returns to idle. - - Client may send a new prompt. - """ - - type: Literal["exhausted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_retry_status_retrying.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_retry_status_retrying.py deleted file mode 100644 index 73cee49b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_retry_status_retrying.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsRetryStatusRetrying"] - - -class BetaManagedAgentsRetryStatusRetrying(BaseModel): - """The server is retrying automatically. - - Client should wait; the same error type may fire again as retrying, then once as exhausted when the retry budget runs out. - """ - - type: Literal["retrying"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_retry_status_terminal.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_retry_status_terminal.py deleted file mode 100644 index 73f53d69..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_retry_status_terminal.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsRetryStatusTerminal"] - - -class BetaManagedAgentsRetryStatusTerminal(BaseModel): - """ - The session encountered a terminal error and will transition to `terminated` state. - """ - - type: Literal["terminal"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_send_session_events.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_send_session_events.py deleted file mode 100644 index aca0f778..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_send_session_events.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union, Optional -from typing_extensions import Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_user_message_event import BetaManagedAgentsUserMessageEvent -from .beta_managed_agents_user_interrupt_event import BetaManagedAgentsUserInterruptEvent -from .beta_managed_agents_user_tool_confirmation_event import BetaManagedAgentsUserToolConfirmationEvent -from .beta_managed_agents_user_custom_tool_result_event import BetaManagedAgentsUserCustomToolResultEvent - -__all__ = ["BetaManagedAgentsSendSessionEvents", "Data"] - -Data: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsUserMessageEvent, - BetaManagedAgentsUserInterruptEvent, - BetaManagedAgentsUserToolConfirmationEvent, - BetaManagedAgentsUserCustomToolResultEvent, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsSendSessionEvents(BaseModel): - """Events that were successfully sent to the session.""" - - data: Optional[List[Data]] = None - """Sent events""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_deleted_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_deleted_event.py deleted file mode 100644 index 1a7b084b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_deleted_event.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsSessionDeletedEvent"] - - -class BetaManagedAgentsSessionDeletedEvent(BaseModel): - """Emitted when a session has been deleted. - - Terminates any active event stream — no further events will be emitted for this session. - """ - - id: str - """Unique identifier for this event.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["session.deleted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_end_turn.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_end_turn.py deleted file mode 100644 index 106d18e6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_end_turn.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsSessionEndTurn"] - - -class BetaManagedAgentsSessionEndTurn(BaseModel): - """The agent completed its turn naturally and is ready for the next user message.""" - - type: Literal["end_turn"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_error_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_error_event.py deleted file mode 100644 index 9fcc831b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_error_event.py +++ /dev/null @@ -1,49 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from datetime import datetime -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_billing_error import BetaManagedAgentsBillingError -from .beta_managed_agents_unknown_error import BetaManagedAgentsUnknownError -from .beta_managed_agents_model_overloaded_error import BetaManagedAgentsModelOverloadedError -from .beta_managed_agents_model_rate_limited_error import BetaManagedAgentsModelRateLimitedError -from .beta_managed_agents_model_request_failed_error import BetaManagedAgentsModelRequestFailedError -from .beta_managed_agents_mcp_connection_failed_error import BetaManagedAgentsMCPConnectionFailedError -from .beta_managed_agents_mcp_authentication_failed_error import BetaManagedAgentsMCPAuthenticationFailedError - -__all__ = ["BetaManagedAgentsSessionErrorEvent", "Error"] - -Error: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsUnknownError, - BetaManagedAgentsModelOverloadedError, - BetaManagedAgentsModelRateLimitedError, - BetaManagedAgentsModelRequestFailedError, - BetaManagedAgentsMCPConnectionFailedError, - BetaManagedAgentsMCPAuthenticationFailedError, - BetaManagedAgentsBillingError, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsSessionErrorEvent(BaseModel): - """An error event indicating a problem occurred during session execution.""" - - id: str - """Unique identifier for this event.""" - - error: Error - """An unknown or unexpected error occurred during session execution. - - A fallback variant; clients that don't recognize a new error code can match on - `retry_status` and `message` alone. - """ - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["session.error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_event.py deleted file mode 100644 index a0bd2ef5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_event.py +++ /dev/null @@ -1,54 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ...._utils import PropertyInfo -from .beta_managed_agents_user_message_event import BetaManagedAgentsUserMessageEvent -from .beta_managed_agents_agent_message_event import BetaManagedAgentsAgentMessageEvent -from .beta_managed_agents_session_error_event import BetaManagedAgentsSessionErrorEvent -from .beta_managed_agents_agent_thinking_event import BetaManagedAgentsAgentThinkingEvent -from .beta_managed_agents_agent_tool_use_event import BetaManagedAgentsAgentToolUseEvent -from .beta_managed_agents_user_interrupt_event import BetaManagedAgentsUserInterruptEvent -from .beta_managed_agents_session_deleted_event import BetaManagedAgentsSessionDeletedEvent -from .beta_managed_agents_agent_tool_result_event import BetaManagedAgentsAgentToolResultEvent -from .beta_managed_agents_agent_mcp_tool_use_event import BetaManagedAgentsAgentMCPToolUseEvent -from .beta_managed_agents_session_status_idle_event import BetaManagedAgentsSessionStatusIdleEvent -from .beta_managed_agents_agent_custom_tool_use_event import BetaManagedAgentsAgentCustomToolUseEvent -from .beta_managed_agents_agent_mcp_tool_result_event import BetaManagedAgentsAgentMCPToolResultEvent -from .beta_managed_agents_session_status_running_event import BetaManagedAgentsSessionStatusRunningEvent -from .beta_managed_agents_span_model_request_end_event import BetaManagedAgentsSpanModelRequestEndEvent -from .beta_managed_agents_user_tool_confirmation_event import BetaManagedAgentsUserToolConfirmationEvent -from .beta_managed_agents_user_custom_tool_result_event import BetaManagedAgentsUserCustomToolResultEvent -from .beta_managed_agents_span_model_request_start_event import BetaManagedAgentsSpanModelRequestStartEvent -from .beta_managed_agents_session_status_terminated_event import BetaManagedAgentsSessionStatusTerminatedEvent -from .beta_managed_agents_session_status_rescheduled_event import BetaManagedAgentsSessionStatusRescheduledEvent -from .beta_managed_agents_agent_thread_context_compacted_event import BetaManagedAgentsAgentThreadContextCompactedEvent - -__all__ = ["BetaManagedAgentsSessionEvent"] - -BetaManagedAgentsSessionEvent: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsUserMessageEvent, - BetaManagedAgentsUserInterruptEvent, - BetaManagedAgentsUserToolConfirmationEvent, - BetaManagedAgentsUserCustomToolResultEvent, - BetaManagedAgentsAgentCustomToolUseEvent, - BetaManagedAgentsAgentMessageEvent, - BetaManagedAgentsAgentThinkingEvent, - BetaManagedAgentsAgentMCPToolUseEvent, - BetaManagedAgentsAgentMCPToolResultEvent, - BetaManagedAgentsAgentToolUseEvent, - BetaManagedAgentsAgentToolResultEvent, - BetaManagedAgentsAgentThreadContextCompactedEvent, - BetaManagedAgentsSessionErrorEvent, - BetaManagedAgentsSessionStatusRescheduledEvent, - BetaManagedAgentsSessionStatusRunningEvent, - BetaManagedAgentsSessionStatusIdleEvent, - BetaManagedAgentsSessionStatusTerminatedEvent, - BetaManagedAgentsSpanModelRequestStartEvent, - BetaManagedAgentsSpanModelRequestEndEvent, - BetaManagedAgentsSessionDeletedEvent, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_requires_action.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_requires_action.py deleted file mode 100644 index 3c1d92f8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_requires_action.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsSessionRequiresAction"] - - -class BetaManagedAgentsSessionRequiresAction(BaseModel): - """ - The agent is idle waiting on one or more blocking user-input events (tool confirmation, custom tool result, etc.). Resolving all of them transitions the session back to running. - """ - - event_ids: List[str] - """The ids of events the agent is blocked on. - - Resolving fewer than all re-emits `session.status_idle` with the remainder. - """ - - type: Literal["requires_action"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_resource.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_resource.py deleted file mode 100644 index 47f26cfd..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_resource.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ...._utils import PropertyInfo -from .beta_managed_agents_file_resource import BetaManagedAgentsFileResource -from .beta_managed_agents_memory_store_resource import BetaManagedAgentsMemoryStoreResource -from .beta_managed_agents_github_repository_resource import BetaManagedAgentsGitHubRepositoryResource - -__all__ = ["BetaManagedAgentsSessionResource"] - -BetaManagedAgentsSessionResource: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsGitHubRepositoryResource, BetaManagedAgentsFileResource, BetaManagedAgentsMemoryStoreResource - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_retries_exhausted.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_retries_exhausted.py deleted file mode 100644 index bd64b488..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_retries_exhausted.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsSessionRetriesExhausted"] - - -class BetaManagedAgentsSessionRetriesExhausted(BaseModel): - """ - The turn ended because the retry budget was exhausted (`max_iterations` hit or an error escalated to `retry_status: 'exhausted'`). - """ - - type: Literal["retries_exhausted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_idle_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_idle_event.py deleted file mode 100644 index 7eddcaeb..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_idle_event.py +++ /dev/null @@ -1,37 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from datetime import datetime -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_session_end_turn import BetaManagedAgentsSessionEndTurn -from .beta_managed_agents_session_requires_action import BetaManagedAgentsSessionRequiresAction -from .beta_managed_agents_session_retries_exhausted import BetaManagedAgentsSessionRetriesExhausted - -__all__ = ["BetaManagedAgentsSessionStatusIdleEvent", "StopReason"] - -StopReason: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsSessionEndTurn, - BetaManagedAgentsSessionRequiresAction, - BetaManagedAgentsSessionRetriesExhausted, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsSessionStatusIdleEvent(BaseModel): - """Indicates the agent has paused and is awaiting user input.""" - - id: str - """Unique identifier for this event.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - stop_reason: StopReason - """The agent completed its turn naturally and is ready for the next user message.""" - - type: Literal["session.status_idle"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_rescheduled_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_rescheduled_event.py deleted file mode 100644 index 39e776c7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_rescheduled_event.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsSessionStatusRescheduledEvent"] - - -class BetaManagedAgentsSessionStatusRescheduledEvent(BaseModel): - """ - Indicates the session is recovering from an error state and is rescheduled for execution. - """ - - id: str - """Unique identifier for this event.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["session.status_rescheduled"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_running_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_running_event.py deleted file mode 100644 index 8eac536d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_running_event.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsSessionStatusRunningEvent"] - - -class BetaManagedAgentsSessionStatusRunningEvent(BaseModel): - """Indicates the session is actively running and the agent is working.""" - - id: str - """Unique identifier for this event.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["session.status_running"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_terminated_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_terminated_event.py deleted file mode 100644 index b630b903..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_session_status_terminated_event.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsSessionStatusTerminatedEvent"] - - -class BetaManagedAgentsSessionStatusTerminatedEvent(BaseModel): - """Indicates the session has terminated, either due to an error or completion.""" - - id: str - """Unique identifier for this event.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["session.status_terminated"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_span_model_request_end_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_span_model_request_end_event.py deleted file mode 100644 index 8e45a435..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_span_model_request_end_event.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ...._compat import PYDANTIC_V1, ConfigDict -from ...._models import BaseModel -from .beta_managed_agents_span_model_usage import BetaManagedAgentsSpanModelUsage - -__all__ = ["BetaManagedAgentsSpanModelRequestEndEvent"] - - -class BetaManagedAgentsSpanModelRequestEndEvent(BaseModel): - """Emitted when a model request completes.""" - - id: str - """Unique identifier for this event.""" - - is_error: Optional[bool] = None - """Whether the model request resulted in an error.""" - - model_request_start_id: str - """The id of the corresponding `span.model_request_start` event.""" - - model_usage: BetaManagedAgentsSpanModelUsage - """Token usage for a single model request.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["span.model_request_end"] - - if not PYDANTIC_V1: - # allow fields with a `model_` prefix - model_config = ConfigDict(protected_namespaces=tuple()) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_span_model_request_start_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_span_model_request_start_event.py deleted file mode 100644 index a10e8d24..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_span_model_request_start_event.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsSpanModelRequestStartEvent"] - - -class BetaManagedAgentsSpanModelRequestStartEvent(BaseModel): - """Emitted when a model request is initiated by the agent.""" - - id: str - """Unique identifier for this event.""" - - processed_at: datetime - """A timestamp in RFC 3339 format""" - - type: Literal["span.model_request_start"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_span_model_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_span_model_usage.py deleted file mode 100644 index e2c2e9df..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_span_model_usage.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsSpanModelUsage"] - - -class BetaManagedAgentsSpanModelUsage(BaseModel): - """Token usage for a single model request.""" - - cache_creation_input_tokens: int - """Tokens used to create prompt cache in this request.""" - - cache_read_input_tokens: int - """Tokens read from prompt cache in this request.""" - - input_tokens: int - """Input tokens consumed by this request.""" - - output_tokens: int - """Output tokens generated by this request.""" - - speed: Optional[Literal["standard", "fast"]] = None - """Inference speed mode. - - `fast` provides significantly faster output token generation at premium pricing. - Not all models support `fast`; invalid combinations are rejected at create time. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_stream_session_events.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_stream_session_events.py deleted file mode 100644 index fc77e8ac..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_stream_session_events.py +++ /dev/null @@ -1,54 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ...._utils import PropertyInfo -from .beta_managed_agents_user_message_event import BetaManagedAgentsUserMessageEvent -from .beta_managed_agents_agent_message_event import BetaManagedAgentsAgentMessageEvent -from .beta_managed_agents_session_error_event import BetaManagedAgentsSessionErrorEvent -from .beta_managed_agents_agent_thinking_event import BetaManagedAgentsAgentThinkingEvent -from .beta_managed_agents_agent_tool_use_event import BetaManagedAgentsAgentToolUseEvent -from .beta_managed_agents_user_interrupt_event import BetaManagedAgentsUserInterruptEvent -from .beta_managed_agents_session_deleted_event import BetaManagedAgentsSessionDeletedEvent -from .beta_managed_agents_agent_tool_result_event import BetaManagedAgentsAgentToolResultEvent -from .beta_managed_agents_agent_mcp_tool_use_event import BetaManagedAgentsAgentMCPToolUseEvent -from .beta_managed_agents_session_status_idle_event import BetaManagedAgentsSessionStatusIdleEvent -from .beta_managed_agents_agent_custom_tool_use_event import BetaManagedAgentsAgentCustomToolUseEvent -from .beta_managed_agents_agent_mcp_tool_result_event import BetaManagedAgentsAgentMCPToolResultEvent -from .beta_managed_agents_session_status_running_event import BetaManagedAgentsSessionStatusRunningEvent -from .beta_managed_agents_span_model_request_end_event import BetaManagedAgentsSpanModelRequestEndEvent -from .beta_managed_agents_user_tool_confirmation_event import BetaManagedAgentsUserToolConfirmationEvent -from .beta_managed_agents_user_custom_tool_result_event import BetaManagedAgentsUserCustomToolResultEvent -from .beta_managed_agents_span_model_request_start_event import BetaManagedAgentsSpanModelRequestStartEvent -from .beta_managed_agents_session_status_terminated_event import BetaManagedAgentsSessionStatusTerminatedEvent -from .beta_managed_agents_session_status_rescheduled_event import BetaManagedAgentsSessionStatusRescheduledEvent -from .beta_managed_agents_agent_thread_context_compacted_event import BetaManagedAgentsAgentThreadContextCompactedEvent - -__all__ = ["BetaManagedAgentsStreamSessionEvents"] - -BetaManagedAgentsStreamSessionEvents: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsUserMessageEvent, - BetaManagedAgentsUserInterruptEvent, - BetaManagedAgentsUserToolConfirmationEvent, - BetaManagedAgentsUserCustomToolResultEvent, - BetaManagedAgentsAgentCustomToolUseEvent, - BetaManagedAgentsAgentMessageEvent, - BetaManagedAgentsAgentThinkingEvent, - BetaManagedAgentsAgentMCPToolUseEvent, - BetaManagedAgentsAgentMCPToolResultEvent, - BetaManagedAgentsAgentToolUseEvent, - BetaManagedAgentsAgentToolResultEvent, - BetaManagedAgentsAgentThreadContextCompactedEvent, - BetaManagedAgentsSessionErrorEvent, - BetaManagedAgentsSessionStatusRescheduledEvent, - BetaManagedAgentsSessionStatusRunningEvent, - BetaManagedAgentsSessionStatusIdleEvent, - BetaManagedAgentsSessionStatusTerminatedEvent, - BetaManagedAgentsSpanModelRequestStartEvent, - BetaManagedAgentsSpanModelRequestEndEvent, - BetaManagedAgentsSessionDeletedEvent, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_text_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_text_block.py deleted file mode 100644 index 82730cf0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_text_block.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsTextBlock"] - - -class BetaManagedAgentsTextBlock(BaseModel): - """Regular text content.""" - - text: str - """The text content.""" - - type: Literal["text"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_text_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_text_block_param.py deleted file mode 100644 index b878f0e2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_text_block_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsTextBlockParam"] - - -class BetaManagedAgentsTextBlockParam(TypedDict, total=False): - """Regular text content.""" - - text: Required[str] - """The text content.""" - - type: Required[Literal["text"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_unknown_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_unknown_error.py deleted file mode 100644 index 34edab8e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_unknown_error.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying -from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal -from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted - -__all__ = ["BetaManagedAgentsUnknownError", "RetryStatus"] - -RetryStatus: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsRetryStatusRetrying, - BetaManagedAgentsRetryStatusExhausted, - BetaManagedAgentsRetryStatusTerminal, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsUnknownError(BaseModel): - """An unknown or unexpected error occurred during session execution. - - A fallback variant; clients that don't recognize a new error code can match on `retry_status` and `message` alone. - """ - - message: str - """Human-readable error description.""" - - retry_status: RetryStatus - """What the client should do next in response to this error.""" - - type: Literal["unknown_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_document_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_document_source.py deleted file mode 100644 index 043197b6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_document_source.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsURLDocumentSource"] - - -class BetaManagedAgentsURLDocumentSource(BaseModel): - """Document referenced by URL.""" - - type: Literal["url"] - - url: str - """URL of the document to fetch.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_document_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_document_source_param.py deleted file mode 100644 index ea76827a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_document_source_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsURLDocumentSourceParam"] - - -class BetaManagedAgentsURLDocumentSourceParam(TypedDict, total=False): - """Document referenced by URL.""" - - type: Required[Literal["url"]] - - url: Required[str] - """URL of the document to fetch.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_image_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_image_source.py deleted file mode 100644 index 56c54545..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_image_source.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsURLImageSource"] - - -class BetaManagedAgentsURLImageSource(BaseModel): - """Image referenced by URL.""" - - type: Literal["url"] - - url: str - """URL of the image to fetch.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_image_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_image_source_param.py deleted file mode 100644 index d3a4b570..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_url_image_source_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsURLImageSourceParam"] - - -class BetaManagedAgentsURLImageSourceParam(TypedDict, total=False): - """Image referenced by URL.""" - - type: Required[Literal["url"]] - - url: Required[str] - """URL of the image to fetch.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_custom_tool_result_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_custom_tool_result_event.py deleted file mode 100644 index f5988e36..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_custom_tool_result_event.py +++ /dev/null @@ -1,44 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union, Optional -from datetime import datetime -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock -from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock -from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock - -__all__ = ["BetaManagedAgentsUserCustomToolResultEvent", "Content"] - -Content: TypeAlias = Annotated[ - Union[BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsUserCustomToolResultEvent(BaseModel): - """Event sent by the client providing the result of a custom tool execution.""" - - id: str - """Unique identifier for this event.""" - - custom_tool_use_id: str - """ - The id of the `agent.custom_tool_use` event this result corresponds to, which - can be found in the last `session.status_idle` - [event's](https://platform.claude.com/docs/en/api/beta/sessions/events/list#beta_managed_agents_session_requires_action.event_ids) - `stop_reason.event_ids` field. - """ - - type: Literal["user.custom_tool_result"] - - content: Optional[List[Content]] = None - """The result content returned by the tool.""" - - is_error: Optional[bool] = None - """Whether the tool execution resulted in an error.""" - - processed_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_custom_tool_result_event_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_custom_tool_result_event_params.py deleted file mode 100644 index 05c79e6f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_custom_tool_result_event_params.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_managed_agents_text_block_param import BetaManagedAgentsTextBlockParam -from .beta_managed_agents_image_block_param import BetaManagedAgentsImageBlockParam -from .beta_managed_agents_document_block_param import BetaManagedAgentsDocumentBlockParam - -__all__ = ["BetaManagedAgentsUserCustomToolResultEventParams", "Content"] - -Content: TypeAlias = Union[ - BetaManagedAgentsTextBlockParam, BetaManagedAgentsImageBlockParam, BetaManagedAgentsDocumentBlockParam -] - - -class BetaManagedAgentsUserCustomToolResultEventParams(TypedDict, total=False): - """Parameters for providing the result of a custom tool execution.""" - - custom_tool_use_id: Required[str] - """ - The id of the `agent.custom_tool_use` event this result corresponds to, which - can be found in the last `session.status_idle` - [event's](https://platform.claude.com/docs/en/api/beta/sessions/events/list#beta_managed_agents_session_requires_action.event_ids) - `stop_reason.event_ids` field. - """ - - type: Required[Literal["user.custom_tool_result"]] - - content: Iterable[Content] - """The result content returned by the tool.""" - - is_error: Optional[bool] - """Whether the tool execution resulted in an error.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_interrupt_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_interrupt_event.py deleted file mode 100644 index 0c267eba..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_interrupt_event.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsUserInterruptEvent"] - - -class BetaManagedAgentsUserInterruptEvent(BaseModel): - """An interrupt event that pauses agent execution and returns control to the user.""" - - id: str - """Unique identifier for this event.""" - - type: Literal["user.interrupt"] - - processed_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_interrupt_event_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_interrupt_event_params.py deleted file mode 100644 index cb1aac28..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_interrupt_event_params.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsUserInterruptEventParams"] - - -class BetaManagedAgentsUserInterruptEventParams(TypedDict, total=False): - """Parameters for sending an interrupt to pause the agent.""" - - type: Required[Literal["user.interrupt"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_message_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_message_event.py deleted file mode 100644 index aa97139f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_message_event.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union, Optional -from datetime import datetime -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock -from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock -from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock - -__all__ = ["BetaManagedAgentsUserMessageEvent", "Content"] - -Content: TypeAlias = Annotated[ - Union[BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsUserMessageEvent(BaseModel): - """A user message event in the session conversation.""" - - id: str - """Unique identifier for this event.""" - - content: List[Content] - """Array of content blocks comprising the user message.""" - - type: Literal["user.message"] - - processed_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_message_event_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_message_event_params.py deleted file mode 100644 index bb87cc78..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_message_event_params.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .beta_managed_agents_text_block_param import BetaManagedAgentsTextBlockParam -from .beta_managed_agents_image_block_param import BetaManagedAgentsImageBlockParam -from .beta_managed_agents_document_block_param import BetaManagedAgentsDocumentBlockParam - -__all__ = ["BetaManagedAgentsUserMessageEventParams", "Content"] - -Content: TypeAlias = Union[ - BetaManagedAgentsTextBlockParam, BetaManagedAgentsImageBlockParam, BetaManagedAgentsDocumentBlockParam -] - - -class BetaManagedAgentsUserMessageEventParams(TypedDict, total=False): - """Parameters for sending a user message to the session.""" - - content: Required[Iterable[Content]] - """Array of content blocks for the user message.""" - - type: Required[Literal["user.message"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_tool_confirmation_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_tool_confirmation_event.py deleted file mode 100644 index 7815a04b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_tool_confirmation_event.py +++ /dev/null @@ -1,38 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsUserToolConfirmationEvent"] - - -class BetaManagedAgentsUserToolConfirmationEvent(BaseModel): - """A tool confirmation event that approves or denies a pending tool execution.""" - - id: str - """Unique identifier for this event.""" - - result: Literal["allow", "deny"] - """UserToolConfirmationResult enum""" - - tool_use_id: str - """ - The id of the `agent.tool_use` or `agent.mcp_tool_use` event this result - corresponds to, which can be found in the last `session.status_idle` - [event's](https://platform.claude.com/docs/en/api/beta/sessions/events/list#beta_managed_agents_session_requires_action.event_ids) - `stop_reason.event_ids` field. - """ - - type: Literal["user.tool_confirmation"] - - deny_message: Optional[str] = None - """Optional message providing context for a 'deny' decision. - - Only allowed when result is 'deny'. - """ - - processed_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_tool_confirmation_event_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_tool_confirmation_event_params.py deleted file mode 100644 index ea01c9a4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/beta_managed_agents_user_tool_confirmation_event_params.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsUserToolConfirmationEventParams"] - - -class BetaManagedAgentsUserToolConfirmationEventParams(TypedDict, total=False): - """Parameters for confirming or denying a tool execution request.""" - - result: Required[Literal["allow", "deny"]] - """UserToolConfirmationResult enum""" - - tool_use_id: Required[str] - """ - The id of the `agent.tool_use` or `agent.mcp_tool_use` event this result - corresponds to, which can be found in the last `session.status_idle` - [event's](https://platform.claude.com/docs/en/api/beta/sessions/events/list#beta_managed_agents_session_requires_action.event_ids) - `stop_reason.event_ids` field. - """ - - type: Required[Literal["user.tool_confirmation"]] - - deny_message: Optional[str] - """Optional message providing context for a 'deny' decision. - - Only allowed when result is 'deny'. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/event_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/event_list_params.py deleted file mode 100644 index 7601f578..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/event_list_params.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Literal, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam - -__all__ = ["EventListParams"] - - -class EventListParams(TypedDict, total=False): - limit: int - """Query parameter for limit""" - - order: Literal["asc", "desc"] - """Sort direction for results, ordered by created_at. - - Defaults to asc (chronological). - """ - - page: str - """Opaque pagination cursor from a previous response's next_page.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/event_send_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/event_send_params.py deleted file mode 100644 index 13924367..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/event_send_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Iterable -from typing_extensions import Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_event_params import BetaManagedAgentsEventParams - -__all__ = ["EventSendParams"] - - -class EventSendParams(TypedDict, total=False): - events: Required[Iterable[BetaManagedAgentsEventParams]] - """Events to send to the `session`.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_add_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_add_params.py deleted file mode 100644 index 2b0b7356..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_add_params.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam - -__all__ = ["ResourceAddParams"] - - -class ResourceAddParams(TypedDict, total=False): - file_id: Required[str] - """ID of a previously uploaded file.""" - - type: Required[Literal["file"]] - - mount_path: Optional[str] - """Mount path in the container. Defaults to `/mnt/session/uploads/`.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_list_params.py deleted file mode 100644 index 936719b6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_list_params.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam - -__all__ = ["ResourceListParams"] - - -class ResourceListParams(TypedDict, total=False): - limit: int - """Maximum number of resources to return per page (max 1000). - - If omitted, returns all resources. - """ - - page: str - """Opaque cursor from a previous response's next_page field.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_retrieve_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_retrieve_response.py deleted file mode 100644 index a956ba17..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_retrieve_response.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ...._utils import PropertyInfo -from .beta_managed_agents_file_resource import BetaManagedAgentsFileResource -from .beta_managed_agents_memory_store_resource import BetaManagedAgentsMemoryStoreResource -from .beta_managed_agents_github_repository_resource import BetaManagedAgentsGitHubRepositoryResource - -__all__ = ["ResourceRetrieveResponse"] - -ResourceRetrieveResponse: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsGitHubRepositoryResource, BetaManagedAgentsFileResource, BetaManagedAgentsMemoryStoreResource - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_update_params.py deleted file mode 100644 index 923bfbf4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_update_params.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam - -__all__ = ["ResourceUpdateParams"] - - -class ResourceUpdateParams(TypedDict, total=False): - session_id: Required[str] - - authorization_token: Required[str] - """New authorization token for the resource. - - Currently only `github_repository` resources support token rotation. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_update_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_update_response.py deleted file mode 100644 index d20f0a34..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/sessions/resource_update_response.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ...._utils import PropertyInfo -from .beta_managed_agents_file_resource import BetaManagedAgentsFileResource -from .beta_managed_agents_memory_store_resource import BetaManagedAgentsMemoryStoreResource -from .beta_managed_agents_github_repository_resource import BetaManagedAgentsGitHubRepositoryResource - -__all__ = ["ResourceUpdateResponse"] - -ResourceUpdateResponse: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsGitHubRepositoryResource, BetaManagedAgentsFileResource, BetaManagedAgentsMemoryStoreResource - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_create_params.py deleted file mode 100644 index d0341f9b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_create_params.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Annotated, TypedDict - -from ..._types import FileTypes, SequenceNotStr -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["SkillCreateParams"] - - -class SkillCreateParams(TypedDict, total=False): - display_title: Optional[str] - """Display title for the skill. - - This is a human-readable label that is not included in the prompt sent to the - model. - """ - - files: Optional[SequenceNotStr[FileTypes]] - """Files to upload for the skill. - - All files must be in the same top-level directory and must include a SKILL.md - file at the root of that directory. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_create_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_create_response.py deleted file mode 100644 index 7b7afe1e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_create_response.py +++ /dev/null @@ -1,49 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["SkillCreateResponse"] - - -class SkillCreateResponse(BaseModel): - id: str - """Unique identifier for the skill. - - The format and length of IDs may change over time. - """ - - created_at: str - """ISO 8601 timestamp of when the skill was created.""" - - display_title: Optional[str] = None - """Display title for the skill. - - This is a human-readable label that is not included in the prompt sent to the - model. - """ - - latest_version: Optional[str] = None - """The latest version identifier for the skill. - - This represents the most recent version of the skill that has been created. - """ - - source: str - """Source of the skill. - - This may be one of the following values: - - - `"custom"`: the skill was created by a user - - `"anthropic"`: the skill was created by Anthropic - """ - - type: str - """Object type. - - For Skills, this is always `"skill"`. - """ - - updated_at: str - """ISO 8601 timestamp of when the skill was last updated.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_delete_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_delete_response.py deleted file mode 100644 index 68c96fe4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_delete_response.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel - -__all__ = ["SkillDeleteResponse"] - - -class SkillDeleteResponse(BaseModel): - id: str - """Unique identifier for the skill. - - The format and length of IDs may change over time. - """ - - type: str - """Deleted object type. - - For Skills, this is always `"skill_deleted"`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_list_params.py deleted file mode 100644 index df2faed3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_list_params.py +++ /dev/null @@ -1,38 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["SkillListParams"] - - -class SkillListParams(TypedDict, total=False): - limit: int - """Number of results to return per page. - - Maximum value is 100. Defaults to 20. - """ - - page: Optional[str] - """Pagination token for fetching a specific page of results. - - Pass the value from a previous response's `next_page` field to get the next page - of results. - """ - - source: Optional[str] - """Filter skills by source. - - If provided, only skills from the specified source will be returned: - - - `"custom"`: only return user-created skills - - `"anthropic"`: only return Anthropic-created skills - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_list_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_list_response.py deleted file mode 100644 index d4cceab4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_list_response.py +++ /dev/null @@ -1,49 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["SkillListResponse"] - - -class SkillListResponse(BaseModel): - id: str - """Unique identifier for the skill. - - The format and length of IDs may change over time. - """ - - created_at: str - """ISO 8601 timestamp of when the skill was created.""" - - display_title: Optional[str] = None - """Display title for the skill. - - This is a human-readable label that is not included in the prompt sent to the - model. - """ - - latest_version: Optional[str] = None - """The latest version identifier for the skill. - - This represents the most recent version of the skill that has been created. - """ - - source: str - """Source of the skill. - - This may be one of the following values: - - - `"custom"`: the skill was created by a user - - `"anthropic"`: the skill was created by Anthropic - """ - - type: str - """Object type. - - For Skills, this is always `"skill"`. - """ - - updated_at: str - """ISO 8601 timestamp of when the skill was last updated.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_retrieve_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_retrieve_response.py deleted file mode 100644 index b5793017..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skill_retrieve_response.py +++ /dev/null @@ -1,49 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["SkillRetrieveResponse"] - - -class SkillRetrieveResponse(BaseModel): - id: str - """Unique identifier for the skill. - - The format and length of IDs may change over time. - """ - - created_at: str - """ISO 8601 timestamp of when the skill was created.""" - - display_title: Optional[str] = None - """Display title for the skill. - - This is a human-readable label that is not included in the prompt sent to the - model. - """ - - latest_version: Optional[str] = None - """The latest version identifier for the skill. - - This represents the most recent version of the skill that has been created. - """ - - source: str - """Source of the skill. - - This may be one of the following values: - - - `"custom"`: the skill was created by a user - - `"anthropic"`: the skill was created by Anthropic - """ - - type: str - """Object type. - - For Skills, this is always `"skill"`. - """ - - updated_at: str - """ISO 8601 timestamp of when the skill was last updated.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/__init__.py deleted file mode 100644 index fe49c800..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .version_list_params import VersionListParams as VersionListParams -from .version_create_params import VersionCreateParams as VersionCreateParams -from .version_list_response import VersionListResponse as VersionListResponse -from .version_create_response import VersionCreateResponse as VersionCreateResponse -from .version_delete_response import VersionDeleteResponse as VersionDeleteResponse -from .version_retrieve_response import VersionRetrieveResponse as VersionRetrieveResponse diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_create_params.py deleted file mode 100644 index 66bb7680..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_create_params.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Annotated, TypedDict - -from ...._types import FileTypes, SequenceNotStr -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam - -__all__ = ["VersionCreateParams"] - - -class VersionCreateParams(TypedDict, total=False): - files: Optional[SequenceNotStr[FileTypes]] - """Files to upload for the skill. - - All files must be in the same top-level directory and must include a SKILL.md - file at the root of that directory. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_create_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_create_response.py deleted file mode 100644 index dffbe25b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_create_response.py +++ /dev/null @@ -1,49 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ...._models import BaseModel - -__all__ = ["VersionCreateResponse"] - - -class VersionCreateResponse(BaseModel): - id: str - """Unique identifier for the skill version. - - The format and length of IDs may change over time. - """ - - created_at: str - """ISO 8601 timestamp of when the skill version was created.""" - - description: str - """Description of the skill version. - - This is extracted from the SKILL.md file in the skill upload. - """ - - directory: str - """Directory name of the skill version. - - This is the top-level directory name that was extracted from the uploaded files. - """ - - name: str - """Human-readable name of the skill version. - - This is extracted from the SKILL.md file in the skill upload. - """ - - skill_id: str - """Identifier for the skill that this version belongs to.""" - - type: str - """Object type. - - For Skill Versions, this is always `"skill_version"`. - """ - - version: str - """Version identifier for the skill. - - Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_delete_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_delete_response.py deleted file mode 100644 index ff4b9362..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_delete_response.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ...._models import BaseModel - -__all__ = ["VersionDeleteResponse"] - - -class VersionDeleteResponse(BaseModel): - id: str - """Version identifier for the skill. - - Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). - """ - - type: str - """Deleted object type. - - For Skill Versions, this is always `"skill_version_deleted"`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_list_params.py deleted file mode 100644 index a3c77b60..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_list_params.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam - -__all__ = ["VersionListParams"] - - -class VersionListParams(TypedDict, total=False): - limit: Optional[int] - """Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - """ - - page: Optional[str] - """Optionally set to the `next_page` token from the previous response.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_list_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_list_response.py deleted file mode 100644 index 2d70956e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_list_response.py +++ /dev/null @@ -1,49 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ...._models import BaseModel - -__all__ = ["VersionListResponse"] - - -class VersionListResponse(BaseModel): - id: str - """Unique identifier for the skill version. - - The format and length of IDs may change over time. - """ - - created_at: str - """ISO 8601 timestamp of when the skill version was created.""" - - description: str - """Description of the skill version. - - This is extracted from the SKILL.md file in the skill upload. - """ - - directory: str - """Directory name of the skill version. - - This is the top-level directory name that was extracted from the uploaded files. - """ - - name: str - """Human-readable name of the skill version. - - This is extracted from the SKILL.md file in the skill upload. - """ - - skill_id: str - """Identifier for the skill that this version belongs to.""" - - type: str - """Object type. - - For Skill Versions, this is always `"skill_version"`. - """ - - version: str - """Version identifier for the skill. - - Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_retrieve_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_retrieve_response.py deleted file mode 100644 index 3c0b10b7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/skills/version_retrieve_response.py +++ /dev/null @@ -1,49 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ...._models import BaseModel - -__all__ = ["VersionRetrieveResponse"] - - -class VersionRetrieveResponse(BaseModel): - id: str - """Unique identifier for the skill version. - - The format and length of IDs may change over time. - """ - - created_at: str - """ISO 8601 timestamp of when the skill version was created.""" - - description: str - """Description of the skill version. - - This is extracted from the SKILL.md file in the skill upload. - """ - - directory: str - """Directory name of the skill version. - - This is the top-level directory name that was extracted from the uploaded files. - """ - - name: str - """Human-readable name of the skill version. - - This is extracted from the SKILL.md file in the skill upload. - """ - - skill_id: str - """Identifier for the skill that this version belongs to.""" - - type: str - """Object type. - - For Skill Versions, this is always `"skill_version"`. - """ - - version: str - """Version identifier for the skill. - - Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/user_profile_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/user_profile_create_params.py deleted file mode 100644 index b84f622f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/user_profile_create_params.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["UserProfileCreateParams"] - - -class UserProfileCreateParams(TypedDict, total=False): - external_id: Optional[str] - """Platform's own identifier for this user. - - Not enforced unique. Maximum 255 characters. - """ - - metadata: Dict[str, str] - """Free-form key-value data to attach to this user profile. - - Maximum 16 keys, with keys up to 64 characters and values up to 512 characters. - Values must be non-empty strings. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/user_profile_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/user_profile_list_params.py deleted file mode 100644 index 0cf2a3df..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/user_profile_list_params.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Literal, Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["UserProfileListParams"] - - -class UserProfileListParams(TypedDict, total=False): - limit: int - """Query parameter for limit""" - - order: Literal["asc", "desc"] - """Query parameter for order""" - - page: str - """Query parameter for page""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/user_profile_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/user_profile_update_params.py deleted file mode 100644 index 51f42865..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/user_profile_update_params.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["UserProfileUpdateParams"] - - -class UserProfileUpdateParams(TypedDict, total=False): - external_id: Optional[str] - """If present, replaces the stored external_id. - - Omit to leave unchanged. Maximum 255 characters. - """ - - metadata: Dict[str, str] - """Key-value pairs to merge into the stored metadata. - - Keys provided overwrite existing values. To remove a key, set its value to an - empty string. Keys not provided are left unchanged. Maximum 16 keys, with keys - up to 64 characters and values up to 512 characters. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vault_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vault_create_params.py deleted file mode 100644 index 80b4dba2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vault_create_params.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["VaultCreateParams"] - - -class VaultCreateParams(TypedDict, total=False): - display_name: Required[str] - """Human-readable name for the vault. 1-255 characters.""" - - metadata: Dict[str, str] - """Arbitrary key-value metadata to attach to the vault. - - Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vault_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vault_list_params.py deleted file mode 100644 index 234a82a8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vault_list_params.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["VaultListParams"] - - -class VaultListParams(TypedDict, total=False): - include_archived: bool - """Whether to include archived vaults in the results.""" - - limit: int - """Maximum number of vaults to return per page. Defaults to 20, maximum 100.""" - - page: str - """Opaque pagination token from a previous `list_vaults` response.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vault_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vault_update_params.py deleted file mode 100644 index 9ec0782d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vault_update_params.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Optional -from typing_extensions import Annotated, TypedDict - -from ..._utils import PropertyInfo -from ..anthropic_beta_param import AnthropicBetaParam - -__all__ = ["VaultUpdateParams"] - - -class VaultUpdateParams(TypedDict, total=False): - display_name: Optional[str] - """Updated human-readable name for the vault. 1-255 characters.""" - - metadata: Optional[Dict[str, Optional[str]]] - """Metadata patch. - - Set a key to a string to upsert it, or to null to delete it. Omitted keys are - preserved. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/__init__.py deleted file mode 100644 index 23ee943f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .credential_list_params import CredentialListParams as CredentialListParams -from .credential_create_params import CredentialCreateParams as CredentialCreateParams -from .credential_update_params import CredentialUpdateParams as CredentialUpdateParams -from .beta_managed_agents_credential import BetaManagedAgentsCredential as BetaManagedAgentsCredential -from .beta_managed_agents_deleted_credential import ( - BetaManagedAgentsDeletedCredential as BetaManagedAgentsDeletedCredential, -) -from .beta_managed_agents_mcp_oauth_auth_response import ( - BetaManagedAgentsMCPOAuthAuthResponse as BetaManagedAgentsMCPOAuthAuthResponse, -) -from .beta_managed_agents_mcp_oauth_create_params import ( - BetaManagedAgentsMCPOAuthCreateParams as BetaManagedAgentsMCPOAuthCreateParams, -) -from .beta_managed_agents_mcp_oauth_update_params import ( - BetaManagedAgentsMCPOAuthUpdateParams as BetaManagedAgentsMCPOAuthUpdateParams, -) -from .beta_managed_agents_mcp_oauth_refresh_params import ( - BetaManagedAgentsMCPOAuthRefreshParams as BetaManagedAgentsMCPOAuthRefreshParams, -) -from .beta_managed_agents_mcp_oauth_refresh_response import ( - BetaManagedAgentsMCPOAuthRefreshResponse as BetaManagedAgentsMCPOAuthRefreshResponse, -) -from .beta_managed_agents_static_bearer_auth_response import ( - BetaManagedAgentsStaticBearerAuthResponse as BetaManagedAgentsStaticBearerAuthResponse, -) -from .beta_managed_agents_static_bearer_create_params import ( - BetaManagedAgentsStaticBearerCreateParams as BetaManagedAgentsStaticBearerCreateParams, -) -from .beta_managed_agents_static_bearer_update_params import ( - BetaManagedAgentsStaticBearerUpdateParams as BetaManagedAgentsStaticBearerUpdateParams, -) -from .beta_managed_agents_token_endpoint_auth_none_param import ( - BetaManagedAgentsTokenEndpointAuthNoneParam as BetaManagedAgentsTokenEndpointAuthNoneParam, -) -from .beta_managed_agents_token_endpoint_auth_post_param import ( - BetaManagedAgentsTokenEndpointAuthPostParam as BetaManagedAgentsTokenEndpointAuthPostParam, -) -from .beta_managed_agents_mcp_oauth_refresh_update_params import ( - BetaManagedAgentsMCPOAuthRefreshUpdateParams as BetaManagedAgentsMCPOAuthRefreshUpdateParams, -) -from .beta_managed_agents_token_endpoint_auth_basic_param import ( - BetaManagedAgentsTokenEndpointAuthBasicParam as BetaManagedAgentsTokenEndpointAuthBasicParam, -) -from .beta_managed_agents_token_endpoint_auth_none_response import ( - BetaManagedAgentsTokenEndpointAuthNoneResponse as BetaManagedAgentsTokenEndpointAuthNoneResponse, -) -from .beta_managed_agents_token_endpoint_auth_post_response import ( - BetaManagedAgentsTokenEndpointAuthPostResponse as BetaManagedAgentsTokenEndpointAuthPostResponse, -) -from .beta_managed_agents_token_endpoint_auth_basic_response import ( - BetaManagedAgentsTokenEndpointAuthBasicResponse as BetaManagedAgentsTokenEndpointAuthBasicResponse, -) -from .beta_managed_agents_token_endpoint_auth_post_update_param import ( - BetaManagedAgentsTokenEndpointAuthPostUpdateParam as BetaManagedAgentsTokenEndpointAuthPostUpdateParam, -) -from .beta_managed_agents_token_endpoint_auth_basic_update_param import ( - BetaManagedAgentsTokenEndpointAuthBasicUpdateParam as BetaManagedAgentsTokenEndpointAuthBasicUpdateParam, -) diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_credential.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_credential.py deleted file mode 100644 index 9a575f63..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_credential.py +++ /dev/null @@ -1,50 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Union, Optional -from datetime import datetime -from typing_extensions import Literal, Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_mcp_oauth_auth_response import BetaManagedAgentsMCPOAuthAuthResponse -from .beta_managed_agents_static_bearer_auth_response import BetaManagedAgentsStaticBearerAuthResponse - -__all__ = ["BetaManagedAgentsCredential", "Auth"] - -Auth: TypeAlias = Annotated[ - Union[BetaManagedAgentsMCPOAuthAuthResponse, BetaManagedAgentsStaticBearerAuthResponse], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsCredential(BaseModel): - """A credential stored in a vault. - - Sensitive fields are never returned in responses. - """ - - id: str - """Unique identifier for the credential.""" - - archived_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" - - auth: Auth - """Authentication details for a credential.""" - - created_at: datetime - """A timestamp in RFC 3339 format""" - - metadata: Dict[str, str] - """Arbitrary key-value metadata attached to the credential.""" - - type: Literal["vault_credential"] - - updated_at: datetime - """A timestamp in RFC 3339 format""" - - vault_id: str - """Identifier of the vault this credential belongs to.""" - - display_name: Optional[str] = None - """Human-readable name for the credential.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_deleted_credential.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_deleted_credential.py deleted file mode 100644 index d080cf60..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_deleted_credential.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsDeletedCredential"] - - -class BetaManagedAgentsDeletedCredential(BaseModel): - """Confirmation of a deleted credential.""" - - id: str - """Unique identifier of the deleted credential.""" - - type: Literal["vault_credential_deleted"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_auth_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_auth_response.py deleted file mode 100644 index 874f71e5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_auth_response.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ...._models import BaseModel -from .beta_managed_agents_mcp_oauth_refresh_response import BetaManagedAgentsMCPOAuthRefreshResponse - -__all__ = ["BetaManagedAgentsMCPOAuthAuthResponse"] - - -class BetaManagedAgentsMCPOAuthAuthResponse(BaseModel): - """OAuth credential details for an MCP server.""" - - mcp_server_url: str - """URL of the MCP server this credential authenticates against.""" - - type: Literal["mcp_oauth"] - - expires_at: Optional[datetime] = None - """A timestamp in RFC 3339 format""" - - refresh: Optional[BetaManagedAgentsMCPOAuthRefreshResponse] = None - """OAuth refresh token configuration returned in credential responses.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_create_params.py deleted file mode 100644 index 6595d288..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_create_params.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from datetime import datetime -from typing_extensions import Literal, Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from .beta_managed_agents_mcp_oauth_refresh_params import BetaManagedAgentsMCPOAuthRefreshParams - -__all__ = ["BetaManagedAgentsMCPOAuthCreateParams"] - - -class BetaManagedAgentsMCPOAuthCreateParams(TypedDict, total=False): - """Parameters for creating an MCP OAuth credential.""" - - access_token: Required[str] - """OAuth access token.""" - - mcp_server_url: Required[str] - """URL of the MCP server this credential authenticates against.""" - - type: Required[Literal["mcp_oauth"]] - - expires_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] - """A timestamp in RFC 3339 format""" - - refresh: Optional[BetaManagedAgentsMCPOAuthRefreshParams] - """OAuth refresh token parameters for creating a credential with refresh support.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_params.py deleted file mode 100644 index c4580a8b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_params.py +++ /dev/null @@ -1,40 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Required, TypeAlias, TypedDict - -from .beta_managed_agents_token_endpoint_auth_none_param import BetaManagedAgentsTokenEndpointAuthNoneParam -from .beta_managed_agents_token_endpoint_auth_post_param import BetaManagedAgentsTokenEndpointAuthPostParam -from .beta_managed_agents_token_endpoint_auth_basic_param import BetaManagedAgentsTokenEndpointAuthBasicParam - -__all__ = ["BetaManagedAgentsMCPOAuthRefreshParams", "TokenEndpointAuth"] - -TokenEndpointAuth: TypeAlias = Union[ - BetaManagedAgentsTokenEndpointAuthNoneParam, - BetaManagedAgentsTokenEndpointAuthBasicParam, - BetaManagedAgentsTokenEndpointAuthPostParam, -] - - -class BetaManagedAgentsMCPOAuthRefreshParams(TypedDict, total=False): - """OAuth refresh token parameters for creating a credential with refresh support.""" - - client_id: Required[str] - """OAuth client ID.""" - - refresh_token: Required[str] - """OAuth refresh token.""" - - token_endpoint: Required[str] - """Token endpoint URL used to refresh the access token.""" - - token_endpoint_auth: Required[TokenEndpointAuth] - """Token endpoint requires no client authentication.""" - - resource: Optional[str] - """OAuth resource indicator.""" - - scope: Optional[str] - """OAuth scope for the refresh request.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_response.py deleted file mode 100644 index bc68746d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_response.py +++ /dev/null @@ -1,40 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional -from typing_extensions import Annotated, TypeAlias - -from ...._utils import PropertyInfo -from ...._models import BaseModel -from .beta_managed_agents_token_endpoint_auth_none_response import BetaManagedAgentsTokenEndpointAuthNoneResponse -from .beta_managed_agents_token_endpoint_auth_post_response import BetaManagedAgentsTokenEndpointAuthPostResponse -from .beta_managed_agents_token_endpoint_auth_basic_response import BetaManagedAgentsTokenEndpointAuthBasicResponse - -__all__ = ["BetaManagedAgentsMCPOAuthRefreshResponse", "TokenEndpointAuth"] - -TokenEndpointAuth: TypeAlias = Annotated[ - Union[ - BetaManagedAgentsTokenEndpointAuthNoneResponse, - BetaManagedAgentsTokenEndpointAuthBasicResponse, - BetaManagedAgentsTokenEndpointAuthPostResponse, - ], - PropertyInfo(discriminator="type"), -] - - -class BetaManagedAgentsMCPOAuthRefreshResponse(BaseModel): - """OAuth refresh token configuration returned in credential responses.""" - - client_id: str - """OAuth client ID.""" - - token_endpoint: str - """Token endpoint URL used to refresh the access token.""" - - token_endpoint_auth: TokenEndpointAuth - """Token endpoint requires no client authentication.""" - - resource: Optional[str] = None - """OAuth resource indicator.""" - - scope: Optional[str] = None - """OAuth scope for the refresh request.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_update_params.py deleted file mode 100644 index da4990ab..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_refresh_update_params.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import TypeAlias, TypedDict - -from .beta_managed_agents_token_endpoint_auth_post_update_param import BetaManagedAgentsTokenEndpointAuthPostUpdateParam -from .beta_managed_agents_token_endpoint_auth_basic_update_param import ( - BetaManagedAgentsTokenEndpointAuthBasicUpdateParam, -) - -__all__ = ["BetaManagedAgentsMCPOAuthRefreshUpdateParams", "TokenEndpointAuth"] - -TokenEndpointAuth: TypeAlias = Union[ - BetaManagedAgentsTokenEndpointAuthBasicUpdateParam, BetaManagedAgentsTokenEndpointAuthPostUpdateParam -] - - -class BetaManagedAgentsMCPOAuthRefreshUpdateParams(TypedDict, total=False): - """Parameters for updating OAuth refresh token configuration.""" - - refresh_token: Optional[str] - """Updated OAuth refresh token.""" - - scope: Optional[str] - """Updated OAuth scope for the refresh request.""" - - token_endpoint_auth: TokenEndpointAuth - """Updated HTTP Basic authentication parameters for the token endpoint.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_update_params.py deleted file mode 100644 index edbf855d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_mcp_oauth_update_params.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from datetime import datetime -from typing_extensions import Literal, Required, Annotated, TypedDict - -from ...._utils import PropertyInfo -from .beta_managed_agents_mcp_oauth_refresh_update_params import BetaManagedAgentsMCPOAuthRefreshUpdateParams - -__all__ = ["BetaManagedAgentsMCPOAuthUpdateParams"] - - -class BetaManagedAgentsMCPOAuthUpdateParams(TypedDict, total=False): - """Parameters for updating an MCP OAuth credential. - - The `mcp_server_url` is immutable. - """ - - type: Required[Literal["mcp_oauth"]] - - access_token: Optional[str] - """Updated OAuth access token.""" - - expires_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] - """A timestamp in RFC 3339 format""" - - refresh: Optional[BetaManagedAgentsMCPOAuthRefreshUpdateParams] - """Parameters for updating OAuth refresh token configuration.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_static_bearer_auth_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_static_bearer_auth_response.py deleted file mode 100644 index dafa5879..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_static_bearer_auth_response.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsStaticBearerAuthResponse"] - - -class BetaManagedAgentsStaticBearerAuthResponse(BaseModel): - """Static bearer token credential details for an MCP server.""" - - mcp_server_url: str - """URL of the MCP server this credential authenticates against.""" - - type: Literal["static_bearer"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_static_bearer_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_static_bearer_create_params.py deleted file mode 100644 index 3c2b1b54..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_static_bearer_create_params.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsStaticBearerCreateParams"] - - -class BetaManagedAgentsStaticBearerCreateParams(TypedDict, total=False): - """Parameters for creating a static bearer token credential.""" - - token: Required[str] - """Static bearer token value.""" - - mcp_server_url: Required[str] - """URL of the MCP server this credential authenticates against.""" - - type: Required[Literal["static_bearer"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_static_bearer_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_static_bearer_update_params.py deleted file mode 100644 index 3cb1c459..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_static_bearer_update_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsStaticBearerUpdateParams"] - - -class BetaManagedAgentsStaticBearerUpdateParams(TypedDict, total=False): - """Parameters for updating a static bearer token credential. - - The `mcp_server_url` is immutable. - """ - - type: Required[Literal["static_bearer"]] - - token: Optional[str] - """Updated static bearer token value.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_param.py deleted file mode 100644 index 11a985da..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsTokenEndpointAuthBasicParam"] - - -class BetaManagedAgentsTokenEndpointAuthBasicParam(TypedDict, total=False): - """Token endpoint uses HTTP Basic authentication with client credentials.""" - - client_secret: Required[str] - """OAuth client secret.""" - - type: Required[Literal["client_secret_basic"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_response.py deleted file mode 100644 index 38f1d4e2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_response.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsTokenEndpointAuthBasicResponse"] - - -class BetaManagedAgentsTokenEndpointAuthBasicResponse(BaseModel): - """Token endpoint uses HTTP Basic authentication with client credentials.""" - - type: Literal["client_secret_basic"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_update_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_update_param.py deleted file mode 100644 index 62d21ad2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_basic_update_param.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsTokenEndpointAuthBasicUpdateParam"] - - -class BetaManagedAgentsTokenEndpointAuthBasicUpdateParam(TypedDict, total=False): - """Updated HTTP Basic authentication parameters for the token endpoint.""" - - type: Required[Literal["client_secret_basic"]] - - client_secret: Optional[str] - """Updated OAuth client secret.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_none_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_none_param.py deleted file mode 100644 index b5c3fb69..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_none_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsTokenEndpointAuthNoneParam"] - - -class BetaManagedAgentsTokenEndpointAuthNoneParam(TypedDict, total=False): - """Token endpoint requires no client authentication.""" - - type: Required[Literal["none"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_none_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_none_response.py deleted file mode 100644 index c8995d29..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_none_response.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsTokenEndpointAuthNoneResponse"] - - -class BetaManagedAgentsTokenEndpointAuthNoneResponse(BaseModel): - """Token endpoint requires no client authentication.""" - - type: Literal["none"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_param.py deleted file mode 100644 index 092686a5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsTokenEndpointAuthPostParam"] - - -class BetaManagedAgentsTokenEndpointAuthPostParam(TypedDict, total=False): - """Token endpoint uses POST body authentication with client credentials.""" - - client_secret: Required[str] - """OAuth client secret.""" - - type: Required[Literal["client_secret_post"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_response.py deleted file mode 100644 index c1ac6eee..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_response.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ...._models import BaseModel - -__all__ = ["BetaManagedAgentsTokenEndpointAuthPostResponse"] - - -class BetaManagedAgentsTokenEndpointAuthPostResponse(BaseModel): - """Token endpoint uses POST body authentication with client credentials.""" - - type: Literal["client_secret_post"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_update_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_update_param.py deleted file mode 100644 index 74774474..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/beta_managed_agents_token_endpoint_auth_post_update_param.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["BetaManagedAgentsTokenEndpointAuthPostUpdateParam"] - - -class BetaManagedAgentsTokenEndpointAuthPostUpdateParam(TypedDict, total=False): - """Updated POST body authentication parameters for the token endpoint.""" - - type: Required[Literal["client_secret_post"]] - - client_secret: Optional[str] - """Updated OAuth client secret.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/credential_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/credential_create_params.py deleted file mode 100644 index f62a0463..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/credential_create_params.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Union, Optional -from typing_extensions import Required, Annotated, TypeAlias, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_mcp_oauth_create_params import BetaManagedAgentsMCPOAuthCreateParams -from .beta_managed_agents_static_bearer_create_params import BetaManagedAgentsStaticBearerCreateParams - -__all__ = ["CredentialCreateParams", "Auth"] - - -class CredentialCreateParams(TypedDict, total=False): - auth: Required[Auth] - """Authentication details for creating a credential.""" - - display_name: Optional[str] - """Human-readable name for the credential. Up to 255 characters.""" - - metadata: Dict[str, str] - """Arbitrary key-value metadata to attach to the credential. - - Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" - - -Auth: TypeAlias = Union[BetaManagedAgentsMCPOAuthCreateParams, BetaManagedAgentsStaticBearerCreateParams] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/credential_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/credential_list_params.py deleted file mode 100644 index 467b7041..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/credential_list_params.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Annotated, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam - -__all__ = ["CredentialListParams"] - - -class CredentialListParams(TypedDict, total=False): - include_archived: bool - """Whether to include archived credentials in the results.""" - - limit: int - """Maximum number of credentials to return per page. Defaults to 20, maximum 100.""" - - page: str - """Opaque pagination token from a previous `list_credentials` response.""" - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/credential_update_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/credential_update_params.py deleted file mode 100644 index a5ebb503..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta/vaults/credential_update_params.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Union, Optional -from typing_extensions import Required, Annotated, TypeAlias, TypedDict - -from ...._utils import PropertyInfo -from ...anthropic_beta_param import AnthropicBetaParam -from .beta_managed_agents_mcp_oauth_update_params import BetaManagedAgentsMCPOAuthUpdateParams -from .beta_managed_agents_static_bearer_update_params import BetaManagedAgentsStaticBearerUpdateParams - -__all__ = ["CredentialUpdateParams", "Auth"] - - -class CredentialUpdateParams(TypedDict, total=False): - vault_id: Required[str] - - auth: Auth - """Updated authentication details for a credential.""" - - display_name: Optional[str] - """Updated human-readable name for the credential. 1-255 characters.""" - - metadata: Optional[Dict[str, Optional[str]]] - """Metadata patch. - - Set a key to a string to upsert it, or to null to delete it. Omitted keys are - preserved. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" - - -Auth: TypeAlias = Union[BetaManagedAgentsMCPOAuthUpdateParams, BetaManagedAgentsStaticBearerUpdateParams] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_api_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_api_error.py deleted file mode 100644 index 16aa604e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_api_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BetaAPIError"] - - -class BetaAPIError(BaseModel): - message: str - - type: Literal["api_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_authentication_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_authentication_error.py deleted file mode 100644 index 8a555570..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_authentication_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BetaAuthenticationError"] - - -class BetaAuthenticationError(BaseModel): - message: str - - type: Literal["authentication_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_billing_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_billing_error.py deleted file mode 100644 index 1ab37614..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_billing_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BetaBillingError"] - - -class BetaBillingError(BaseModel): - message: str - - type: Literal["billing_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_error.py deleted file mode 100644 index 029d80dc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_error.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from .._utils import PropertyInfo -from .beta_api_error import BetaAPIError -from .beta_billing_error import BetaBillingError -from .beta_not_found_error import BetaNotFoundError -from .beta_overloaded_error import BetaOverloadedError -from .beta_permission_error import BetaPermissionError -from .beta_rate_limit_error import BetaRateLimitError -from .beta_authentication_error import BetaAuthenticationError -from .beta_gateway_timeout_error import BetaGatewayTimeoutError -from .beta_invalid_request_error import BetaInvalidRequestError - -__all__ = ["BetaError"] - -BetaError: TypeAlias = Annotated[ - Union[ - BetaInvalidRequestError, - BetaAuthenticationError, - BetaBillingError, - BetaPermissionError, - BetaNotFoundError, - BetaRateLimitError, - BetaGatewayTimeoutError, - BetaAPIError, - BetaOverloadedError, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_error_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_error_response.py deleted file mode 100644 index 23c9c193..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_error_response.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel -from .beta_error import BetaError - -__all__ = ["BetaErrorResponse"] - - -class BetaErrorResponse(BaseModel): - error: BetaError - - request_id: Optional[str] = None - - type: Literal["error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_gateway_timeout_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_gateway_timeout_error.py deleted file mode 100644 index 9a29705b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_gateway_timeout_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BetaGatewayTimeoutError"] - - -class BetaGatewayTimeoutError(BaseModel): - message: str - - type: Literal["timeout_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_invalid_request_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_invalid_request_error.py deleted file mode 100644 index a84d53cc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_invalid_request_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BetaInvalidRequestError"] - - -class BetaInvalidRequestError(BaseModel): - message: str - - type: Literal["invalid_request_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_not_found_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_not_found_error.py deleted file mode 100644 index 3d57cb5a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_not_found_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BetaNotFoundError"] - - -class BetaNotFoundError(BaseModel): - message: str - - type: Literal["not_found_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_overloaded_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_overloaded_error.py deleted file mode 100644 index ff5dbe81..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_overloaded_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BetaOverloadedError"] - - -class BetaOverloadedError(BaseModel): - message: str - - type: Literal["overloaded_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_permission_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_permission_error.py deleted file mode 100644 index 986cf894..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_permission_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BetaPermissionError"] - - -class BetaPermissionError(BaseModel): - message: str - - type: Literal["permission_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/beta_rate_limit_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/beta_rate_limit_error.py deleted file mode 100644 index ae3cb1ae..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/beta_rate_limit_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["BetaRateLimitError"] - - -class BetaRateLimitError(BaseModel): - message: str - - type: Literal["rate_limit_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/cache_control_ephemeral_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/cache_control_ephemeral_param.py deleted file mode 100644 index 0bdbe03c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/cache_control_ephemeral_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["CacheControlEphemeralParam"] - - -class CacheControlEphemeralParam(TypedDict, total=False): - type: Required[Literal["ephemeral"]] - - ttl: Literal["5m", "1h"] - """The time-to-live for the cache control breakpoint. - - This may be one the following values: - - - `5m`: 5 minutes - - `1h`: 1 hour - - Defaults to `5m`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/cache_creation.py b/.venv/lib/python3.12/site-packages/anthropic/types/cache_creation.py deleted file mode 100644 index 6f3e127e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/cache_creation.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel - -__all__ = ["CacheCreation"] - - -class CacheCreation(BaseModel): - ephemeral_1h_input_tokens: int - """The number of input tokens used to create the 1 hour cache entry.""" - - ephemeral_5m_input_tokens: int - """The number of input tokens used to create the 5 minute cache entry.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/capability_support.py b/.venv/lib/python3.12/site-packages/anthropic/types/capability_support.py deleted file mode 100644 index 2436fa2f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/capability_support.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel - -__all__ = ["CapabilitySupport"] - - -class CapabilitySupport(BaseModel): - """Indicates whether a capability is supported.""" - - supported: bool - """Whether this capability is supported by the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citation_char_location.py b/.venv/lib/python3.12/site-packages/anthropic/types/citation_char_location.py deleted file mode 100644 index fa95782a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citation_char_location.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["CitationCharLocation"] - - -class CitationCharLocation(BaseModel): - cited_text: str - - document_index: int - - document_title: Optional[str] = None - - end_char_index: int - - file_id: Optional[str] = None - - start_char_index: int - - type: Literal["char_location"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citation_char_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/citation_char_location_param.py deleted file mode 100644 index 1cc1dfb1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citation_char_location_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["CitationCharLocationParam"] - - -class CitationCharLocationParam(TypedDict, total=False): - cited_text: Required[str] - - document_index: Required[int] - - document_title: Required[Optional[str]] - - end_char_index: Required[int] - - start_char_index: Required[int] - - type: Required[Literal["char_location"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citation_content_block_location.py b/.venv/lib/python3.12/site-packages/anthropic/types/citation_content_block_location.py deleted file mode 100644 index 0113d848..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citation_content_block_location.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["CitationContentBlockLocation"] - - -class CitationContentBlockLocation(BaseModel): - cited_text: str - - document_index: int - - document_title: Optional[str] = None - - end_block_index: int - - file_id: Optional[str] = None - - start_block_index: int - - type: Literal["content_block_location"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citation_content_block_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/citation_content_block_location_param.py deleted file mode 100644 index ee0a6a23..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citation_content_block_location_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["CitationContentBlockLocationParam"] - - -class CitationContentBlockLocationParam(TypedDict, total=False): - cited_text: Required[str] - - document_index: Required[int] - - document_title: Required[Optional[str]] - - end_block_index: Required[int] - - start_block_index: Required[int] - - type: Required[Literal["content_block_location"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citation_page_location.py b/.venv/lib/python3.12/site-packages/anthropic/types/citation_page_location.py deleted file mode 100644 index 38af60c0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citation_page_location.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["CitationPageLocation"] - - -class CitationPageLocation(BaseModel): - cited_text: str - - document_index: int - - document_title: Optional[str] = None - - end_page_number: int - - file_id: Optional[str] = None - - start_page_number: int - - type: Literal["page_location"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citation_page_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/citation_page_location_param.py deleted file mode 100644 index 483837b5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citation_page_location_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["CitationPageLocationParam"] - - -class CitationPageLocationParam(TypedDict, total=False): - cited_text: Required[str] - - document_index: Required[int] - - document_title: Required[Optional[str]] - - end_page_number: Required[int] - - start_page_number: Required[int] - - type: Required[Literal["page_location"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citation_search_result_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/citation_search_result_location_param.py deleted file mode 100644 index 501d180a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citation_search_result_location_param.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["CitationSearchResultLocationParam"] - - -class CitationSearchResultLocationParam(TypedDict, total=False): - cited_text: Required[str] - - end_block_index: Required[int] - - search_result_index: Required[int] - - source: Required[str] - - start_block_index: Required[int] - - title: Required[Optional[str]] - - type: Required[Literal["search_result_location"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citation_web_search_result_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/citation_web_search_result_location_param.py deleted file mode 100644 index dd46e59c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citation_web_search_result_location_param.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["CitationWebSearchResultLocationParam"] - - -class CitationWebSearchResultLocationParam(TypedDict, total=False): - cited_text: Required[str] - - encrypted_index: Required[str] - - title: Required[Optional[str]] - - type: Required[Literal["web_search_result_location"]] - - url: Required[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citations_config.py b/.venv/lib/python3.12/site-packages/anthropic/types/citations_config.py deleted file mode 100644 index a820422a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citations_config.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel - -__all__ = ["CitationsConfig"] - - -class CitationsConfig(BaseModel): - enabled: bool diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citations_config_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/citations_config_param.py deleted file mode 100644 index 817397f8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citations_config_param.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["CitationsConfigParam"] - - -class CitationsConfigParam(TypedDict, total=False): - enabled: bool diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citations_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/citations_delta.py deleted file mode 100644 index 1cc6a286..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citations_delta.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from .._utils import PropertyInfo -from .._models import BaseModel -from .citation_char_location import CitationCharLocation -from .citation_page_location import CitationPageLocation -from .citation_content_block_location import CitationContentBlockLocation -from .citations_search_result_location import CitationsSearchResultLocation -from .citations_web_search_result_location import CitationsWebSearchResultLocation - -__all__ = ["CitationsDelta", "Citation"] - -Citation: TypeAlias = Annotated[ - Union[ - CitationCharLocation, - CitationPageLocation, - CitationContentBlockLocation, - CitationsWebSearchResultLocation, - CitationsSearchResultLocation, - ], - PropertyInfo(discriminator="type"), -] - - -class CitationsDelta(BaseModel): - citation: Citation - - type: Literal["citations_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citations_search_result_location.py b/.venv/lib/python3.12/site-packages/anthropic/types/citations_search_result_location.py deleted file mode 100644 index bb380d91..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citations_search_result_location.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["CitationsSearchResultLocation"] - - -class CitationsSearchResultLocation(BaseModel): - cited_text: str - - end_block_index: int - - search_result_index: int - - source: str - - start_block_index: int - - title: Optional[str] = None - - type: Literal["search_result_location"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/citations_web_search_result_location.py b/.venv/lib/python3.12/site-packages/anthropic/types/citations_web_search_result_location.py deleted file mode 100644 index 02a113ca..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/citations_web_search_result_location.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["CitationsWebSearchResultLocation"] - - -class CitationsWebSearchResultLocation(BaseModel): - cited_text: str - - encrypted_index: str - - title: Optional[str] = None - - type: Literal["web_search_result_location"] - - url: str diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_output_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_output_block.py deleted file mode 100644 index e5919d29..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_output_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["CodeExecutionOutputBlock"] - - -class CodeExecutionOutputBlock(BaseModel): - file_id: str - - type: Literal["code_execution_output"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_output_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_output_block_param.py deleted file mode 100644 index 4f9252ca..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_output_block_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["CodeExecutionOutputBlockParam"] - - -class CodeExecutionOutputBlockParam(TypedDict, total=False): - file_id: Required[str] - - type: Required[Literal["code_execution_output"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_result_block.py deleted file mode 100644 index 8c48e0a0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_result_block.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from .._models import BaseModel -from .code_execution_output_block import CodeExecutionOutputBlock - -__all__ = ["CodeExecutionResultBlock"] - - -class CodeExecutionResultBlock(BaseModel): - content: List[CodeExecutionOutputBlock] - - return_code: int - - stderr: str - - stdout: str - - type: Literal["code_execution_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_result_block_param.py deleted file mode 100644 index ff33f974..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_result_block_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Literal, Required, TypedDict - -from .code_execution_output_block_param import CodeExecutionOutputBlockParam - -__all__ = ["CodeExecutionResultBlockParam"] - - -class CodeExecutionResultBlockParam(TypedDict, total=False): - content: Required[Iterable[CodeExecutionOutputBlockParam]] - - return_code: Required[int] - - stderr: Required[str] - - stdout: Required[str] - - type: Required[Literal["code_execution_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_20250522_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_20250522_param.py deleted file mode 100644 index 9ac5f611..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_20250522_param.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["CodeExecutionTool20250522Param"] - - -class CodeExecutionTool20250522Param(TypedDict, total=False): - name: Required[Literal["code_execution"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["code_execution_20250522"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_20250825_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_20250825_param.py deleted file mode 100644 index bd9af5ea..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_20250825_param.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["CodeExecutionTool20250825Param"] - - -class CodeExecutionTool20250825Param(TypedDict, total=False): - name: Required[Literal["code_execution"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["code_execution_20250825"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_20260120_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_20260120_param.py deleted file mode 100644 index 36d5fd23..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_20260120_param.py +++ /dev/null @@ -1,38 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["CodeExecutionTool20260120Param"] - - -class CodeExecutionTool20260120Param(TypedDict, total=False): - """ - Code execution tool with REPL state persistence (daemon mode + gVisor checkpoint). - """ - - name: Required[Literal["code_execution"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["code_execution_20260120"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block.py deleted file mode 100644 index 2f355938..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel -from .code_execution_tool_result_block_content import CodeExecutionToolResultBlockContent - -__all__ = ["CodeExecutionToolResultBlock"] - - -class CodeExecutionToolResultBlock(BaseModel): - content: CodeExecutionToolResultBlockContent - """Code execution result with encrypted stdout for PFC + web_search results.""" - - tool_use_id: str - - type: Literal["code_execution_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block_content.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block_content.py deleted file mode 100644 index 56fb034a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block_content.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import TypeAlias - -from .code_execution_result_block import CodeExecutionResultBlock -from .code_execution_tool_result_error import CodeExecutionToolResultError -from .encrypted_code_execution_result_block import EncryptedCodeExecutionResultBlock - -__all__ = ["CodeExecutionToolResultBlockContent"] - -CodeExecutionToolResultBlockContent: TypeAlias = Union[ - CodeExecutionToolResultError, CodeExecutionResultBlock, EncryptedCodeExecutionResultBlock -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block_param.py deleted file mode 100644 index 71fb118f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam -from .code_execution_tool_result_block_param_content_param import CodeExecutionToolResultBlockParamContentParam - -__all__ = ["CodeExecutionToolResultBlockParam"] - - -class CodeExecutionToolResultBlockParam(TypedDict, total=False): - content: Required[CodeExecutionToolResultBlockParamContentParam] - """Code execution result with encrypted stdout for PFC + web_search results.""" - - tool_use_id: Required[str] - - type: Required[Literal["code_execution_tool_result"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block_param_content_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block_param_content_param.py deleted file mode 100644 index c67d4f86..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_block_param_content_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .code_execution_result_block_param import CodeExecutionResultBlockParam -from .code_execution_tool_result_error_param import CodeExecutionToolResultErrorParam -from .encrypted_code_execution_result_block_param import EncryptedCodeExecutionResultBlockParam - -__all__ = ["CodeExecutionToolResultBlockParamContentParam"] - -CodeExecutionToolResultBlockParamContentParam: TypeAlias = Union[ - CodeExecutionToolResultErrorParam, CodeExecutionResultBlockParam, EncryptedCodeExecutionResultBlockParam -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_error.py deleted file mode 100644 index 16dbcf11..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_error.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel -from .code_execution_tool_result_error_code import CodeExecutionToolResultErrorCode - -__all__ = ["CodeExecutionToolResultError"] - - -class CodeExecutionToolResultError(BaseModel): - error_code: CodeExecutionToolResultErrorCode - - type: Literal["code_execution_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_error_code.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_error_code.py deleted file mode 100644 index 13267da9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_error_code.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["CodeExecutionToolResultErrorCode"] - -CodeExecutionToolResultErrorCode: TypeAlias = Literal[ - "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded" -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_error_param.py deleted file mode 100644 index bc9533ec..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/code_execution_tool_result_error_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .code_execution_tool_result_error_code import CodeExecutionToolResultErrorCode - -__all__ = ["CodeExecutionToolResultErrorParam"] - - -class CodeExecutionToolResultErrorParam(TypedDict, total=False): - error_code: Required[CodeExecutionToolResultErrorCode] - - type: Required[Literal["code_execution_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/completion.py b/.venv/lib/python3.12/site-packages/anthropic/types/completion.py deleted file mode 100644 index e6293210..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/completion.py +++ /dev/null @@ -1,43 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .model import Model -from .._models import BaseModel - -__all__ = ["Completion"] - - -class Completion(BaseModel): - id: str - """Unique object identifier. - - The format and length of IDs may change over time. - """ - - completion: str - """The resulting completion up to and excluding the stop sequences.""" - - model: Model - """ - The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - stop_reason: Optional[str] = None - """The reason that we stopped. - - This may be one the following values: - - - `"stop_sequence"`: we reached a stop sequence — either provided by you via the - `stop_sequences` parameter, or a stop sequence built into the model - - `"max_tokens"`: we exceeded `max_tokens_to_sample` or the model's maximum - """ - - type: Literal["completion"] - """Object type. - - For Text Completions, this is always `"completion"`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/completion_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/completion_create_params.py deleted file mode 100644 index 9f337e8f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/completion_create_params.py +++ /dev/null @@ -1,133 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Union -from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict - -from .._types import SequenceNotStr -from .._utils import PropertyInfo -from .model_param import ModelParam -from .metadata_param import MetadataParam -from .anthropic_beta_param import AnthropicBetaParam - -__all__ = [ - "CompletionRequestStreamingMetadata", - "CompletionRequestNonStreamingMetadata", - "CompletionRequestNonStreaming", - "CompletionRequestStreaming", - "CompletionCreateParamsBase", - "Metadata", - "CompletionCreateParamsNonStreaming", - "CompletionCreateParamsStreaming", -] - - -class CompletionCreateParamsBase(TypedDict, total=False): - max_tokens_to_sample: Required[int] - """The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - """ - - model: Required[ModelParam] - """ - The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - prompt: Required[str] - """The prompt that you want Claude to complete. - - For proper response generation you will need to format your prompt using - alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: - - ``` - "\n\nHuman: {userQuestion}\n\nAssistant:" - ``` - - See [prompt validation](https://docs.claude.com/en/api/prompt-validation) and - our guide to [prompt design](https://docs.claude.com/en/docs/intro-to-prompting) - for more details. - """ - - metadata: MetadataParam - """An object describing metadata about the request.""" - - stop_sequences: SequenceNotStr[str] - """Sequences that will cause the model to stop generating. - - Our models stop on `"\n\nHuman:"`, and may include additional built-in stop - sequences in the future. By providing the stop_sequences parameter, you may - include additional strings that will cause the model to stop generating. - """ - - temperature: float - """Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - """ - - top_k: int - """Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - """ - - top_p: float - """Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" - - -Metadata: TypeAlias = MetadataParam -"""This is deprecated, `MetadataParam` should be used instead""" - - -class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase, total=False): - stream: Literal[False] - """Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/streaming) for details. - """ - - -class CompletionCreateParamsStreaming(CompletionCreateParamsBase): - stream: Required[Literal[True]] - """Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/streaming) for details. - """ - - -CompletionRequestStreamingMetadata = MetadataParam -"""This is deprecated, `MetadataParam` should be used instead""" - -CompletionRequestNonStreamingMetadata = MetadataParam -"""This is deprecated, `MetadataParam` should be used instead""" - -CompletionRequestNonStreaming = CompletionCreateParamsNonStreaming -"""This is deprecated, `CompletionCreateParamsNonStreaming` should be used instead""" - -CompletionRequestStreaming = CompletionCreateParamsStreaming -"""This is deprecated, `CompletionCreateParamsStreaming` should be used instead""" - -CompletionCreateParams = Union[CompletionCreateParamsNonStreaming, CompletionCreateParamsStreaming] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/container.py b/.venv/lib/python3.12/site-packages/anthropic/types/container.py deleted file mode 100644 index b0059820..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/container.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from datetime import datetime - -from .._models import BaseModel - -__all__ = ["Container"] - - -class Container(BaseModel): - """ - Information about the container used in the request (for the code execution tool) - """ - - id: str - """Identifier for the container used in this request""" - - expires_at: datetime - """The time at which the container will expire.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/container_upload_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/container_upload_block.py deleted file mode 100644 index c6dd3baf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/container_upload_block.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["ContainerUploadBlock"] - - -class ContainerUploadBlock(BaseModel): - """Response model for a file uploaded to the container.""" - - file_id: str - - type: Literal["container_upload"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/container_upload_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/container_upload_block_param.py deleted file mode 100644 index 1e6fd513..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/container_upload_block_param.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ContainerUploadBlockParam"] - - -class ContainerUploadBlockParam(TypedDict, total=False): - """ - A content block that represents a file to be uploaded to the container - Files uploaded via this block will be available in the container's input directory. - """ - - file_id: Required[str] - - type: Required[Literal["container_upload"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/content_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/content_block.py deleted file mode 100644 index 41338a72..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/content_block.py +++ /dev/null @@ -1,38 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from .._utils import PropertyInfo -from .text_block import TextBlock -from .thinking_block import ThinkingBlock -from .tool_use_block import ToolUseBlock -from .server_tool_use_block import ServerToolUseBlock -from .container_upload_block import ContainerUploadBlock -from .redacted_thinking_block import RedactedThinkingBlock -from .web_fetch_tool_result_block import WebFetchToolResultBlock -from .web_search_tool_result_block import WebSearchToolResultBlock -from .tool_search_tool_result_block import ToolSearchToolResultBlock -from .code_execution_tool_result_block import CodeExecutionToolResultBlock -from .bash_code_execution_tool_result_block import BashCodeExecutionToolResultBlock -from .text_editor_code_execution_tool_result_block import TextEditorCodeExecutionToolResultBlock - -__all__ = ["ContentBlock"] - -ContentBlock: TypeAlias = Annotated[ - Union[ - TextBlock, - ThinkingBlock, - RedactedThinkingBlock, - ToolUseBlock, - ServerToolUseBlock, - WebSearchToolResultBlock, - WebFetchToolResultBlock, - CodeExecutionToolResultBlock, - BashCodeExecutionToolResultBlock, - TextEditorCodeExecutionToolResultBlock, - ToolSearchToolResultBlock, - ContainerUploadBlock, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_delta_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/content_block_delta_event.py deleted file mode 100644 index 8d30cdef..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_delta_event.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .raw_content_block_delta_event import RawContentBlockDeltaEvent - -__all__ = ["ContentBlockDeltaEvent"] - -ContentBlockDeltaEvent = RawContentBlockDeltaEvent -"""The RawContentBlockDeltaEvent type should be used instead""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/content_block_param.py deleted file mode 100644 index f743a986..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_param.py +++ /dev/null @@ -1,44 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .text_block_param import TextBlockParam -from .image_block_param import ImageBlockParam -from .document_block_param import DocumentBlockParam -from .thinking_block_param import ThinkingBlockParam -from .tool_use_block_param import ToolUseBlockParam -from .tool_result_block_param import ToolResultBlockParam -from .search_result_block_param import SearchResultBlockParam -from .server_tool_use_block_param import ServerToolUseBlockParam -from .container_upload_block_param import ContainerUploadBlockParam -from .redacted_thinking_block_param import RedactedThinkingBlockParam -from .web_fetch_tool_result_block_param import WebFetchToolResultBlockParam -from .web_search_tool_result_block_param import WebSearchToolResultBlockParam -from .tool_search_tool_result_block_param import ToolSearchToolResultBlockParam -from .code_execution_tool_result_block_param import CodeExecutionToolResultBlockParam -from .bash_code_execution_tool_result_block_param import BashCodeExecutionToolResultBlockParam -from .text_editor_code_execution_tool_result_block_param import TextEditorCodeExecutionToolResultBlockParam - -__all__ = ["ContentBlockParam"] - -ContentBlockParam: TypeAlias = Union[ - TextBlockParam, - ImageBlockParam, - DocumentBlockParam, - SearchResultBlockParam, - ThinkingBlockParam, - RedactedThinkingBlockParam, - ToolUseBlockParam, - ToolResultBlockParam, - ServerToolUseBlockParam, - WebSearchToolResultBlockParam, - WebFetchToolResultBlockParam, - CodeExecutionToolResultBlockParam, - BashCodeExecutionToolResultBlockParam, - TextEditorCodeExecutionToolResultBlockParam, - ToolSearchToolResultBlockParam, - ContainerUploadBlockParam, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_source_content_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/content_block_source_content_param.py deleted file mode 100644 index 0e70cd25..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_source_content_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .text_block_param import TextBlockParam -from .image_block_param import ImageBlockParam - -__all__ = ["ContentBlockSourceContentParam"] - -ContentBlockSourceContentParam: TypeAlias = Union[TextBlockParam, ImageBlockParam] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/content_block_source_param.py deleted file mode 100644 index 8050f3e6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_source_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable -from typing_extensions import Literal, Required, TypedDict - -from .content_block_source_content_param import ContentBlockSourceContentParam - -__all__ = ["ContentBlockSourceParam"] - - -class ContentBlockSourceParam(TypedDict, total=False): - content: Required[Union[str, Iterable[ContentBlockSourceContentParam]]] - - type: Required[Literal["content"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_start_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/content_block_start_event.py deleted file mode 100644 index 2244a975..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_start_event.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .raw_content_block_start_event import RawContentBlockStartEvent - -__all__ = ["ContentBlockStartEvent"] - -ContentBlockStartEvent = RawContentBlockStartEvent -"""The RawContentBlockStartEvent type should be used instead""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_stop_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/content_block_stop_event.py deleted file mode 100644 index 2c67bccc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/content_block_stop_event.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .raw_content_block_stop_event import RawContentBlockStopEvent - -__all__ = ["ContentBlockStopEvent"] - -ContentBlockStopEvent = RawContentBlockStopEvent -"""The RawContentBlockStopEvent type should be used instead""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/context_management_capability.py b/.venv/lib/python3.12/site-packages/anthropic/types/context_management_capability.py deleted file mode 100644 index 625189f7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/context_management_capability.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from .._models import BaseModel -from .capability_support import CapabilitySupport - -__all__ = ["ContextManagementCapability"] - - -class ContextManagementCapability(BaseModel): - """Context management capability details.""" - - clear_thinking_20251015: Optional[CapabilitySupport] = None - """Indicates whether a capability is supported.""" - - clear_tool_uses_20250919: Optional[CapabilitySupport] = None - """Indicates whether a capability is supported.""" - - compact_20260112: Optional[CapabilitySupport] = None - """Indicates whether a capability is supported.""" - - supported: bool - """Whether this capability is supported by the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/direct_caller.py b/.venv/lib/python3.12/site-packages/anthropic/types/direct_caller.py deleted file mode 100644 index 05dfea72..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/direct_caller.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["DirectCaller"] - - -class DirectCaller(BaseModel): - """Tool invocation directly from the model.""" - - type: Literal["direct"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/direct_caller_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/direct_caller_param.py deleted file mode 100644 index 15eed6bd..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/direct_caller_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["DirectCallerParam"] - - -class DirectCallerParam(TypedDict, total=False): - """Tool invocation directly from the model.""" - - type: Required[Literal["direct"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/document_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/document_block.py deleted file mode 100644 index 53ec8bde..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/document_block.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from .._utils import PropertyInfo -from .._models import BaseModel -from .citations_config import CitationsConfig -from .base64_pdf_source import Base64PDFSource -from .plain_text_source import PlainTextSource - -__all__ = ["DocumentBlock", "Source"] - -Source: TypeAlias = Annotated[Union[Base64PDFSource, PlainTextSource], PropertyInfo(discriminator="type")] - - -class DocumentBlock(BaseModel): - citations: Optional[CitationsConfig] = None - """Citation configuration for the document""" - - source: Source - - title: Optional[str] = None - """The title of the document""" - - type: Literal["document"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/document_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/document_block_param.py deleted file mode 100644 index 29fd857f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/document_block_param.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .url_pdf_source_param import URLPDFSourceParam -from .citations_config_param import CitationsConfigParam -from .base64_pdf_source_param import Base64PDFSourceParam -from .plain_text_source_param import PlainTextSourceParam -from .content_block_source_param import ContentBlockSourceParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["DocumentBlockParam", "Source"] - -Source: TypeAlias = Union[Base64PDFSourceParam, PlainTextSourceParam, ContentBlockSourceParam, URLPDFSourceParam] - - -class DocumentBlockParam(TypedDict, total=False): - source: Required[Source] - - type: Required[Literal["document"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: Optional[CitationsConfigParam] - - context: Optional[str] - - title: Optional[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/effort_capability.py b/.venv/lib/python3.12/site-packages/anthropic/types/effort_capability.py deleted file mode 100644 index 26b61ef7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/effort_capability.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from .._models import BaseModel -from .capability_support import CapabilitySupport - -__all__ = ["EffortCapability"] - - -class EffortCapability(BaseModel): - """Effort (reasoning_effort) capability details.""" - - high: CapabilitySupport - """Whether the model supports high effort level.""" - - low: CapabilitySupport - """Whether the model supports low effort level.""" - - max: CapabilitySupport - """Whether the model supports max effort level.""" - - medium: CapabilitySupport - """Whether the model supports medium effort level.""" - - supported: bool - """Whether this capability is supported by the model.""" - - xhigh: Optional[CapabilitySupport] = None - """Indicates whether a capability is supported.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/encrypted_code_execution_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/encrypted_code_execution_result_block.py deleted file mode 100644 index ebbdf9c8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/encrypted_code_execution_result_block.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from .._models import BaseModel -from .code_execution_output_block import CodeExecutionOutputBlock - -__all__ = ["EncryptedCodeExecutionResultBlock"] - - -class EncryptedCodeExecutionResultBlock(BaseModel): - """Code execution result with encrypted stdout for PFC + web_search results.""" - - content: List[CodeExecutionOutputBlock] - - encrypted_stdout: str - - return_code: int - - stderr: str - - type: Literal["encrypted_code_execution_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/encrypted_code_execution_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/encrypted_code_execution_result_block_param.py deleted file mode 100644 index 931e3a34..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/encrypted_code_execution_result_block_param.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Literal, Required, TypedDict - -from .code_execution_output_block_param import CodeExecutionOutputBlockParam - -__all__ = ["EncryptedCodeExecutionResultBlockParam"] - - -class EncryptedCodeExecutionResultBlockParam(TypedDict, total=False): - """Code execution result with encrypted stdout for PFC + web_search results.""" - - content: Required[Iterable[CodeExecutionOutputBlockParam]] - - encrypted_stdout: Required[str] - - return_code: Required[int] - - stderr: Required[str] - - type: Required[Literal["encrypted_code_execution_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/image_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/image_block_param.py deleted file mode 100644 index 24691f85..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/image_block_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .url_image_source_param import URLImageSourceParam -from .base64_image_source_param import Base64ImageSourceParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ImageBlockParam", "Source"] - -Source: TypeAlias = Union[Base64ImageSourceParam, URLImageSourceParam] - - -class ImageBlockParam(TypedDict, total=False): - source: Required[Source] - - type: Required[Literal["image"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/input_json_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/input_json_delta.py deleted file mode 100644 index 5d735d72..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/input_json_delta.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["InputJSONDelta", "InputJsonDelta"] - - -class InputJSONDelta(BaseModel): - partial_json: str - - type: Literal["input_json_delta"] - - -InputJsonDelta = InputJSONDelta diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/json_output_format_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/json_output_format_param.py deleted file mode 100644 index ff06fa0d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/json_output_format_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["JSONOutputFormatParam"] - - -class JSONOutputFormatParam(TypedDict, total=False): - schema: Required[Dict[str, object]] - """The JSON schema of the format""" - - type: Required[Literal["json_schema"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/memory_tool_20250818_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/memory_tool_20250818_param.py deleted file mode 100644 index a8e199e0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/memory_tool_20250818_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["MemoryTool20250818Param"] - - -class MemoryTool20250818Param(TypedDict, total=False): - name: Required[Literal["memory"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["memory_20250818"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message.py b/.venv/lib/python3.12/site-packages/anthropic/types/message.py deleted file mode 100644 index f7971468..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message.py +++ /dev/null @@ -1,128 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import Literal - -from .model import Model -from .usage import Usage -from .._models import BaseModel -from .container import Container -from .stop_reason import StopReason -from .content_block import ContentBlock, ContentBlock as ContentBlock -from .refusal_stop_details import RefusalStopDetails - -__all__ = ["Message"] - - -class Message(BaseModel): - id: str - """Unique object identifier. - - The format and length of IDs may change over time. - """ - - container: Optional[Container] = None - """ - Information about the container used in the request (for the code execution - tool) - """ - - content: List[ContentBlock] - """Content generated by the model. - - This is an array of content blocks, each of which has a `type` that determines - its shape. - - Example: - - ```json - [{ "type": "text", "text": "Hi, I'm Claude." }] - ``` - - If the request input `messages` ended with an `assistant` turn, then the - response `content` will continue directly from that last turn. You can use this - to constrain the model's output. - - For example, if the input `messages` were: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Then the response `content` might be: - - ```json - [{ "type": "text", "text": "B)" }] - ``` - """ - - model: Model - """ - The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - role: Literal["assistant"] - """Conversational role of the generated message. - - This will always be `"assistant"`. - """ - - stop_details: Optional[RefusalStopDetails] = None - """Structured information about a refusal.""" - - stop_reason: Optional[StopReason] = None - """The reason that we stopped. - - This may be one the following values: - - - `"end_turn"`: the model reached a natural stopping point - - `"max_tokens"`: we exceeded the requested `max_tokens` or the model's maximum - - `"stop_sequence"`: one of your provided custom `stop_sequences` was generated - - `"tool_use"`: the model invoked one or more tools - - `"pause_turn"`: we paused a long-running turn. You may provide the response - back as-is in a subsequent request to let the model continue. - - `"refusal"`: when streaming classifiers intervene to handle potential policy - violations - - In non-streaming mode this value is always non-null. In streaming mode, it is - null in the `message_start` event and non-null otherwise. - """ - - stop_sequence: Optional[str] = None - """Which custom stop sequence was generated, if any. - - This value will be a non-null string if one of your custom stop sequences was - generated. - """ - - type: Literal["message"] - """Object type. - - For Messages, this is always `"message"`. - """ - - usage: Usage - """Billing and rate-limit usage. - - Anthropic's API bills and rate-limits by token counts, as tokens represent the - underlying cost to our systems. - - Under the hood, the API transforms requests into a format suitable for the - model. The model's output then goes through a parsing stage before becoming an - API response. As a result, the token counts in `usage` will not match one-to-one - with the exact visible content of an API request or response. - - For example, `output_tokens` will be non-zero, even for an empty string response - from Claude. - - Total input tokens in a request is the summation of `input_tokens`, - `cache_creation_input_tokens`, and `cache_read_input_tokens`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message_count_tokens_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/message_count_tokens_params.py deleted file mode 100644 index a056a5b5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message_count_tokens_params.py +++ /dev/null @@ -1,208 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable, Optional -from typing_extensions import Required, TypedDict - -from .model_param import ModelParam -from .message_param import MessageParam -from .text_block_param import TextBlockParam -from .tool_choice_param import ToolChoiceParam -from .output_config_param import OutputConfigParam -from .thinking_config_param import ThinkingConfigParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam -from .message_count_tokens_tool_param import MessageCountTokensToolParam - -__all__ = ["MessageCountTokensParams"] - - -class MessageCountTokensParams(TypedDict, total=False): - messages: Required[Iterable[MessageParam]] - """Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - """ - - model: Required[ModelParam] - """ - The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - cache_control: Optional[CacheControlEphemeralParam] - """ - Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - """ - - output_config: OutputConfigParam - """Configuration options for the model's output, such as the output format.""" - - system: Union[str, Iterable[TextBlockParam]] - """System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - """ - - thinking: ThinkingConfigParam - """Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - """ - - tool_choice: ToolChoiceParam - """How the model should use the provided tools. - - The model can use a specific tool, any available tool, decide by itself, or not - use tools at all. - """ - - tools: Iterable[MessageCountTokensToolParam] - """Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message_count_tokens_tool_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/message_count_tokens_tool_param.py deleted file mode 100644 index 09cf99ca..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message_count_tokens_tool_param.py +++ /dev/null @@ -1,44 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .tool_param import ToolParam -from .tool_bash_20250124_param import ToolBash20250124Param -from .memory_tool_20250818_param import MemoryTool20250818Param -from .web_fetch_tool_20250910_param import WebFetchTool20250910Param -from .web_fetch_tool_20260209_param import WebFetchTool20260209Param -from .web_fetch_tool_20260309_param import WebFetchTool20260309Param -from .web_search_tool_20250305_param import WebSearchTool20250305Param -from .web_search_tool_20260209_param import WebSearchTool20260209Param -from .tool_text_editor_20250124_param import ToolTextEditor20250124Param -from .tool_text_editor_20250429_param import ToolTextEditor20250429Param -from .tool_text_editor_20250728_param import ToolTextEditor20250728Param -from .code_execution_tool_20250522_param import CodeExecutionTool20250522Param -from .code_execution_tool_20250825_param import CodeExecutionTool20250825Param -from .code_execution_tool_20260120_param import CodeExecutionTool20260120Param -from .tool_search_tool_bm25_20251119_param import ToolSearchToolBm25_20251119Param -from .tool_search_tool_regex_20251119_param import ToolSearchToolRegex20251119Param - -__all__ = ["MessageCountTokensToolParam"] - -MessageCountTokensToolParam: TypeAlias = Union[ - ToolParam, - ToolBash20250124Param, - CodeExecutionTool20250522Param, - CodeExecutionTool20250825Param, - CodeExecutionTool20260120Param, - MemoryTool20250818Param, - ToolTextEditor20250124Param, - ToolTextEditor20250429Param, - ToolTextEditor20250728Param, - WebSearchTool20250305Param, - WebFetchTool20250910Param, - WebSearchTool20260209Param, - WebFetchTool20260209Param, - WebFetchTool20260309Param, - ToolSearchToolBm25_20251119Param, - ToolSearchToolRegex20251119Param, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/message_create_params.py deleted file mode 100644 index 09c79491..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message_create_params.py +++ /dev/null @@ -1,330 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .._types import SequenceNotStr -from .model_param import ModelParam -from .message_param import MessageParam -from .metadata_param import MetadataParam -from .text_block_param import TextBlockParam -from .tool_union_param import ToolUnionParam -from .tool_choice_param import ToolChoiceParam -from .output_config_param import OutputConfigParam -from .thinking_config_param import ThinkingConfigParam -from .tool_choice_any_param import ToolChoiceAnyParam -from .tool_choice_auto_param import ToolChoiceAutoParam -from .tool_choice_tool_param import ToolChoiceToolParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = [ - "MessageCreateParamsBase", - "Metadata", - "ToolChoice", - "ToolChoiceToolChoiceAuto", - "ToolChoiceToolChoiceAny", - "ToolChoiceToolChoiceTool", - "MessageCreateParamsNonStreaming", - "MessageCreateParamsStreaming", -] - - -class MessageCreateParamsBase(TypedDict, total=False): - max_tokens: Required[int] - """The maximum number of tokens to generate before stopping. - - Note that our models may stop _before_ reaching this maximum. This parameter - only specifies the absolute maximum number of tokens to generate. - - Different models have different maximum values for this parameter. See - [models](https://docs.claude.com/en/docs/models-overview) for details. - """ - - messages: Required[Iterable[MessageParam]] - """Input messages. - - Our models are trained to operate on alternating `user` and `assistant` - conversational turns. When creating a new `Message`, you specify the prior - conversational turns with the `messages` parameter, and the model then generates - the next `Message` in the conversation. Consecutive `user` or `assistant` turns - in your request will be combined into a single turn. - - Each input message must be an object with a `role` and `content`. You can - specify a single `user`-role message, or you can include multiple `user` and - `assistant` messages. - - If the final message uses the `assistant` role, the response content will - continue immediately from the content in that message. This can be used to - constrain part of the model's response. - - Example with a single `user` message: - - ```json - [{ "role": "user", "content": "Hello, Claude" }] - ``` - - Example with multiple conversational turns: - - ```json - [ - { "role": "user", "content": "Hello there." }, - { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, - { "role": "user", "content": "Can you explain LLMs in plain English?" } - ] - ``` - - Example with a partially-filled response from Claude: - - ```json - [ - { - "role": "user", - "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" - }, - { "role": "assistant", "content": "The best answer is (" } - ] - ``` - - Each input message `content` may be either a single `string` or an array of - content blocks, where each block has a specific `type`. Using a `string` for - `content` is shorthand for an array of one content block of type `"text"`. The - following input messages are equivalent: - - ```json - { "role": "user", "content": "Hello, Claude" } - ``` - - ```json - { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } - ``` - - See [input examples](https://docs.claude.com/en/api/messages-examples). - - Note that if you want to include a - [system prompt](https://docs.claude.com/en/docs/system-prompts), you can use the - top-level `system` parameter — there is no `"system"` role for input messages in - the Messages API. - - There is a limit of 100,000 messages in a single request. - """ - - model: Required[ModelParam] - """ - The model that will complete your prompt.\n\nSee - [models](https://docs.anthropic.com/en/docs/models-overview) for additional - details and options. - """ - - cache_control: Optional[CacheControlEphemeralParam] - """ - Top-level cache control automatically applies a cache_control marker to the last - cacheable block in the request. - """ - - container: Optional[str] - """Container identifier for reuse across requests.""" - - inference_geo: Optional[str] - """Specifies the geographic region for inference processing. - - If not specified, the workspace's `default_inference_geo` is used. - """ - - metadata: MetadataParam - """An object describing metadata about the request.""" - - output_config: OutputConfigParam - """Configuration options for the model's output, such as the output format.""" - - service_tier: Literal["auto", "standard_only"] - """ - Determines whether to use priority capacity (if available) or standard capacity - for this request. - - Anthropic offers different levels of service for your API requests. See - [service-tiers](https://docs.claude.com/en/api/service-tiers) for details. - """ - - stop_sequences: SequenceNotStr[str] - """Custom text sequences that will cause the model to stop generating. - - Our models will normally stop when they have naturally completed their turn, - which will result in a response `stop_reason` of `"end_turn"`. - - If you want the model to stop generating when it encounters custom strings of - text, you can use the `stop_sequences` parameter. If the model encounters one of - the custom sequences, the response `stop_reason` value will be `"stop_sequence"` - and the response `stop_sequence` value will contain the matched stop sequence. - """ - - system: Union[str, Iterable[TextBlockParam]] - """System prompt. - - A system prompt is a way of providing context and instructions to Claude, such - as specifying a particular goal or role. See our - [guide to system prompts](https://docs.claude.com/en/docs/system-prompts). - """ - - temperature: float - """Amount of randomness injected into the response. - - Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` - for analytical / multiple choice, and closer to `1.0` for creative and - generative tasks. - - Note that even with `temperature` of `0.0`, the results will not be fully - deterministic. - """ - - thinking: ThinkingConfigParam - """Configuration for enabling Claude's extended thinking. - - When enabled, responses include `thinking` content blocks showing Claude's - thinking process before the final answer. Requires a minimum budget of 1,024 - tokens and counts towards your `max_tokens` limit. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - """ - - tool_choice: ToolChoiceParam - """How the model should use the provided tools. - - The model can use a specific tool, any available tool, decide by itself, or not - use tools at all. - """ - - tools: Iterable[ToolUnionParam] - """Definitions of tools that the model may use. - - If you include `tools` in your API request, the model may return `tool_use` - content blocks that represent the model's use of those tools. You can then run - those tools using the tool input generated by the model and then optionally - return results back to the model using `tool_result` content blocks. - - There are two types of tools: **client tools** and **server tools**. The - behavior described below applies to client tools. For - [server tools](https://docs.claude.com/en/docs/agents-and-tools/tool-use/overview#server-tools), - see their individual documentation as each has its own behavior (e.g., the - [web search tool](https://docs.claude.com/en/docs/agents-and-tools/tool-use/web-search-tool)). - - Each tool definition includes: - - - `name`: Name of the tool. - - `description`: Optional, but strongly-recommended description of the tool. - - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the - tool `input` shape that the model will produce in `tool_use` output content - blocks. - - For example, if you defined `tools` as: - - ```json - [ - { - "name": "get_stock_price", - "description": "Get the current stock price for a given ticker symbol.", - "input_schema": { - "type": "object", - "properties": { - "ticker": { - "type": "string", - "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." - } - }, - "required": ["ticker"] - } - } - ] - ``` - - And then asked the model "What's the S&P 500 at today?", the model might produce - `tool_use` content blocks in the response like this: - - ```json - [ - { - "type": "tool_use", - "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "name": "get_stock_price", - "input": { "ticker": "^GSPC" } - } - ] - ``` - - You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an - input, and return the following back to the model in a subsequent `user` - message: - - ```json - [ - { - "type": "tool_result", - "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", - "content": "259.75 USD" - } - ] - ``` - - Tools can be used for workflows that include running client-side tools and - functions, or more generally whenever you want the model to produce a particular - JSON structure of output. - - See our [guide](https://docs.claude.com/en/docs/tool-use) for more details. - """ - - top_k: int - """Only sample from the top K options for each subsequent token. - - Used to remove "long tail" low probability responses. - [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). - - Recommended for advanced use cases only. - """ - - top_p: float - """Use nucleus sampling. - - In nucleus sampling, we compute the cumulative distribution over all the options - for each subsequent token in decreasing probability order and cut it off once it - reaches a particular probability specified by `top_p`. - - Recommended for advanced use cases only. - """ - - -Metadata: TypeAlias = MetadataParam -"""This is deprecated, `MetadataParam` should be used instead""" - -ToolChoice: TypeAlias = ToolChoiceParam -"""This is deprecated, `ToolChoiceParam` should be used instead""" - -ToolChoiceToolChoiceAuto: TypeAlias = ToolChoiceAutoParam -"""This is deprecated, `ToolChoiceAutoParam` should be used instead""" - -ToolChoiceToolChoiceAny: TypeAlias = ToolChoiceAnyParam -"""This is deprecated, `ToolChoiceAnyParam` should be used instead""" - -ToolChoiceToolChoiceTool: TypeAlias = ToolChoiceToolParam -"""This is deprecated, `ToolChoiceToolParam` should be used instead""" - - -class MessageCreateParamsNonStreaming(MessageCreateParamsBase, total=False): - stream: Literal[False] - """Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - """ - - -class MessageCreateParamsStreaming(MessageCreateParamsBase): - stream: Required[Literal[True]] - """Whether to incrementally stream the response using server-sent events. - - See [streaming](https://docs.claude.com/en/api/messages-streaming) for details. - """ - - -MessageCreateParams = Union[MessageCreateParamsNonStreaming, MessageCreateParamsStreaming] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message_delta_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/message_delta_event.py deleted file mode 100644 index cb1687ba..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message_delta_event.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .raw_message_delta_event import RawMessageDeltaEvent - -__all__ = ["MessageDeltaEvent"] - -MessageDeltaEvent = RawMessageDeltaEvent -"""The RawMessageDeltaEvent type should be used instead""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message_delta_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/message_delta_usage.py deleted file mode 100644 index 11bd49d7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message_delta_usage.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from .._models import BaseModel -from .server_tool_usage import ServerToolUsage - -__all__ = ["MessageDeltaUsage"] - - -class MessageDeltaUsage(BaseModel): - cache_creation_input_tokens: Optional[int] = None - """The cumulative number of input tokens used to create the cache entry.""" - - cache_read_input_tokens: Optional[int] = None - """The cumulative number of input tokens read from the cache.""" - - input_tokens: Optional[int] = None - """The cumulative number of input tokens which were used.""" - - output_tokens: int - """The cumulative number of output tokens which were used.""" - - server_tool_use: Optional[ServerToolUsage] = None - """The number of server tool requests.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/message_param.py deleted file mode 100644 index 97303b50..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message_param.py +++ /dev/null @@ -1,57 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable -from typing_extensions import Literal, Required, TypedDict - -from .content_block import ContentBlock -from .text_block_param import TextBlockParam -from .image_block_param import ImageBlockParam -from .document_block_param import DocumentBlockParam -from .thinking_block_param import ThinkingBlockParam -from .tool_use_block_param import ToolUseBlockParam -from .tool_result_block_param import ToolResultBlockParam -from .search_result_block_param import SearchResultBlockParam -from .server_tool_use_block_param import ServerToolUseBlockParam -from .container_upload_block_param import ContainerUploadBlockParam -from .redacted_thinking_block_param import RedactedThinkingBlockParam -from .web_fetch_tool_result_block_param import WebFetchToolResultBlockParam -from .web_search_tool_result_block_param import WebSearchToolResultBlockParam -from .tool_search_tool_result_block_param import ToolSearchToolResultBlockParam -from .code_execution_tool_result_block_param import CodeExecutionToolResultBlockParam -from .bash_code_execution_tool_result_block_param import BashCodeExecutionToolResultBlockParam -from .text_editor_code_execution_tool_result_block_param import TextEditorCodeExecutionToolResultBlockParam - -__all__ = ["MessageParam"] - - -class MessageParam(TypedDict, total=False): - content: Required[ - Union[ - str, - Iterable[ - Union[ - TextBlockParam, - ImageBlockParam, - DocumentBlockParam, - SearchResultBlockParam, - ThinkingBlockParam, - RedactedThinkingBlockParam, - ToolUseBlockParam, - ToolResultBlockParam, - ServerToolUseBlockParam, - WebSearchToolResultBlockParam, - WebFetchToolResultBlockParam, - CodeExecutionToolResultBlockParam, - BashCodeExecutionToolResultBlockParam, - TextEditorCodeExecutionToolResultBlockParam, - ToolSearchToolResultBlockParam, - ContainerUploadBlockParam, - ContentBlock, - ] - ], - ] - ] - - role: Required[Literal["user", "assistant"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message_start_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/message_start_event.py deleted file mode 100644 index d089e563..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message_start_event.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .raw_message_start_event import RawMessageStartEvent - -__all__ = ["MessageStartEvent"] - -MessageStartEvent = RawMessageStartEvent -"""The RawMessageStartEvent type should be used instead""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message_stop_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/message_stop_event.py deleted file mode 100644 index 30564cbe..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message_stop_event.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .raw_message_stop_event import RawMessageStopEvent - -__all__ = ["MessageStopEvent"] - -MessageStopEvent = RawMessageStopEvent -"""The RawMessageStopEvent type should be used instead""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message_stream_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/message_stream_event.py deleted file mode 100644 index 65485501..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message_stream_event.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .raw_message_stream_event import RawMessageStreamEvent - -__all__ = ["MessageStreamEvent"] - -MessageStreamEvent = RawMessageStreamEvent -"""The RawMessageStreamEvent type should be used instead""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/message_tokens_count.py b/.venv/lib/python3.12/site-packages/anthropic/types/message_tokens_count.py deleted file mode 100644 index ff114493..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/message_tokens_count.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel - -__all__ = ["MessageTokensCount"] - - -class MessageTokensCount(BaseModel): - input_tokens: int - """ - The total number of tokens across the provided list of messages, system prompt, - and tools. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/__init__.py deleted file mode 100644 index 25d311da..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .message_batch import MessageBatch as MessageBatch -from .batch_list_params import BatchListParams as BatchListParams -from .batch_create_params import BatchCreateParams as BatchCreateParams -from .message_batch_result import MessageBatchResult as MessageBatchResult -from .deleted_message_batch import DeletedMessageBatch as DeletedMessageBatch -from .message_batch_errored_result import MessageBatchErroredResult as MessageBatchErroredResult -from .message_batch_expired_result import MessageBatchExpiredResult as MessageBatchExpiredResult -from .message_batch_request_counts import MessageBatchRequestCounts as MessageBatchRequestCounts -from .message_batch_canceled_result import MessageBatchCanceledResult as MessageBatchCanceledResult -from .message_batch_succeeded_result import MessageBatchSucceededResult as MessageBatchSucceededResult -from .message_batch_individual_response import MessageBatchIndividualResponse as MessageBatchIndividualResponse diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/batch_create_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/batch_create_params.py deleted file mode 100644 index 880136e2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/batch_create_params.py +++ /dev/null @@ -1,37 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Required, TypedDict - -from ..message_create_params import MessageCreateParamsNonStreaming - -__all__ = ["BatchCreateParams", "Request"] - - -class BatchCreateParams(TypedDict, total=False): - requests: Required[Iterable[Request]] - """List of requests for prompt completion. - - Each is an individual request to create a Message. - """ - - - -class Request(TypedDict, total=False): - custom_id: Required[str] - """Developer-provided ID created for each request in a Message Batch. - - Useful for matching results to requests, as results may be given out of request - order. - - Must be unique for each request within the Message Batch. - """ - - params: Required[MessageCreateParamsNonStreaming] - """Messages API creation parameters for the individual request. - - See the [Messages API reference](https://docs.claude.com/en/api/messages) for - full documentation on available parameters. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/batch_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/batch_list_params.py deleted file mode 100644 index 7b290a77..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/batch_list_params.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["BatchListParams"] - - -class BatchListParams(TypedDict, total=False): - after_id: str - """ID of the object to use as a cursor for pagination. - - When provided, returns the page of results immediately after this object. - """ - - before_id: str - """ID of the object to use as a cursor for pagination. - - When provided, returns the page of results immediately before this object. - """ - - limit: int - """Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/deleted_message_batch.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/deleted_message_batch.py deleted file mode 100644 index 7a6c321e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/deleted_message_batch.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["DeletedMessageBatch"] - - -class DeletedMessageBatch(BaseModel): - id: str - """ID of the Message Batch.""" - - type: Literal["message_batch_deleted"] - """Deleted object type. - - For Message Batches, this is always `"message_batch_deleted"`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch.py deleted file mode 100644 index a03e73e1..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch.py +++ /dev/null @@ -1,77 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from ..._models import BaseModel -from .message_batch_request_counts import MessageBatchRequestCounts - -__all__ = ["MessageBatch"] - - -class MessageBatch(BaseModel): - id: str - """Unique object identifier. - - The format and length of IDs may change over time. - """ - - archived_at: Optional[datetime] = None - """ - RFC 3339 datetime string representing the time at which the Message Batch was - archived and its results became unavailable. - """ - - cancel_initiated_at: Optional[datetime] = None - """ - RFC 3339 datetime string representing the time at which cancellation was - initiated for the Message Batch. Specified only if cancellation was initiated. - """ - - created_at: datetime - """ - RFC 3339 datetime string representing the time at which the Message Batch was - created. - """ - - ended_at: Optional[datetime] = None - """ - RFC 3339 datetime string representing the time at which processing for the - Message Batch ended. Specified only once processing ends. - - Processing ends when every request in a Message Batch has either succeeded, - errored, canceled, or expired. - """ - - expires_at: datetime - """ - RFC 3339 datetime string representing the time at which the Message Batch will - expire and end processing, which is 24 hours after creation. - """ - - processing_status: Literal["in_progress", "canceling", "ended"] - """Processing status of the Message Batch.""" - - request_counts: MessageBatchRequestCounts - """Tallies requests within the Message Batch, categorized by their status. - - Requests start as `processing` and move to one of the other statuses only once - processing of the entire batch ends. The sum of all values always matches the - total number of requests in the batch. - """ - - results_url: Optional[str] = None - """URL to a `.jsonl` file containing the results of the Message Batch requests. - - Specified only once processing ends. - - Results in the file are not guaranteed to be in the same order as requests. Use - the `custom_id` field to match results to requests. - """ - - type: Literal["message_batch"] - """Object type. - - For Message Batches, this is always `"message_batch"`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_canceled_result.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_canceled_result.py deleted file mode 100644 index 9826aa91..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_canceled_result.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["MessageBatchCanceledResult"] - - -class MessageBatchCanceledResult(BaseModel): - type: Literal["canceled"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_errored_result.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_errored_result.py deleted file mode 100644 index 5f890bfd..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_errored_result.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel -from ..shared.error_response import ErrorResponse - -__all__ = ["MessageBatchErroredResult"] - - -class MessageBatchErroredResult(BaseModel): - error: ErrorResponse - - type: Literal["errored"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_expired_result.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_expired_result.py deleted file mode 100644 index ab9964e7..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_expired_result.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["MessageBatchExpiredResult"] - - -class MessageBatchExpiredResult(BaseModel): - type: Literal["expired"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_individual_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_individual_response.py deleted file mode 100644 index 2bb5c5f3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_individual_response.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel -from .message_batch_result import MessageBatchResult - -__all__ = ["MessageBatchIndividualResponse"] - - -class MessageBatchIndividualResponse(BaseModel): - """ - This is a single line in the response `.jsonl` file and does not represent the response as a whole. - """ - - custom_id: str - """Developer-provided ID created for each request in a Message Batch. - - Useful for matching results to requests, as results may be given out of request - order. - - Must be unique for each request within the Message Batch. - """ - - result: MessageBatchResult - """Processing result for this request. - - Contains a Message output if processing was successful, an error response if - processing failed, or the reason why processing was not attempted, such as - cancellation or expiration. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_request_counts.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_request_counts.py deleted file mode 100644 index 041f3b38..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_request_counts.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from ..._models import BaseModel - -__all__ = ["MessageBatchRequestCounts"] - - -class MessageBatchRequestCounts(BaseModel): - canceled: int - """Number of requests in the Message Batch that have been canceled. - - This is zero until processing of the entire Message Batch has ended. - """ - - errored: int - """Number of requests in the Message Batch that encountered an error. - - This is zero until processing of the entire Message Batch has ended. - """ - - expired: int - """Number of requests in the Message Batch that have expired. - - This is zero until processing of the entire Message Batch has ended. - """ - - processing: int - """Number of requests in the Message Batch that are processing.""" - - succeeded: int - """Number of requests in the Message Batch that have completed successfully. - - This is zero until processing of the entire Message Batch has ended. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_result.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_result.py deleted file mode 100644 index 3186f2aa..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_result.py +++ /dev/null @@ -1,19 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from .message_batch_errored_result import MessageBatchErroredResult -from .message_batch_expired_result import MessageBatchExpiredResult -from .message_batch_canceled_result import MessageBatchCanceledResult -from .message_batch_succeeded_result import MessageBatchSucceededResult - -__all__ = ["MessageBatchResult"] - -MessageBatchResult: TypeAlias = Annotated[ - Union[ - MessageBatchSucceededResult, MessageBatchErroredResult, MessageBatchCanceledResult, MessageBatchExpiredResult - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_succeeded_result.py b/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_succeeded_result.py deleted file mode 100644 index 1cc454a4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/messages/message_batch_succeeded_result.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..message import Message -from ..._models import BaseModel - -__all__ = ["MessageBatchSucceededResult"] - - -class MessageBatchSucceededResult(BaseModel): - message: Message - - type: Literal["succeeded"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/metadata_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/metadata_param.py deleted file mode 100644 index b7bc1ea3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/metadata_param.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import TypedDict - -__all__ = ["MetadataParam"] - - -class MetadataParam(TypedDict, total=False): - user_id: Optional[str] - """An external identifier for the user who is associated with the request. - - This should be a uuid, hash value, or other opaque identifier. Anthropic may use - this id to help detect abuse. Do not include any identifying information such as - name, email address, or phone number. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/model.py b/.venv/lib/python3.12/site-packages/anthropic/types/model.py deleted file mode 100644 index 87999881..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/model.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, TypeAlias - -__all__ = ["Model"] - -Model: TypeAlias = Union[ - Literal[ - "claude-opus-4-7", - "claude-mythos-preview", - "claude-opus-4-6", - "claude-sonnet-4-6", - "claude-haiku-4-5", - "claude-haiku-4-5-20251001", - "claude-opus-4-5", - "claude-opus-4-5-20251101", - "claude-sonnet-4-5", - "claude-sonnet-4-5-20250929", - "claude-opus-4-1", - "claude-opus-4-1-20250805", - "claude-opus-4-0", - "claude-opus-4-20250514", - "claude-sonnet-4-0", - "claude-sonnet-4-20250514", - "claude-3-haiku-20240307", - ], - str, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/model_capabilities.py b/.venv/lib/python3.12/site-packages/anthropic/types/model_capabilities.py deleted file mode 100644 index c7d69b61..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/model_capabilities.py +++ /dev/null @@ -1,40 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel -from .effort_capability import EffortCapability -from .capability_support import CapabilitySupport -from .thinking_capability import ThinkingCapability -from .context_management_capability import ContextManagementCapability - -__all__ = ["ModelCapabilities"] - - -class ModelCapabilities(BaseModel): - """Model capability information.""" - - batch: CapabilitySupport - """Whether the model supports the Batch API.""" - - citations: CapabilitySupport - """Whether the model supports citation generation.""" - - code_execution: CapabilitySupport - """Whether the model supports code execution tools.""" - - context_management: ContextManagementCapability - """Context management support and available strategies.""" - - effort: EffortCapability - """Effort (reasoning_effort) support and available levels.""" - - image_input: CapabilitySupport - """Whether the model accepts image content blocks.""" - - pdf_input: CapabilitySupport - """Whether the model accepts PDF content blocks.""" - - structured_outputs: CapabilitySupport - """Whether the model supports structured output / JSON mode / strict tool schemas.""" - - thinking: ThinkingCapability - """Thinking capability and supported type configurations.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/model_info.py b/.venv/lib/python3.12/site-packages/anthropic/types/model_info.py deleted file mode 100644 index 0e309be2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/model_info.py +++ /dev/null @@ -1,39 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from .._models import BaseModel -from .model_capabilities import ModelCapabilities - -__all__ = ["ModelInfo"] - - -class ModelInfo(BaseModel): - id: str - """Unique model identifier.""" - - capabilities: Optional[ModelCapabilities] = None - """Model capability information.""" - - created_at: datetime - """RFC 3339 datetime string representing the time at which the model was released. - - May be set to an epoch value if the release date is unknown. - """ - - display_name: str - """A human-readable name for the model.""" - - max_input_tokens: Optional[int] = None - """Maximum input context window size in tokens for this model.""" - - max_tokens: Optional[int] = None - """Maximum value for the `max_tokens` parameter when using this model.""" - - type: Literal["model"] - """Object type. - - For Models, this is always `"model"`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/model_list_params.py b/.venv/lib/python3.12/site-packages/anthropic/types/model_list_params.py deleted file mode 100644 index 9ef1a04c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/model_list_params.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List -from typing_extensions import Annotated, TypedDict - -from .._utils import PropertyInfo -from .anthropic_beta_param import AnthropicBetaParam - -__all__ = ["ModelListParams"] - - -class ModelListParams(TypedDict, total=False): - after_id: str - """ID of the object to use as a cursor for pagination. - - When provided, returns the page of results immediately after this object. - """ - - before_id: str - """ID of the object to use as a cursor for pagination. - - When provided, returns the page of results immediately before this object. - """ - - limit: int - """Number of items to return per page. - - Defaults to `20`. Ranges from `1` to `1000`. - """ - - betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] - """Optional header to specify the beta version(s) you want to use.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/model_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/model_param.py deleted file mode 100644 index 8e77f312..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/model_param.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Literal, TypeAlias - -__all__ = ["ModelParam"] - -ModelParam: TypeAlias = Union[ - Literal[ - "claude-opus-4-7", - "claude-mythos-preview", - "claude-opus-4-6", - "claude-sonnet-4-6", - "claude-haiku-4-5", - "claude-haiku-4-5-20251001", - "claude-opus-4-5", - "claude-opus-4-5-20251101", - "claude-sonnet-4-5", - "claude-sonnet-4-5-20250929", - "claude-opus-4-1", - "claude-opus-4-1-20250805", - "claude-opus-4-0", - "claude-opus-4-20250514", - "claude-sonnet-4-0", - "claude-sonnet-4-20250514", - "claude-3-haiku-20240307", - ], - str, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/output_config_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/output_config_param.py deleted file mode 100644 index 4c8fad0e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/output_config_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, TypedDict - -from .json_output_format_param import JSONOutputFormatParam - -__all__ = ["OutputConfigParam"] - - -class OutputConfigParam(TypedDict, total=False): - effort: Optional[Literal["low", "medium", "high", "xhigh", "max"]] - """All possible effort levels.""" - - format: Optional[JSONOutputFormatParam] - """A schema to specify Claude's output format in responses. - - See - [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/parsed_message.py b/.venv/lib/python3.12/site-packages/anthropic/types/parsed_message.py deleted file mode 100644 index 607e6e2d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/parsed_message.py +++ /dev/null @@ -1,56 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, List, Union, Generic, Optional -from typing_extensions import TypeVar, Annotated, TypeAlias - -from .._utils import PropertyInfo -from .message import Message -from .text_block import TextBlock -from .thinking_block import ThinkingBlock -from .tool_use_block import ToolUseBlock -from .server_tool_use_block import ServerToolUseBlock -from .redacted_thinking_block import RedactedThinkingBlock -from .web_search_tool_result_block import WebSearchToolResultBlock - -ResponseFormatT = TypeVar("ResponseFormatT", default=None) - - -__all__ = [ - "ParsedTextBlock", - "ParsedContentBlock", - "ParsedMessage", -] - - -class ParsedTextBlock(TextBlock, Generic[ResponseFormatT]): - parsed_output: Optional[ResponseFormatT] = None - - __api_exclude__ = {"parsed_output"} - - -# Note that generic unions are not valid for pydantic at runtime -ParsedContentBlock: TypeAlias = Annotated[ - Union[ - ParsedTextBlock[ResponseFormatT], - ThinkingBlock, - RedactedThinkingBlock, - ToolUseBlock, - ServerToolUseBlock, - WebSearchToolResultBlock, - ], - PropertyInfo(discriminator="type"), -] - - -class ParsedMessage(Message, Generic[ResponseFormatT]): - if TYPE_CHECKING: - content: List[ParsedContentBlock[ResponseFormatT]] # type: ignore[assignment] - else: - content: List[ParsedContentBlock] - - @property - def parsed_output(self) -> Optional[ResponseFormatT]: - for content in self.content: - if content.type == "text" and content.parsed_output: - return content.parsed_output - return None diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/plain_text_source.py b/.venv/lib/python3.12/site-packages/anthropic/types/plain_text_source.py deleted file mode 100644 index 36629264..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/plain_text_source.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["PlainTextSource"] - - -class PlainTextSource(BaseModel): - data: str - - media_type: Literal["text/plain"] - - type: Literal["text"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/plain_text_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/plain_text_source_param.py deleted file mode 100644 index a2a3b8de..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/plain_text_source_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["PlainTextSourceParam"] - - -class PlainTextSourceParam(TypedDict, total=False): - data: Required[str] - - media_type: Required[Literal["text/plain"]] - - type: Required[Literal["text"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_delta.py deleted file mode 100644 index 7ebe5a56..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_delta.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from .._utils import PropertyInfo -from .text_delta import TextDelta -from .thinking_delta import ThinkingDelta -from .citations_delta import CitationsDelta -from .signature_delta import SignatureDelta -from .input_json_delta import InputJSONDelta - -__all__ = ["RawContentBlockDelta"] - -RawContentBlockDelta: TypeAlias = Annotated[ - Union[TextDelta, InputJSONDelta, CitationsDelta, ThinkingDelta, SignatureDelta], PropertyInfo(discriminator="type") -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_delta_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_delta_event.py deleted file mode 100644 index 39a36e5e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_delta_event.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel -from .raw_content_block_delta import RawContentBlockDelta - -__all__ = ["RawContentBlockDeltaEvent"] - - -class RawContentBlockDeltaEvent(BaseModel): - delta: RawContentBlockDelta - - index: int - - type: Literal["content_block_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_start_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_start_event.py deleted file mode 100644 index 65b0b874..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_start_event.py +++ /dev/null @@ -1,48 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, Annotated, TypeAlias - -from .._utils import PropertyInfo -from .._models import BaseModel -from .text_block import TextBlock -from .thinking_block import ThinkingBlock -from .tool_use_block import ToolUseBlock -from .server_tool_use_block import ServerToolUseBlock -from .container_upload_block import ContainerUploadBlock -from .redacted_thinking_block import RedactedThinkingBlock -from .web_fetch_tool_result_block import WebFetchToolResultBlock -from .web_search_tool_result_block import WebSearchToolResultBlock -from .tool_search_tool_result_block import ToolSearchToolResultBlock -from .code_execution_tool_result_block import CodeExecutionToolResultBlock -from .bash_code_execution_tool_result_block import BashCodeExecutionToolResultBlock -from .text_editor_code_execution_tool_result_block import TextEditorCodeExecutionToolResultBlock - -__all__ = ["RawContentBlockStartEvent", "ContentBlock"] - -ContentBlock: TypeAlias = Annotated[ - Union[ - TextBlock, - ThinkingBlock, - RedactedThinkingBlock, - ToolUseBlock, - ServerToolUseBlock, - WebSearchToolResultBlock, - WebFetchToolResultBlock, - CodeExecutionToolResultBlock, - BashCodeExecutionToolResultBlock, - TextEditorCodeExecutionToolResultBlock, - ToolSearchToolResultBlock, - ContainerUploadBlock, - ], - PropertyInfo(discriminator="type"), -] - - -class RawContentBlockStartEvent(BaseModel): - content_block: ContentBlock - """Response model for a file uploaded to the container.""" - - index: int - - type: Literal["content_block_start"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_stop_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_stop_event.py deleted file mode 100644 index 6241a8b2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/raw_content_block_stop_event.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["RawContentBlockStopEvent"] - - -class RawContentBlockStopEvent(BaseModel): - index: int - - type: Literal["content_block_stop"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_delta_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_delta_event.py deleted file mode 100644 index c7915b60..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_delta_event.py +++ /dev/null @@ -1,51 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel -from .container import Container -from .stop_reason import StopReason -from .message_delta_usage import MessageDeltaUsage -from .refusal_stop_details import RefusalStopDetails - -__all__ = ["RawMessageDeltaEvent", "Delta"] - - -class Delta(BaseModel): - container: Optional[Container] = None - """ - Information about the container used in the request (for the code execution - tool) - """ - - stop_details: Optional[RefusalStopDetails] = None - """Structured information about a refusal.""" - - stop_reason: Optional[StopReason] = None - - stop_sequence: Optional[str] = None - - -class RawMessageDeltaEvent(BaseModel): - delta: Delta - - type: Literal["message_delta"] - - usage: MessageDeltaUsage - """Billing and rate-limit usage. - - Anthropic's API bills and rate-limits by token counts, as tokens represent the - underlying cost to our systems. - - Under the hood, the API transforms requests into a format suitable for the - model. The model's output then goes through a parsing stage before becoming an - API response. As a result, the token counts in `usage` will not match one-to-one - with the exact visible content of an API request or response. - - For example, `output_tokens` will be non-zero, even for an empty string response - from Claude. - - Total input tokens in a request is the summation of `input_tokens`, - `cache_creation_input_tokens`, and `cache_read_input_tokens`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_start_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_start_event.py deleted file mode 100644 index 1b9e8904..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_start_event.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .message import Message -from .._models import BaseModel - -__all__ = ["RawMessageStartEvent"] - - -class RawMessageStartEvent(BaseModel): - message: Message - - type: Literal["message_start"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_stop_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_stop_event.py deleted file mode 100644 index d40ccfe2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_stop_event.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["RawMessageStopEvent"] - - -class RawMessageStopEvent(BaseModel): - type: Literal["message_stop"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_stream_event.py b/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_stream_event.py deleted file mode 100644 index 728fbe88..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/raw_message_stream_event.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from .._utils import PropertyInfo -from .raw_message_stop_event import RawMessageStopEvent -from .raw_message_delta_event import RawMessageDeltaEvent -from .raw_message_start_event import RawMessageStartEvent -from .raw_content_block_stop_event import RawContentBlockStopEvent -from .raw_content_block_delta_event import RawContentBlockDeltaEvent -from .raw_content_block_start_event import RawContentBlockStartEvent - -__all__ = ["RawMessageStreamEvent"] - -RawMessageStreamEvent: TypeAlias = Annotated[ - Union[ - RawMessageStartEvent, - RawMessageDeltaEvent, - RawMessageStopEvent, - RawContentBlockStartEvent, - RawContentBlockDeltaEvent, - RawContentBlockStopEvent, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/redacted_thinking_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/redacted_thinking_block.py deleted file mode 100644 index 4850b335..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/redacted_thinking_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["RedactedThinkingBlock"] - - -class RedactedThinkingBlock(BaseModel): - data: str - - type: Literal["redacted_thinking"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/redacted_thinking_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/redacted_thinking_block_param.py deleted file mode 100644 index 0933188c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/redacted_thinking_block_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["RedactedThinkingBlockParam"] - - -class RedactedThinkingBlockParam(TypedDict, total=False): - data: Required[str] - - type: Required[Literal["redacted_thinking"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/refusal_stop_details.py b/.venv/lib/python3.12/site-packages/anthropic/types/refusal_stop_details.py deleted file mode 100644 index 1ff64aaa..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/refusal_stop_details.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["RefusalStopDetails"] - - -class RefusalStopDetails(BaseModel): - """Structured information about a refusal.""" - - category: Optional[Literal["cyber", "bio"]] = None - """The policy category that triggered the refusal. - - `null` when the refusal doesn't map to a named category. - """ - - explanation: Optional[str] = None - """Human-readable explanation of the refusal. - - This text is not guaranteed to be stable. `null` when no explanation is - available for the category. - """ - - type: Literal["refusal"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/search_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/search_result_block_param.py deleted file mode 100644 index 706e6c0a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/search_result_block_param.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .text_block_param import TextBlockParam -from .citations_config_param import CitationsConfigParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["SearchResultBlockParam"] - - -class SearchResultBlockParam(TypedDict, total=False): - content: Required[Iterable[TextBlockParam]] - - source: Required[str] - - title: Required[str] - - type: Required[Literal["search_result"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: CitationsConfigParam diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller.py b/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller.py deleted file mode 100644 index 7e59fd52..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["ServerToolCaller"] - - -class ServerToolCaller(BaseModel): - """Tool invocation generated by a server-side tool.""" - - tool_id: str - - type: Literal["code_execution_20250825"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller_20260120.py b/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller_20260120.py deleted file mode 100644 index 104f1b8b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller_20260120.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["ServerToolCaller20260120"] - - -class ServerToolCaller20260120(BaseModel): - tool_id: str - - type: Literal["code_execution_20260120"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller_20260120_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller_20260120_param.py deleted file mode 100644 index 26a2d79a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller_20260120_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ServerToolCaller20260120Param"] - - -class ServerToolCaller20260120Param(TypedDict, total=False): - tool_id: Required[str] - - type: Required[Literal["code_execution_20260120"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller_param.py deleted file mode 100644 index cea02648..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_caller_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ServerToolCallerParam"] - - -class ServerToolCallerParam(TypedDict, total=False): - """Tool invocation generated by a server-side tool.""" - - tool_id: Required[str] - - type: Required[Literal["code_execution_20250825"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_usage.py deleted file mode 100644 index 7381c4aa..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_usage.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel - -__all__ = ["ServerToolUsage"] - - -class ServerToolUsage(BaseModel): - web_fetch_requests: int - """The number of web fetch tool requests.""" - - web_search_requests: int - """The number of web search tool requests.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_use_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_use_block.py deleted file mode 100644 index 8434cfd8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_use_block.py +++ /dev/null @@ -1,37 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from .._utils import PropertyInfo -from .._models import BaseModel -from .direct_caller import DirectCaller -from .server_tool_caller import ServerToolCaller -from .server_tool_caller_20260120 import ServerToolCaller20260120 - -__all__ = ["ServerToolUseBlock", "Caller"] - -Caller: TypeAlias = Annotated[ - Union[DirectCaller, ServerToolCaller, ServerToolCaller20260120], PropertyInfo(discriminator="type") -] - - -class ServerToolUseBlock(BaseModel): - id: str - - caller: Optional[Caller] = None - """Tool invocation directly from the model.""" - - input: Dict[str, object] - - name: Literal[ - "web_search", - "web_fetch", - "code_execution", - "bash_code_execution", - "text_editor_code_execution", - "tool_search_tool_regex", - "tool_search_tool_bm25", - ] - - type: Literal["server_tool_use"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_use_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_use_block_param.py deleted file mode 100644 index 8f4452b8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/server_tool_use_block_param.py +++ /dev/null @@ -1,41 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .direct_caller_param import DirectCallerParam -from .server_tool_caller_param import ServerToolCallerParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam -from .server_tool_caller_20260120_param import ServerToolCaller20260120Param - -__all__ = ["ServerToolUseBlockParam", "Caller"] - -Caller: TypeAlias = Union[DirectCallerParam, ServerToolCallerParam, ServerToolCaller20260120Param] - - -class ServerToolUseBlockParam(TypedDict, total=False): - id: Required[str] - - input: Required[Dict[str, object]] - - name: Required[ - Literal[ - "web_search", - "web_fetch", - "code_execution", - "bash_code_execution", - "text_editor_code_execution", - "tool_search_tool_regex", - "tool_search_tool_bm25", - ] - ] - - type: Required[Literal["server_tool_use"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - caller: Caller - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/__init__.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/__init__.py deleted file mode 100644 index d519bae5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .error_type import ErrorType as ErrorType -from .error_object import ErrorObject as ErrorObject -from .billing_error import BillingError as BillingError -from .error_response import ErrorResponse as ErrorResponse -from .not_found_error import NotFoundError as NotFoundError -from .api_error_object import APIErrorObject as APIErrorObject -from .overloaded_error import OverloadedError as OverloadedError -from .permission_error import PermissionError as PermissionError -from .rate_limit_error import RateLimitError as RateLimitError -from .authentication_error import AuthenticationError as AuthenticationError -from .gateway_timeout_error import GatewayTimeoutError as GatewayTimeoutError -from .invalid_request_error import InvalidRequestError as InvalidRequestError diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/api_error_object.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/api_error_object.py deleted file mode 100644 index dd92bead..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/api_error_object.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["APIErrorObject"] - - -class APIErrorObject(BaseModel): - message: str - - type: Literal["api_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/authentication_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/authentication_error.py deleted file mode 100644 index f777f5c8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/authentication_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["AuthenticationError"] - - -class AuthenticationError(BaseModel): - message: str - - type: Literal["authentication_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/billing_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/billing_error.py deleted file mode 100644 index 26be12bb..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/billing_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["BillingError"] - - -class BillingError(BaseModel): - message: str - - type: Literal["billing_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/error_object.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/error_object.py deleted file mode 100644 index 086db503..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/error_object.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from ..._utils import PropertyInfo -from .billing_error import BillingError -from .not_found_error import NotFoundError -from .api_error_object import APIErrorObject -from .overloaded_error import OverloadedError -from .permission_error import PermissionError -from .rate_limit_error import RateLimitError -from .authentication_error import AuthenticationError -from .gateway_timeout_error import GatewayTimeoutError -from .invalid_request_error import InvalidRequestError - -__all__ = ["ErrorObject"] - -ErrorObject: TypeAlias = Annotated[ - Union[ - InvalidRequestError, - AuthenticationError, - BillingError, - PermissionError, - NotFoundError, - RateLimitError, - GatewayTimeoutError, - APIErrorObject, - OverloadedError, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/error_response.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/error_response.py deleted file mode 100644 index cc44ade2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/error_response.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from ..._models import BaseModel -from .error_object import ErrorObject - -__all__ = ["ErrorResponse"] - - -class ErrorResponse(BaseModel): - error: ErrorObject - - request_id: Optional[str] = None - - type: Literal["error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/error_type.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/error_type.py deleted file mode 100644 index f452d75b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/error_type.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["ErrorType"] - -ErrorType: TypeAlias = Literal[ - "invalid_request_error", - "authentication_error", - "permission_error", - "not_found_error", - "rate_limit_error", - "timeout_error", - "overloaded_error", - "api_error", - "billing_error", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/gateway_timeout_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/gateway_timeout_error.py deleted file mode 100644 index 908aa12f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/gateway_timeout_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["GatewayTimeoutError"] - - -class GatewayTimeoutError(BaseModel): - message: str - - type: Literal["timeout_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/invalid_request_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/invalid_request_error.py deleted file mode 100644 index ee5befc0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/invalid_request_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["InvalidRequestError"] - - -class InvalidRequestError(BaseModel): - message: str - - type: Literal["invalid_request_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/not_found_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/not_found_error.py deleted file mode 100644 index 43e826fb..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/not_found_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["NotFoundError"] - - -class NotFoundError(BaseModel): - message: str - - type: Literal["not_found_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/overloaded_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/overloaded_error.py deleted file mode 100644 index 74ee8373..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/overloaded_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["OverloadedError"] - - -class OverloadedError(BaseModel): - message: str - - type: Literal["overloaded_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/permission_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/permission_error.py deleted file mode 100644 index 48eb3546..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/permission_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["PermissionError"] - - -class PermissionError(BaseModel): - message: str - - type: Literal["permission_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/shared/rate_limit_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/shared/rate_limit_error.py deleted file mode 100644 index 3fa065ac..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/shared/rate_limit_error.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from ..._models import BaseModel - -__all__ = ["RateLimitError"] - - -class RateLimitError(BaseModel): - message: str - - type: Literal["rate_limit_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/signature_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/signature_delta.py deleted file mode 100644 index 55d15189..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/signature_delta.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["SignatureDelta"] - - -class SignatureDelta(BaseModel): - signature: str - - type: Literal["signature_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/stop_reason.py b/.venv/lib/python3.12/site-packages/anthropic/types/stop_reason.py deleted file mode 100644 index 3d371592..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/stop_reason.py +++ /dev/null @@ -1,7 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["StopReason"] - -StopReason: TypeAlias = Literal["end_turn", "max_tokens", "stop_sequence", "tool_use", "pause_turn", "refusal"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_block.py deleted file mode 100644 index ecdddb69..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_block.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import Literal - -from .._models import BaseModel -from .text_citation import TextCitation - -__all__ = ["TextBlock"] - - -class TextBlock(BaseModel): - citations: Optional[List[TextCitation]] = None - """Citations supporting the text block. - - The type of citation returned will depend on the type of document being cited. - Citing a PDF results in `page_location`, plain text results in `char_location`, - and content document results in `content_block_location`. - """ - - text: str - - type: Literal["text"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_block_param.py deleted file mode 100644 index 5a0f1215..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_block_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .text_citation_param import TextCitationParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["TextBlockParam"] - - -class TextBlockParam(TypedDict, total=False): - text: Required[str] - - type: Required[Literal["text"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: Optional[Iterable[TextCitationParam]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_citation.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_citation.py deleted file mode 100644 index a5a7ad53..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_citation.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Annotated, TypeAlias - -from .._utils import PropertyInfo -from .citation_char_location import CitationCharLocation -from .citation_page_location import CitationPageLocation -from .citation_content_block_location import CitationContentBlockLocation -from .citations_search_result_location import CitationsSearchResultLocation -from .citations_web_search_result_location import CitationsWebSearchResultLocation - -__all__ = ["TextCitation"] - -TextCitation: TypeAlias = Annotated[ - Union[ - CitationCharLocation, - CitationPageLocation, - CitationContentBlockLocation, - CitationsWebSearchResultLocation, - CitationsSearchResultLocation, - ], - PropertyInfo(discriminator="type"), -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_citation_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_citation_param.py deleted file mode 100644 index 3af3e8d3..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_citation_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .citation_char_location_param import CitationCharLocationParam -from .citation_page_location_param import CitationPageLocationParam -from .citation_content_block_location_param import CitationContentBlockLocationParam -from .citation_search_result_location_param import CitationSearchResultLocationParam -from .citation_web_search_result_location_param import CitationWebSearchResultLocationParam - -__all__ = ["TextCitationParam"] - -TextCitationParam: TypeAlias = Union[ - CitationCharLocationParam, - CitationPageLocationParam, - CitationContentBlockLocationParam, - CitationWebSearchResultLocationParam, - CitationSearchResultLocationParam, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_delta.py deleted file mode 100644 index 7ce96491..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_delta.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["TextDelta"] - - -class TextDelta(BaseModel): - text: str - - type: Literal["text_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_create_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_create_result_block.py deleted file mode 100644 index a9f68e30..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_create_result_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["TextEditorCodeExecutionCreateResultBlock"] - - -class TextEditorCodeExecutionCreateResultBlock(BaseModel): - is_file_update: bool - - type: Literal["text_editor_code_execution_create_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_create_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_create_result_block_param.py deleted file mode 100644 index f737cb50..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_create_result_block_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["TextEditorCodeExecutionCreateResultBlockParam"] - - -class TextEditorCodeExecutionCreateResultBlockParam(TypedDict, total=False): - is_file_update: Required[bool] - - type: Required[Literal["text_editor_code_execution_create_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_str_replace_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_str_replace_result_block.py deleted file mode 100644 index 8b8433f9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_str_replace_result_block.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["TextEditorCodeExecutionStrReplaceResultBlock"] - - -class TextEditorCodeExecutionStrReplaceResultBlock(BaseModel): - lines: Optional[List[str]] = None - - new_lines: Optional[int] = None - - new_start: Optional[int] = None - - old_lines: Optional[int] = None - - old_start: Optional[int] = None - - type: Literal["text_editor_code_execution_str_replace_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_str_replace_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_str_replace_result_block_param.py deleted file mode 100644 index 3e6e52e8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_str_replace_result_block_param.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .._types import SequenceNotStr - -__all__ = ["TextEditorCodeExecutionStrReplaceResultBlockParam"] - - -class TextEditorCodeExecutionStrReplaceResultBlockParam(TypedDict, total=False): - type: Required[Literal["text_editor_code_execution_str_replace_result"]] - - lines: Optional[SequenceNotStr[str]] - - new_lines: Optional[int] - - new_start: Optional[int] - - old_lines: Optional[int] - - old_start: Optional[int] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_block.py deleted file mode 100644 index fee85308..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_block.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, TypeAlias - -from .._models import BaseModel -from .text_editor_code_execution_tool_result_error import TextEditorCodeExecutionToolResultError -from .text_editor_code_execution_view_result_block import TextEditorCodeExecutionViewResultBlock -from .text_editor_code_execution_create_result_block import TextEditorCodeExecutionCreateResultBlock -from .text_editor_code_execution_str_replace_result_block import TextEditorCodeExecutionStrReplaceResultBlock - -__all__ = ["TextEditorCodeExecutionToolResultBlock", "Content"] - -Content: TypeAlias = Union[ - TextEditorCodeExecutionToolResultError, - TextEditorCodeExecutionViewResultBlock, - TextEditorCodeExecutionCreateResultBlock, - TextEditorCodeExecutionStrReplaceResultBlock, -] - - -class TextEditorCodeExecutionToolResultBlock(BaseModel): - content: Content - - tool_use_id: str - - type: Literal["text_editor_code_execution_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_block_param.py deleted file mode 100644 index 5173b2ab..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_block_param.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam -from .text_editor_code_execution_tool_result_error_param import TextEditorCodeExecutionToolResultErrorParam -from .text_editor_code_execution_view_result_block_param import TextEditorCodeExecutionViewResultBlockParam -from .text_editor_code_execution_create_result_block_param import TextEditorCodeExecutionCreateResultBlockParam -from .text_editor_code_execution_str_replace_result_block_param import TextEditorCodeExecutionStrReplaceResultBlockParam - -__all__ = ["TextEditorCodeExecutionToolResultBlockParam", "Content"] - -Content: TypeAlias = Union[ - TextEditorCodeExecutionToolResultErrorParam, - TextEditorCodeExecutionViewResultBlockParam, - TextEditorCodeExecutionCreateResultBlockParam, - TextEditorCodeExecutionStrReplaceResultBlockParam, -] - - -class TextEditorCodeExecutionToolResultBlockParam(TypedDict, total=False): - content: Required[Content] - - tool_use_id: Required[str] - - type: Required[Literal["text_editor_code_execution_tool_result"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_error.py deleted file mode 100644 index d4f23c8d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_error.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel -from .text_editor_code_execution_tool_result_error_code import TextEditorCodeExecutionToolResultErrorCode - -__all__ = ["TextEditorCodeExecutionToolResultError"] - - -class TextEditorCodeExecutionToolResultError(BaseModel): - error_code: TextEditorCodeExecutionToolResultErrorCode - - error_message: Optional[str] = None - - type: Literal["text_editor_code_execution_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_error_code.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_error_code.py deleted file mode 100644 index aabe5efe..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_error_code.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["TextEditorCodeExecutionToolResultErrorCode"] - -TextEditorCodeExecutionToolResultErrorCode: TypeAlias = Literal[ - "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "file_not_found" -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_error_param.py deleted file mode 100644 index fd1c2aba..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_tool_result_error_param.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .text_editor_code_execution_tool_result_error_code import TextEditorCodeExecutionToolResultErrorCode - -__all__ = ["TextEditorCodeExecutionToolResultErrorParam"] - - -class TextEditorCodeExecutionToolResultErrorParam(TypedDict, total=False): - error_code: Required[TextEditorCodeExecutionToolResultErrorCode] - - type: Required[Literal["text_editor_code_execution_tool_result_error"]] - - error_message: Optional[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_view_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_view_result_block.py deleted file mode 100644 index 87604c09..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_view_result_block.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["TextEditorCodeExecutionViewResultBlock"] - - -class TextEditorCodeExecutionViewResultBlock(BaseModel): - content: str - - file_type: Literal["text", "image", "pdf"] - - num_lines: Optional[int] = None - - start_line: Optional[int] = None - - total_lines: Optional[int] = None - - type: Literal["text_editor_code_execution_view_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_view_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_view_result_block_param.py deleted file mode 100644 index 9c84e247..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/text_editor_code_execution_view_result_block_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["TextEditorCodeExecutionViewResultBlockParam"] - - -class TextEditorCodeExecutionViewResultBlockParam(TypedDict, total=False): - content: Required[str] - - file_type: Required[Literal["text", "image", "pdf"]] - - type: Required[Literal["text_editor_code_execution_view_result"]] - - num_lines: Optional[int] - - start_line: Optional[int] - - total_lines: Optional[int] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/thinking_block.py deleted file mode 100644 index 7f98b500..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_block.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["ThinkingBlock"] - - -class ThinkingBlock(BaseModel): - signature: str - - thinking: str - - type: Literal["thinking"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/thinking_block_param.py deleted file mode 100644 index d310c7f6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_block_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ThinkingBlockParam"] - - -class ThinkingBlockParam(TypedDict, total=False): - signature: Required[str] - - thinking: Required[str] - - type: Required[Literal["thinking"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_capability.py b/.venv/lib/python3.12/site-packages/anthropic/types/thinking_capability.py deleted file mode 100644 index 83b3b5dc..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_capability.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel -from .thinking_types import ThinkingTypes - -__all__ = ["ThinkingCapability"] - - -class ThinkingCapability(BaseModel): - """Thinking capability details.""" - - supported: bool - """Whether this capability is supported by the model.""" - - types: ThinkingTypes - """Supported thinking type configurations.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_adaptive_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_adaptive_param.py deleted file mode 100644 index b80e26bf..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_adaptive_param.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ThinkingConfigAdaptiveParam"] - - -class ThinkingConfigAdaptiveParam(TypedDict, total=False): - type: Required[Literal["adaptive"]] - - display: Optional[Literal["summarized", "omitted"]] - """Controls how thinking content appears in the response. - - When set to `summarized`, thinking is returned normally. When set to `omitted`, - thinking content is redacted but a signature is returned for multi-turn - continuity. Defaults to `summarized`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_disabled_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_disabled_param.py deleted file mode 100644 index 23b5fbad..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_disabled_param.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ThinkingConfigDisabledParam"] - - -class ThinkingConfigDisabledParam(TypedDict, total=False): - type: Required[Literal["disabled"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_enabled_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_enabled_param.py deleted file mode 100644 index 7b971181..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_enabled_param.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ThinkingConfigEnabledParam"] - - -class ThinkingConfigEnabledParam(TypedDict, total=False): - budget_tokens: Required[int] - """Determines how many tokens Claude can use for its internal reasoning process. - - Larger budgets can enable more thorough analysis for complex problems, improving - response quality. - - Must be ≥1024 and less than `max_tokens`. - - See - [extended thinking](https://docs.claude.com/en/docs/build-with-claude/extended-thinking) - for details. - """ - - type: Required[Literal["enabled"]] - - display: Optional[Literal["summarized", "omitted"]] - """Controls how thinking content appears in the response. - - When set to `summarized`, thinking is returned normally. When set to `omitted`, - thinking content is redacted but a signature is returned for multi-turn - continuity. Defaults to `summarized`. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_param.py deleted file mode 100644 index c9979b1a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_config_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .thinking_config_enabled_param import ThinkingConfigEnabledParam -from .thinking_config_adaptive_param import ThinkingConfigAdaptiveParam -from .thinking_config_disabled_param import ThinkingConfigDisabledParam - -__all__ = ["ThinkingConfigParam"] - -ThinkingConfigParam: TypeAlias = Union[ - ThinkingConfigEnabledParam, ThinkingConfigDisabledParam, ThinkingConfigAdaptiveParam -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_delta.py b/.venv/lib/python3.12/site-packages/anthropic/types/thinking_delta.py deleted file mode 100644 index fb79933c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_delta.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["ThinkingDelta"] - - -class ThinkingDelta(BaseModel): - thinking: str - - type: Literal["thinking_delta"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_types.py b/.venv/lib/python3.12/site-packages/anthropic/types/thinking_types.py deleted file mode 100644 index e0b9e5f9..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/thinking_types.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel -from .capability_support import CapabilitySupport - -__all__ = ["ThinkingTypes"] - - -class ThinkingTypes(BaseModel): - """Supported thinking type configurations.""" - - adaptive: CapabilitySupport - """Whether the model supports thinking with type 'adaptive' (auto).""" - - enabled: CapabilitySupport - """Whether the model supports thinking with type 'enabled'.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_bash_20250124_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_bash_20250124_param.py deleted file mode 100644 index a913c018..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_bash_20250124_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ToolBash20250124Param"] - - -class ToolBash20250124Param(TypedDict, total=False): - name: Required[Literal["bash"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["bash_20250124"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_any_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_any_param.py deleted file mode 100644 index 99ae76cd..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_any_param.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ToolChoiceAnyParam"] - - -class ToolChoiceAnyParam(TypedDict, total=False): - """The model will use any available tools.""" - - type: Required[Literal["any"]] - - disable_parallel_tool_use: bool - """Whether to disable parallel tool use. - - Defaults to `false`. If set to `true`, the model will output exactly one tool - use. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_auto_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_auto_param.py deleted file mode 100644 index 26636d5c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_auto_param.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ToolChoiceAutoParam"] - - -class ToolChoiceAutoParam(TypedDict, total=False): - """The model will automatically decide whether to use tools.""" - - type: Required[Literal["auto"]] - - disable_parallel_tool_use: bool - """Whether to disable parallel tool use. - - Defaults to `false`. If set to `true`, the model will output at most one tool - use. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_none_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_none_param.py deleted file mode 100644 index e185cc22..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_none_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ToolChoiceNoneParam"] - - -class ToolChoiceNoneParam(TypedDict, total=False): - """The model will not be allowed to use tools.""" - - type: Required[Literal["none"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_param.py deleted file mode 100644 index 868277d4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .tool_choice_any_param import ToolChoiceAnyParam -from .tool_choice_auto_param import ToolChoiceAutoParam -from .tool_choice_none_param import ToolChoiceNoneParam -from .tool_choice_tool_param import ToolChoiceToolParam - -__all__ = ["ToolChoiceParam"] - -ToolChoiceParam: TypeAlias = Union[ToolChoiceAutoParam, ToolChoiceAnyParam, ToolChoiceToolParam, ToolChoiceNoneParam] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_tool_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_tool_param.py deleted file mode 100644 index 2c1b1d1c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_choice_tool_param.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["ToolChoiceToolParam"] - - -class ToolChoiceToolParam(TypedDict, total=False): - """The model will use the specified tool with `tool_choice.name`.""" - - name: Required[str] - """The name of the tool to use.""" - - type: Required[Literal["tool"]] - - disable_parallel_tool_use: bool - """Whether to disable parallel tool use. - - Defaults to `false`. If set to `true`, the model will output exactly one tool - use. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_param.py deleted file mode 100644 index 04a93cb6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_param.py +++ /dev/null @@ -1,82 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Union, Iterable, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .._types import SequenceNotStr -from .._models import set_pydantic_config -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ToolParam", "InputSchema"] - - -class InputSchemaTyped(TypedDict, total=False): - """[JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. - - This defines the shape of the `input` that your tool accepts and that the model will produce. - """ - - type: Required[Literal["object"]] - - properties: Optional[Dict[str, object]] - - required: Optional[SequenceNotStr[str]] - - -set_pydantic_config(InputSchemaTyped, {"extra": "allow"}) - -InputSchema: TypeAlias = Union[InputSchemaTyped, Dict[str, object]] - - -class ToolParam(TypedDict, total=False): - input_schema: Required[InputSchema] - """[JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. - - This defines the shape of the `input` that your tool accepts and that the model - will produce. - """ - - name: Required[str] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - description: str - """Description of what this tool does. - - Tool descriptions should be as detailed as possible. The more information that - the model has about what the tool is and how to use it, the better it will - perform. You can use natural language descriptions to reinforce important - aspects of the tool input JSON schema. - """ - - eager_input_streaming: Optional[bool] - """Enable eager input streaming for this tool. - - When true, tool input parameters will be streamed incrementally as they are - generated, and types will be inferred on-the-fly rather than buffering the full - JSON output. When false, streaming is disabled for this tool even if the - fine-grained-tool-streaming beta is active. When null (default), uses the - default behavior based on beta headers. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" - - type: Optional[Literal["custom"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_reference_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_reference_block.py deleted file mode 100644 index e5837589..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_reference_block.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["ToolReferenceBlock"] - - -class ToolReferenceBlock(BaseModel): - tool_name: str - - type: Literal["tool_reference"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_reference_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_reference_block_param.py deleted file mode 100644 index 5fd7ea90..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_reference_block_param.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ToolReferenceBlockParam"] - - -class ToolReferenceBlockParam(TypedDict, total=False): - """Tool reference block that can be included in tool_result content.""" - - tool_name: Required[str] - - type: Required[Literal["tool_reference"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_result_block_param.py deleted file mode 100644 index 90364bd6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_result_block_param.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .text_block_param import TextBlockParam -from .image_block_param import ImageBlockParam -from .document_block_param import DocumentBlockParam -from .search_result_block_param import SearchResultBlockParam -from .tool_reference_block_param import ToolReferenceBlockParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ToolResultBlockParam", "Content"] - -Content: TypeAlias = Union[ - TextBlockParam, ImageBlockParam, SearchResultBlockParam, DocumentBlockParam, ToolReferenceBlockParam -] - - -class ToolResultBlockParam(TypedDict, total=False): - tool_use_id: Required[str] - - type: Required[Literal["tool_result"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - content: Union[str, Iterable[Content]] - - is_error: bool diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_bm25_20251119_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_bm25_20251119_param.py deleted file mode 100644 index 163053f2..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_bm25_20251119_param.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ToolSearchToolBm25_20251119Param"] - - -class ToolSearchToolBm25_20251119Param(TypedDict, total=False): - name: Required[Literal["tool_search_tool_bm25"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["tool_search_tool_bm25_20251119", "tool_search_tool_bm25"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_regex_20251119_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_regex_20251119_param.py deleted file mode 100644 index f1b0291d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_regex_20251119_param.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ToolSearchToolRegex20251119Param"] - - -class ToolSearchToolRegex20251119Param(TypedDict, total=False): - name: Required[Literal["tool_search_tool_regex"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["tool_search_tool_regex_20251119", "tool_search_tool_regex"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_block.py deleted file mode 100644 index df05bb39..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_block.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union -from typing_extensions import Literal, TypeAlias - -from .._models import BaseModel -from .tool_search_tool_result_error import ToolSearchToolResultError -from .tool_search_tool_search_result_block import ToolSearchToolSearchResultBlock - -__all__ = ["ToolSearchToolResultBlock", "Content"] - -Content: TypeAlias = Union[ToolSearchToolResultError, ToolSearchToolSearchResultBlock] - - -class ToolSearchToolResultBlock(BaseModel): - content: Content - - tool_use_id: str - - type: Literal["tool_search_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_block_param.py deleted file mode 100644 index 4907c72c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_block_param.py +++ /dev/null @@ -1,25 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam -from .tool_search_tool_result_error_param import ToolSearchToolResultErrorParam -from .tool_search_tool_search_result_block_param import ToolSearchToolSearchResultBlockParam - -__all__ = ["ToolSearchToolResultBlockParam", "Content"] - -Content: TypeAlias = Union[ToolSearchToolResultErrorParam, ToolSearchToolSearchResultBlockParam] - - -class ToolSearchToolResultBlockParam(TypedDict, total=False): - content: Required[Content] - - tool_use_id: Required[str] - - type: Required[Literal["tool_search_tool_result"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_error.py deleted file mode 100644 index 2686ba3e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_error.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel -from .tool_search_tool_result_error_code import ToolSearchToolResultErrorCode - -__all__ = ["ToolSearchToolResultError"] - - -class ToolSearchToolResultError(BaseModel): - error_code: ToolSearchToolResultErrorCode - - error_message: Optional[str] = None - - type: Literal["tool_search_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_error_code.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_error_code.py deleted file mode 100644 index 19047e03..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_error_code.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["ToolSearchToolResultErrorCode"] - -ToolSearchToolResultErrorCode: TypeAlias = Literal[ - "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded" -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_error_param.py deleted file mode 100644 index 0095839b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_result_error_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .tool_search_tool_result_error_code import ToolSearchToolResultErrorCode - -__all__ = ["ToolSearchToolResultErrorParam"] - - -class ToolSearchToolResultErrorParam(TypedDict, total=False): - error_code: Required[ToolSearchToolResultErrorCode] - - type: Required[Literal["tool_search_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_search_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_search_result_block.py deleted file mode 100644 index f3d235ef..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_search_result_block.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List -from typing_extensions import Literal - -from .._models import BaseModel -from .tool_reference_block import ToolReferenceBlock - -__all__ = ["ToolSearchToolSearchResultBlock"] - - -class ToolSearchToolSearchResultBlock(BaseModel): - tool_references: List[ToolReferenceBlock] - - type: Literal["tool_search_tool_search_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_search_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_search_result_block_param.py deleted file mode 100644 index ed248234..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_search_tool_search_result_block_param.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Iterable -from typing_extensions import Literal, Required, TypedDict - -from .tool_reference_block_param import ToolReferenceBlockParam - -__all__ = ["ToolSearchToolSearchResultBlockParam"] - - -class ToolSearchToolSearchResultBlockParam(TypedDict, total=False): - tool_references: Required[Iterable[ToolReferenceBlockParam]] - - type: Required[Literal["tool_search_tool_search_result"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_text_editor_20250124_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_text_editor_20250124_param.py deleted file mode 100644 index cdb9a64b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_text_editor_20250124_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ToolTextEditor20250124Param"] - - -class ToolTextEditor20250124Param(TypedDict, total=False): - name: Required[Literal["str_replace_editor"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["text_editor_20250124"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_text_editor_20250429_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_text_editor_20250429_param.py deleted file mode 100644 index 22ddfd62..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_text_editor_20250429_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ToolTextEditor20250429Param"] - - -class ToolTextEditor20250429Param(TypedDict, total=False): - name: Required[Literal["str_replace_based_edit_tool"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["text_editor_20250429"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_text_editor_20250728_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_text_editor_20250728_param.py deleted file mode 100644 index 239e0b11..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_text_editor_20250728_param.py +++ /dev/null @@ -1,42 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, List, Iterable, Optional -from typing_extensions import Literal, Required, TypedDict - -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["ToolTextEditor20250728Param"] - - -class ToolTextEditor20250728Param(TypedDict, total=False): - name: Required[Literal["str_replace_based_edit_tool"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["text_editor_20250728"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - input_examples: Iterable[Dict[str, object]] - - max_characters: Optional[int] - """Maximum number of characters to display when viewing a file. - - If not specified, defaults to displaying the full file. - """ - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_union_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_union_param.py deleted file mode 100644 index b74efaba..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_union_param.py +++ /dev/null @@ -1,44 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import TypeAlias - -from .tool_param import ToolParam -from .tool_bash_20250124_param import ToolBash20250124Param -from .memory_tool_20250818_param import MemoryTool20250818Param -from .web_fetch_tool_20250910_param import WebFetchTool20250910Param -from .web_fetch_tool_20260209_param import WebFetchTool20260209Param -from .web_fetch_tool_20260309_param import WebFetchTool20260309Param -from .web_search_tool_20250305_param import WebSearchTool20250305Param -from .web_search_tool_20260209_param import WebSearchTool20260209Param -from .tool_text_editor_20250124_param import ToolTextEditor20250124Param -from .tool_text_editor_20250429_param import ToolTextEditor20250429Param -from .tool_text_editor_20250728_param import ToolTextEditor20250728Param -from .code_execution_tool_20250522_param import CodeExecutionTool20250522Param -from .code_execution_tool_20250825_param import CodeExecutionTool20250825Param -from .code_execution_tool_20260120_param import CodeExecutionTool20260120Param -from .tool_search_tool_bm25_20251119_param import ToolSearchToolBm25_20251119Param -from .tool_search_tool_regex_20251119_param import ToolSearchToolRegex20251119Param - -__all__ = ["ToolUnionParam"] - -ToolUnionParam: TypeAlias = Union[ - ToolParam, - ToolBash20250124Param, - CodeExecutionTool20250522Param, - CodeExecutionTool20250825Param, - CodeExecutionTool20260120Param, - MemoryTool20250818Param, - ToolTextEditor20250124Param, - ToolTextEditor20250429Param, - ToolTextEditor20250728Param, - WebSearchTool20250305Param, - WebFetchTool20250910Param, - WebSearchTool20260209Param, - WebFetchTool20260209Param, - WebFetchTool20260309Param, - ToolSearchToolBm25_20251119Param, - ToolSearchToolRegex20251119Param, -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_use_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_use_block.py deleted file mode 100644 index 99ac7f2b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_use_block.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from .._utils import PropertyInfo -from .._models import BaseModel -from .direct_caller import DirectCaller -from .server_tool_caller import ServerToolCaller -from .server_tool_caller_20260120 import ServerToolCaller20260120 - -__all__ = ["ToolUseBlock", "Caller"] - -Caller: TypeAlias = Annotated[ - Union[DirectCaller, ServerToolCaller, ServerToolCaller20260120], PropertyInfo(discriminator="type") -] - - -class ToolUseBlock(BaseModel): - id: str - - caller: Optional[Caller] = None - """Tool invocation directly from the model.""" - - input: Dict[str, object] - - name: str - - type: Literal["tool_use"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/tool_use_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/tool_use_block_param.py deleted file mode 100644 index e673195a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/tool_use_block_param.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .direct_caller_param import DirectCallerParam -from .server_tool_caller_param import ServerToolCallerParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam -from .server_tool_caller_20260120_param import ServerToolCaller20260120Param - -__all__ = ["ToolUseBlockParam", "Caller"] - -Caller: TypeAlias = Union[DirectCallerParam, ServerToolCallerParam, ServerToolCaller20260120Param] - - -class ToolUseBlockParam(TypedDict, total=False): - id: Required[str] - - input: Required[Dict[str, object]] - - name: Required[str] - - type: Required[Literal["tool_use"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - caller: Caller - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/url_image_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/url_image_source_param.py deleted file mode 100644 index 852b8eee..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/url_image_source_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["URLImageSourceParam"] - - -class URLImageSourceParam(TypedDict, total=False): - type: Required[Literal["url"]] - - url: Required[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/url_pdf_source_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/url_pdf_source_param.py deleted file mode 100644 index b5321d56..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/url_pdf_source_param.py +++ /dev/null @@ -1,13 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["URLPDFSourceParam"] - - -class URLPDFSourceParam(TypedDict, total=False): - type: Required[Literal["url"]] - - url: Required[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/usage.py b/.venv/lib/python3.12/site-packages/anthropic/types/usage.py deleted file mode 100644 index f5ff005b..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/usage.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel -from .cache_creation import CacheCreation -from .server_tool_usage import ServerToolUsage - -__all__ = ["Usage"] - - -class Usage(BaseModel): - cache_creation: Optional[CacheCreation] = None - """Breakdown of cached tokens by TTL""" - - cache_creation_input_tokens: Optional[int] = None - """The number of input tokens used to create the cache entry.""" - - cache_read_input_tokens: Optional[int] = None - """The number of input tokens read from the cache.""" - - inference_geo: Optional[str] = None - """The geographic region where inference was performed for this request.""" - - input_tokens: int - """The number of input tokens which were used.""" - - output_tokens: int - """The number of output tokens which were used.""" - - server_tool_use: Optional[ServerToolUsage] = None - """The number of server tool requests.""" - - service_tier: Optional[Literal["standard", "priority", "batch"]] = None - """If the request used the priority, standard, or batch tier.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/user_location_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/user_location_param.py deleted file mode 100644 index b83daef4..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/user_location_param.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["UserLocationParam"] - - -class UserLocationParam(TypedDict, total=False): - type: Required[Literal["approximate"]] - - city: Optional[str] - """The city of the user.""" - - country: Optional[str] - """ - The two letter - [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the - user. - """ - - region: Optional[str] - """The region of the user.""" - - timezone: Optional[str] - """The [IANA timezone](https://nodatime.org/TimeZones) of the user.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_block.py deleted file mode 100644 index d1cd7669..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_block.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel -from .document_block import DocumentBlock - -__all__ = ["WebFetchBlock"] - - -class WebFetchBlock(BaseModel): - content: DocumentBlock - - retrieved_at: Optional[str] = None - """ISO 8601 timestamp when the content was retrieved""" - - type: Literal["web_fetch_result"] - - url: str - """Fetched content URL""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_block_param.py deleted file mode 100644 index 00f1181a..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_block_param.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -from .document_block_param import DocumentBlockParam - -__all__ = ["WebFetchBlockParam"] - - -class WebFetchBlockParam(TypedDict, total=False): - content: Required[DocumentBlockParam] - - type: Required[Literal["web_fetch_result"]] - - url: Required[str] - """Fetched content URL""" - - retrieved_at: Optional[str] - """ISO 8601 timestamp when the content was retrieved""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_20250910_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_20250910_param.py deleted file mode 100644 index 88b72913..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_20250910_param.py +++ /dev/null @@ -1,57 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .._types import SequenceNotStr -from .citations_config_param import CitationsConfigParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["WebFetchTool20250910Param"] - - -class WebFetchTool20250910Param(TypedDict, total=False): - name: Required[Literal["web_fetch"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["web_fetch_20250910"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - allowed_domains: Optional[SequenceNotStr[str]] - """List of domains to allow fetching from""" - - blocked_domains: Optional[SequenceNotStr[str]] - """List of domains to block fetching from""" - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: Optional[CitationsConfigParam] - """Citations configuration for fetched documents. - - Citations are disabled by default. - """ - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_content_tokens: Optional[int] - """Maximum number of tokens used by including web page text content in the context. - - The limit is approximate and does not apply to binary content such as PDFs. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_20260209_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_20260209_param.py deleted file mode 100644 index c2155cc5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_20260209_param.py +++ /dev/null @@ -1,57 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .._types import SequenceNotStr -from .citations_config_param import CitationsConfigParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["WebFetchTool20260209Param"] - - -class WebFetchTool20260209Param(TypedDict, total=False): - name: Required[Literal["web_fetch"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["web_fetch_20260209"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - allowed_domains: Optional[SequenceNotStr[str]] - """List of domains to allow fetching from""" - - blocked_domains: Optional[SequenceNotStr[str]] - """List of domains to block fetching from""" - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: Optional[CitationsConfigParam] - """Citations configuration for fetched documents. - - Citations are disabled by default. - """ - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_content_tokens: Optional[int] - """Maximum number of tokens used by including web page text content in the context. - - The limit is approximate and does not apply to binary content such as PDFs. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_20260309_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_20260309_param.py deleted file mode 100644 index 207f3135..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_20260309_param.py +++ /dev/null @@ -1,67 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .._types import SequenceNotStr -from .citations_config_param import CitationsConfigParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["WebFetchTool20260309Param"] - - -class WebFetchTool20260309Param(TypedDict, total=False): - """Web fetch tool with use_cache parameter for bypassing cached content.""" - - name: Required[Literal["web_fetch"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["web_fetch_20260309"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - allowed_domains: Optional[SequenceNotStr[str]] - """List of domains to allow fetching from""" - - blocked_domains: Optional[SequenceNotStr[str]] - """List of domains to block fetching from""" - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - citations: Optional[CitationsConfigParam] - """Citations configuration for fetched documents. - - Citations are disabled by default. - """ - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_content_tokens: Optional[int] - """Maximum number of tokens used by including web page text content in the context. - - The limit is approximate and does not apply to binary content such as PDFs. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" - - use_cache: bool - """Whether to use cached content. - - Set to false to bypass the cache and fetch fresh content. Only set to false when - the user explicitly requests fresh content or when fetching rapidly-changing - sources. - """ diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_block.py deleted file mode 100644 index 2a71286c..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_block.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from .._utils import PropertyInfo -from .._models import BaseModel -from .direct_caller import DirectCaller -from .web_fetch_block import WebFetchBlock -from .server_tool_caller import ServerToolCaller -from .server_tool_caller_20260120 import ServerToolCaller20260120 -from .web_fetch_tool_result_error_block import WebFetchToolResultErrorBlock - -__all__ = ["WebFetchToolResultBlock", "Caller", "Content"] - -Caller: TypeAlias = Annotated[ - Union[DirectCaller, ServerToolCaller, ServerToolCaller20260120], PropertyInfo(discriminator="type") -] - -Content: TypeAlias = Union[WebFetchToolResultErrorBlock, WebFetchBlock] - - -class WebFetchToolResultBlock(BaseModel): - caller: Optional[Caller] = None - """Tool invocation directly from the model.""" - - content: Content - - tool_use_id: str - - type: Literal["web_fetch_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_block_param.py deleted file mode 100644 index 28d55289..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_block_param.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .direct_caller_param import DirectCallerParam -from .web_fetch_block_param import WebFetchBlockParam -from .server_tool_caller_param import ServerToolCallerParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam -from .server_tool_caller_20260120_param import ServerToolCaller20260120Param -from .web_fetch_tool_result_error_block_param import WebFetchToolResultErrorBlockParam - -__all__ = ["WebFetchToolResultBlockParam", "Content", "Caller"] - -Content: TypeAlias = Union[WebFetchToolResultErrorBlockParam, WebFetchBlockParam] - -Caller: TypeAlias = Union[DirectCallerParam, ServerToolCallerParam, ServerToolCaller20260120Param] - - -class WebFetchToolResultBlockParam(TypedDict, total=False): - content: Required[Content] - - tool_use_id: Required[str] - - type: Required[Literal["web_fetch_tool_result"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - caller: Caller - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_error_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_error_block.py deleted file mode 100644 index 98d7f0e8..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_error_block.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel -from .web_fetch_tool_result_error_code import WebFetchToolResultErrorCode - -__all__ = ["WebFetchToolResultErrorBlock"] - - -class WebFetchToolResultErrorBlock(BaseModel): - error_code: WebFetchToolResultErrorCode - - type: Literal["web_fetch_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_error_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_error_block_param.py deleted file mode 100644 index 9b8c08f0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_error_block_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .web_fetch_tool_result_error_code import WebFetchToolResultErrorCode - -__all__ = ["WebFetchToolResultErrorBlockParam"] - - -class WebFetchToolResultErrorBlockParam(TypedDict, total=False): - error_code: Required[WebFetchToolResultErrorCode] - - type: Required[Literal["web_fetch_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_error_code.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_error_code.py deleted file mode 100644 index 6dced09f..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_fetch_tool_result_error_code.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["WebFetchToolResultErrorCode"] - -WebFetchToolResultErrorCode: TypeAlias = Literal[ - "invalid_tool_input", - "url_too_long", - "url_not_allowed", - "url_not_accessible", - "unsupported_content_type", - "too_many_requests", - "max_uses_exceeded", - "unavailable", -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_result_block.py deleted file mode 100644 index 21d36aba..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_result_block.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["WebSearchResultBlock"] - - -class WebSearchResultBlock(BaseModel): - encrypted_content: str - - page_age: Optional[str] = None - - title: str - - type: Literal["web_search_result"] - - url: str diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_result_block_param.py deleted file mode 100644 index 1950c710..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_result_block_param.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Optional -from typing_extensions import Literal, Required, TypedDict - -__all__ = ["WebSearchResultBlockParam"] - - -class WebSearchResultBlockParam(TypedDict, total=False): - encrypted_content: Required[str] - - title: Required[str] - - type: Required[Literal["web_search_result"]] - - url: Required[str] - - page_age: Optional[str] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_20250305_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_20250305_param.py deleted file mode 100644 index ecbdca1e..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_20250305_param.py +++ /dev/null @@ -1,60 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .._types import SequenceNotStr -from .user_location_param import UserLocationParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["WebSearchTool20250305Param", "UserLocation"] - - -class WebSearchTool20250305Param(TypedDict, total=False): - name: Required[Literal["web_search"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["web_search_20250305"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - allowed_domains: Optional[SequenceNotStr[str]] - """If provided, only these domains will be included in results. - - Cannot be used alongside `blocked_domains`. - """ - - blocked_domains: Optional[SequenceNotStr[str]] - """If provided, these domains will never appear in results. - - Cannot be used alongside `allowed_domains`. - """ - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" - - user_location: Optional[UserLocationParam] - """Parameters for the user's location. - - Used to provide more relevant search results. - """ - - -UserLocation = UserLocationParam # backward compat alias diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_20260209_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_20260209_param.py deleted file mode 100644 index 7bf62f39..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_20260209_param.py +++ /dev/null @@ -1,60 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import List, Optional -from typing_extensions import Literal, Required, TypedDict - -from .._types import SequenceNotStr -from .user_location_param import UserLocationParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam - -__all__ = ["WebSearchTool20260209Param", "UserLocation"] - - -class WebSearchTool20260209Param(TypedDict, total=False): - name: Required[Literal["web_search"]] - """Name of the tool. - - This is how the tool will be called by the model and in `tool_use` blocks. - """ - - type: Required[Literal["web_search_20260209"]] - - allowed_callers: List[Literal["direct", "code_execution_20250825", "code_execution_20260120"]] - - allowed_domains: Optional[SequenceNotStr[str]] - """If provided, only these domains will be included in results. - - Cannot be used alongside `blocked_domains`. - """ - - blocked_domains: Optional[SequenceNotStr[str]] - """If provided, these domains will never appear in results. - - Cannot be used alongside `allowed_domains`. - """ - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - defer_loading: bool - """If true, tool will not be included in initial system prompt. - - Only loaded when returned via tool_reference from tool search. - """ - - max_uses: Optional[int] - """Maximum number of times the tool can be used in the API request.""" - - strict: bool - """When true, guarantees schema validation on tool names and inputs""" - - user_location: Optional[UserLocationParam] - """Parameters for the user's location. - - Used to provide more relevant search results. - """ - - -UserLocation = UserLocationParam # backward compat alias diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_request_error_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_request_error_param.py deleted file mode 100644 index fcbc62c5..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_request_error_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, TypedDict - -from .web_search_tool_result_error_code import WebSearchToolResultErrorCode - -__all__ = ["WebSearchToolRequestErrorParam"] - - -class WebSearchToolRequestErrorParam(TypedDict, total=False): - error_code: Required[WebSearchToolResultErrorCode] - - type: Required[Literal["web_search_tool_result_error"]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block.py deleted file mode 100644 index a6be581d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Union, Optional -from typing_extensions import Literal, Annotated, TypeAlias - -from .._utils import PropertyInfo -from .._models import BaseModel -from .direct_caller import DirectCaller -from .server_tool_caller import ServerToolCaller -from .server_tool_caller_20260120 import ServerToolCaller20260120 -from .web_search_tool_result_block_content import WebSearchToolResultBlockContent - -__all__ = ["WebSearchToolResultBlock", "Caller"] - -Caller: TypeAlias = Annotated[ - Union[DirectCaller, ServerToolCaller, ServerToolCaller20260120], PropertyInfo(discriminator="type") -] - - -class WebSearchToolResultBlock(BaseModel): - caller: Optional[Caller] = None - """Tool invocation directly from the model.""" - - content: WebSearchToolResultBlockContent - - tool_use_id: str - - type: Literal["web_search_tool_result"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block_content.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block_content.py deleted file mode 100644 index c617e2ee..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block_content.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Union -from typing_extensions import TypeAlias - -from .web_search_result_block import WebSearchResultBlock -from .web_search_tool_result_error import WebSearchToolResultError - -__all__ = ["WebSearchToolResultBlockContent"] - -WebSearchToolResultBlockContent: TypeAlias = Union[WebSearchToolResultError, List[WebSearchResultBlock]] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block_param.py deleted file mode 100644 index c7840e2d..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block_param.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Optional -from typing_extensions import Literal, Required, TypeAlias, TypedDict - -from .direct_caller_param import DirectCallerParam -from .server_tool_caller_param import ServerToolCallerParam -from .cache_control_ephemeral_param import CacheControlEphemeralParam -from .server_tool_caller_20260120_param import ServerToolCaller20260120Param -from .web_search_tool_result_block_param_content_param import WebSearchToolResultBlockParamContentParam - -__all__ = ["WebSearchToolResultBlockParam", "Caller"] - -Caller: TypeAlias = Union[DirectCallerParam, ServerToolCallerParam, ServerToolCaller20260120Param] - - -class WebSearchToolResultBlockParam(TypedDict, total=False): - content: Required[WebSearchToolResultBlockParamContentParam] - - tool_use_id: Required[str] - - type: Required[Literal["web_search_tool_result"]] - - cache_control: Optional[CacheControlEphemeralParam] - """Create a cache control breakpoint at this content block.""" - - caller: Caller - """Tool invocation directly from the model.""" diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block_param_content_param.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block_param_content_param.py deleted file mode 100644 index 0d9c63f6..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_block_param_content_param.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union, Iterable -from typing_extensions import TypeAlias - -from .web_search_result_block_param import WebSearchResultBlockParam -from .web_search_tool_request_error_param import WebSearchToolRequestErrorParam - -__all__ = ["WebSearchToolResultBlockParamContentParam"] - -WebSearchToolResultBlockParamContentParam: TypeAlias = Union[ - Iterable[WebSearchResultBlockParam], WebSearchToolRequestErrorParam -] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_error.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_error.py deleted file mode 100644 index 90a93dd0..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_error.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel -from .web_search_tool_result_error_code import WebSearchToolResultErrorCode - -__all__ = ["WebSearchToolResultError"] - - -class WebSearchToolResultError(BaseModel): - error_code: WebSearchToolResultErrorCode - - type: Literal["web_search_tool_result_error"] diff --git a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_error_code.py b/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_error_code.py deleted file mode 100644 index 09148c73..00000000 --- a/.venv/lib/python3.12/site-packages/anthropic/types/web_search_tool_result_error_code.py +++ /dev/null @@ -1,9 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["WebSearchToolResultErrorCode"] - -WebSearchToolResultErrorCode: TypeAlias = Literal[ - "invalid_tool_input", "unavailable", "max_uses_exceeded", "too_many_requests", "query_too_long", "request_too_large" -] diff --git a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/METADATA b/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/METADATA deleted file mode 100644 index 73ee4910..00000000 --- a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/METADATA +++ /dev/null @@ -1,107 +0,0 @@ -Metadata-Version: 2.4 -Name: anyio -Version: 4.14.1 -Summary: High-level concurrency and networking framework on top of asyncio or Trio -Author-email: Alex Grönholm -License-Expression: MIT -Project-URL: Documentation, https://anyio.readthedocs.io/en/latest/ -Project-URL: Changelog, https://anyio.readthedocs.io/en/stable/versionhistory.html -Project-URL: Source code, https://github.com/agronholm/anyio -Project-URL: Issue tracker, https://github.com/agronholm/anyio/issues -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Framework :: AnyIO -Classifier: Typing :: Typed -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Programming Language :: Python :: 3.15 -Requires-Python: >=3.10 -Description-Content-Type: text/x-rst -License-File: LICENSE -Requires-Dist: exceptiongroup>=1.0.2; python_version < "3.11" -Requires-Dist: idna>=2.8 -Requires-Dist: typing_extensions>=4.5; python_version < "3.13" -Provides-Extra: trio -Requires-Dist: trio>=0.32.0; extra == "trio" -Dynamic: license-file - -.. image:: https://github.com/agronholm/anyio/actions/workflows/test.yml/badge.svg - :target: https://github.com/agronholm/anyio/actions/workflows/test.yml - :alt: Build Status -.. image:: https://coveralls.io/repos/github/agronholm/anyio/badge.svg?branch=master - :target: https://coveralls.io/github/agronholm/anyio?branch=master - :alt: Code Coverage -.. image:: https://readthedocs.org/projects/anyio/badge/?version=latest - :target: https://anyio.readthedocs.io/en/latest/?badge=latest - :alt: Documentation -.. image:: https://badges.gitter.im/gitterHQ/gitter.svg - :target: https://gitter.im/python-trio/AnyIO - :alt: Gitter chat -.. image:: https://tidelift.com/badges/package/pypi/anyio - :target: https://tidelift.com/subscription/pkg/pypi-anyio - :alt: Tidelift - -AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio_ or -Trio_. It implements Trio-like `structured concurrency`_ (SC) on top of asyncio and works in harmony -with the native SC of Trio itself. - -Applications and libraries written against AnyIO's API will run unmodified on either asyncio_ or -Trio_. AnyIO can also be adopted into a library or application incrementally – bit by bit, no full -refactoring necessary. It will blend in with the native libraries of your chosen backend. - -To find out why you might want to use AnyIO's APIs instead of asyncio's, you can read about it -`here `_. - -Documentation -------------- - -View full documentation at: https://anyio.readthedocs.io/ - -Features --------- - -AnyIO offers the following functionality: - -* Task groups (nurseries_ in trio terminology) -* High-level networking (TCP, UDP and UNIX sockets) - - * `Happy eyeballs`_ algorithm for TCP connections (more robust than that of asyncio on Python - 3.8) - * async/await style UDP sockets (unlike asyncio where you still have to use Transports and - Protocols) - -* A versatile API for byte streams and object streams -* Inter-task synchronization and communication (locks, conditions, events, semaphores, object - streams) -* Worker threads -* Subprocesses -* Subinterpreter support for code parallelization (on Python 3.13 and later) -* Asynchronous file I/O (using worker threads) -* Signal handling -* Asynchronous versions of the functools_ and itertools_ modules - -AnyIO also comes with its own pytest_ plugin which also supports asynchronous fixtures. -It even works with the popular Hypothesis_ library. - -.. _asyncio: https://docs.python.org/3/library/asyncio.html -.. _Trio: https://github.com/python-trio/trio -.. _structured concurrency: https://en.wikipedia.org/wiki/Structured_concurrency -.. _nurseries: https://trio.readthedocs.io/en/stable/reference-core.html#nurseries-and-spawning -.. _Happy eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs -.. _pytest: https://docs.pytest.org/en/latest/ -.. _functools: https://docs.python.org/3/library/functools.html -.. _itertools: https://docs.python.org/3/library/itertools.html -.. _Hypothesis: https://hypothesis.works/ - -Security contact information ----------------------------- - -To report a security vulnerability, please use the `Tidelift security contact`_. -Tidelift will coordinate the fix and disclosure. - -.. _Tidelift security contact: https://tidelift.com/security diff --git a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/RECORD b/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/RECORD deleted file mode 100644 index a8480b87..00000000 --- a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/RECORD +++ /dev/null @@ -1,96 +0,0 @@ -anyio-4.14.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -anyio-4.14.1.dist-info/METADATA,sha256=bfkjYaZLYPsPI5JV_Gn7HYF65mteyE8nhjaI0ZqC4L4,4645 -anyio-4.14.1.dist-info/RECORD,, -anyio-4.14.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91 -anyio-4.14.1.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39 -anyio-4.14.1.dist-info/licenses/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081 -anyio-4.14.1.dist-info/scm_file_list.json,sha256=wDSXGv8Ehn5ZW5BhB-RlaAc16zY_OfO27qrlMfMMZy8,3654 -anyio-4.14.1.dist-info/scm_version.json,sha256=gw22Q2aBbdiYhyMbObTYNN7BN-wSpzOCktNiAuulRN8,161 -anyio-4.14.1.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6 -anyio/__init__.py,sha256=HitUIfzvAojSeaHVmJ9rFn8k_yI63G6s_jUL2QChf4U,6405 -anyio/__pycache__/__init__.cpython-312.pyc,, -anyio/__pycache__/from_thread.cpython-312.pyc,, -anyio/__pycache__/functools.cpython-312.pyc,, -anyio/__pycache__/itertools.cpython-312.pyc,, -anyio/__pycache__/lowlevel.cpython-312.pyc,, -anyio/__pycache__/pytest_plugin.cpython-312.pyc,, -anyio/__pycache__/to_interpreter.cpython-312.pyc,, -anyio/__pycache__/to_process.cpython-312.pyc,, -anyio/__pycache__/to_thread.cpython-312.pyc,, -anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -anyio/_backends/__pycache__/__init__.cpython-312.pyc,, -anyio/_backends/__pycache__/_asyncio.cpython-312.pyc,, -anyio/_backends/__pycache__/_trio.cpython-312.pyc,, -anyio/_backends/_asyncio.py,sha256=-q-5gUYg_r5SsN-OYbQnF_lvtW0v51-dFlsU8_gduWA,102077 -anyio/_backends/_trio.py,sha256=vR0ZgxVnOo4AHhHcHVG0worMc-3ZNpAZ6Vxh0m0ZZC0,45189 -anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -anyio/_core/__pycache__/__init__.cpython-312.pyc,, -anyio/_core/__pycache__/_asyncio_selector_thread.cpython-312.pyc,, -anyio/_core/__pycache__/_contextmanagers.cpython-312.pyc,, -anyio/_core/__pycache__/_eventloop.cpython-312.pyc,, -anyio/_core/__pycache__/_exceptions.cpython-312.pyc,, -anyio/_core/__pycache__/_fileio.cpython-312.pyc,, -anyio/_core/__pycache__/_resources.cpython-312.pyc,, -anyio/_core/__pycache__/_signals.cpython-312.pyc,, -anyio/_core/__pycache__/_sockets.cpython-312.pyc,, -anyio/_core/__pycache__/_streams.cpython-312.pyc,, -anyio/_core/__pycache__/_subprocesses.cpython-312.pyc,, -anyio/_core/__pycache__/_synchronization.cpython-312.pyc,, -anyio/_core/__pycache__/_tasks.cpython-312.pyc,, -anyio/_core/__pycache__/_tempfile.cpython-312.pyc,, -anyio/_core/__pycache__/_testing.cpython-312.pyc,, -anyio/_core/__pycache__/_typedattr.cpython-312.pyc,, -anyio/_core/_asyncio_selector_thread.py,sha256=2PdxFM3cs02Kp6BSppbvmRT7q7asreTW5FgBxEsflBo,5626 -anyio/_core/_contextmanagers.py,sha256=YInBCabiEeS-UaP_Jdxa1CaFC71ETPW8HZTHIM8Rsc8,7215 -anyio/_core/_eventloop.py,sha256=ByZUeJD9alMfcyTseRo5IzTO0IltEul_Gyq9iqSjqDk,6658 -anyio/_core/_exceptions.py,sha256=OfzLO4Z3Hog1TnipbIn72YNtkoYxS4lHW9MqKDeGc88,4936 -anyio/_core/_fileio.py,sha256=hHfyV0bXDL-R2ZNnInwse3nmTAd36AIz1cBxgmAwzAQ,31358 -anyio/_core/_resources.py,sha256=NbmU5O5UX3xEyACnkmYX28Fmwdl-f-ny0tHym26e0w0,435 -anyio/_core/_signals.py,sha256=mjTBB2hTKNPRlU0IhnijeQedpWOGERDiMjSlJQsFrug,1016 -anyio/_core/_sockets.py,sha256=9FU423j52XBBfGVr6MdzPTdyw8bGrzApZ5m338-AtsY,35286 -anyio/_core/_streams.py,sha256=FczFwIgDpnkK0bODWJXMpsUJYdvAD04kaUaGzJU8DK0,1806 -anyio/_core/_subprocesses.py,sha256=tkmkPKEkEaiMD8C9WRZBlmgjOYRDRbZdte6e-unay2E,7916 -anyio/_core/_synchronization.py,sha256=jn2nIbTRlBAUXL-mx_a3I_VnasF8GbVFpBRp2-YwCx0,21591 -anyio/_core/_tasks.py,sha256=ELL2jscaSW0Jw_xA6MtQlm3xwvFEzjTbc1u9Tteyt0I,13244 -anyio/_core/_tempfile.py,sha256=jE2w59FRF3yRo4vjkjfZF2YcqsBZvc66VWRwrJGDYGk,19624 -anyio/_core/_testing.py,sha256=u7MPqGXwpTxqI7hclSdNA30z2GH1Nw258uwKvy_RfBg,2340 -anyio/_core/_typedattr.py,sha256=P4ozZikn3-DbpoYcvyghS_FOYAgbmUxeoU8-L_07pZM,2508 -anyio/abc/__init__.py,sha256=6mWhcl_pGXhrgZVHP_TCfMvIXIOp9mroEFM90fYCU_U,2869 -anyio/abc/__pycache__/__init__.cpython-312.pyc,, -anyio/abc/__pycache__/_eventloop.cpython-312.pyc,, -anyio/abc/__pycache__/_resources.cpython-312.pyc,, -anyio/abc/__pycache__/_sockets.cpython-312.pyc,, -anyio/abc/__pycache__/_streams.cpython-312.pyc,, -anyio/abc/__pycache__/_subprocesses.cpython-312.pyc,, -anyio/abc/__pycache__/_tasks.cpython-312.pyc,, -anyio/abc/__pycache__/_testing.cpython-312.pyc,, -anyio/abc/_eventloop.py,sha256=OqWYSEj0TmwL_xniCJt3_jHFWsuMk9THk8tCTGsKapI,10681 -anyio/abc/_resources.py,sha256=DrYvkNN1hH6Uvv5_5uKySvDsnknGVDe8FCKfko0VtN8,783 -anyio/abc/_sockets.py,sha256=OmVDrfemVvF9c5K1tpBgQyV6fn5v0XyCExLAqBOGz9o,13124 -anyio/abc/_streams.py,sha256=HYvna1iZbWcwLROTO6IhLX79RTRLPShZMWe0sG1q54I,7481 -anyio/abc/_subprocesses.py,sha256=cumAPJTktOQtw63IqG0lDpyZqu_l1EElvQHMiwJgL08,2067 -anyio/abc/_tasks.py,sha256=m-FtE4phxeNIELSG7A3H7VUz3jA2Ib5J2JIew8-PS6o,6642 -anyio/abc/_testing.py,sha256=9YYM2AXsYFvf4PLjUEr6yRxDiUeB5QbY_gOg0X_C6lY,2034 -anyio/from_thread.py,sha256=JYsbaCaIB_Iit6kNhtXSteJGt4PcQ7ncq0nIpcelIrg,19265 -anyio/functools.py,sha256=T4JS8IXq-x1S0Lbo2owF8l9fza2KypO147QLeyz4cjs,11797 -anyio/itertools.py,sha256=QV-9mnRCr2yBph8g01QFvN-bQ_Yle-8Sl13YSydBlMI,16168 -anyio/lowlevel.py,sha256=WPtppHfI2qs1nokzjn8elL8LvyqI05AK5Zslhlo71A4,6242 -anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -anyio/pytest_plugin.py,sha256=paMpI_VMNQf2bir0LfvgMpXSiYJoHDzWdKUVTyoHmvQ,13609 -anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -anyio/streams/__pycache__/__init__.cpython-312.pyc,, -anyio/streams/__pycache__/buffered.cpython-312.pyc,, -anyio/streams/__pycache__/file.cpython-312.pyc,, -anyio/streams/__pycache__/memory.cpython-312.pyc,, -anyio/streams/__pycache__/stapled.cpython-312.pyc,, -anyio/streams/__pycache__/text.cpython-312.pyc,, -anyio/streams/__pycache__/tls.cpython-312.pyc,, -anyio/streams/buffered.py,sha256=v3xKtjFHgNV41g2SvMAkA_qd2t9WYlCI1_lNGCAatw0,6650 -anyio/streams/file.py,sha256=msnrotVKGMQomUu_Rj2qz9MvIdUp6d3JGr7MOEO8kV4,4428 -anyio/streams/memory.py,sha256=F0zwzvFJKAhX_LRZGoKzzqDC2oMM-f-yyTBrEYEGOaU,10740 -anyio/streams/stapled.py,sha256=T8Xqwf8K6EgURPxbt1N4i7A8BAk-gScv-GRhjLXIf_o,4390 -anyio/streams/text.py,sha256=BcVAGJw1VRvtIqnv-o0Rb0pwH7p8vwlvl21xHq522ag,5765 -anyio/streams/tls.py,sha256=DQVkXUvsTEYKkBO8dlVU7j_5H8QOtLy4sGi1Wrjqevo,15303 -anyio/to_interpreter.py,sha256=_mLngrMy97TMR6VbW4Y6YzDUk9ZuPcQMPlkuyRh3C9k,7100 -anyio/to_process.py,sha256=68qhLfce7MeXysid4fOpmhfWkgdo7Z7-9BC0VyUciIE,9809 -anyio/to_thread.py,sha256=f6h_k2d743GBv9FhAnhM_YpTvWgIrzBy9cOE0eJ1UJw,2693 diff --git a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/WHEEL deleted file mode 100644 index 14a883f2..00000000 --- a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (82.0.1) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/entry_points.txt deleted file mode 100644 index 44dd9bdc..00000000 --- a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[pytest11] -anyio = anyio.pytest_plugin diff --git a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/licenses/LICENSE deleted file mode 100644 index 104eebf5..00000000 --- a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/licenses/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2018 Alex Grönholm - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/scm_file_list.json b/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/scm_file_list.json deleted file mode 100644 index 72a48145..00000000 --- a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/scm_file_list.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "files": [ - ".pre-commit-config.yaml", - "LICENSE", - "pyproject.toml", - "AGENTS.md", - "README.rst", - "CLAUDE.md", - ".readthedocs.yml", - ".gitignore", - "docs/tempfile.rst", - "docs/signals.rst", - "docs/synchronization.rst", - "docs/contextmanagers.rst", - "docs/testing.rst", - "docs/networking.rst", - "docs/contributing.rst", - "docs/index.rst", - "docs/versionhistory.rst", - "docs/threads.rst", - "docs/api.rst", - "docs/typedattrs.rst", - "docs/basics.rst", - "docs/fileio.rst", - "docs/cancellation.rst", - "docs/support.rst", - "docs/streams.rst", - "docs/why.rst", - "docs/tasks.rst", - "docs/migration.rst", - "docs/conf.py", - "docs/subprocesses.rst", - "docs/faq.rst", - "docs/subinterpreters.rst", - "src/anyio/functools.py", - "src/anyio/py.typed", - "src/anyio/__init__.py", - "src/anyio/pytest_plugin.py", - "src/anyio/itertools.py", - "src/anyio/to_interpreter.py", - "src/anyio/from_thread.py", - "src/anyio/to_process.py", - "src/anyio/to_thread.py", - "src/anyio/lowlevel.py", - "src/anyio/_backends/_trio.py", - "src/anyio/_backends/__init__.py", - "src/anyio/_backends/_asyncio.py", - "src/anyio/streams/memory.py", - "src/anyio/streams/__init__.py", - "src/anyio/streams/tls.py", - "src/anyio/streams/file.py", - "src/anyio/streams/text.py", - "src/anyio/streams/stapled.py", - "src/anyio/streams/buffered.py", - "src/anyio/abc/_eventloop.py", - "src/anyio/abc/__init__.py", - "src/anyio/abc/_sockets.py", - "src/anyio/abc/_tasks.py", - "src/anyio/abc/_subprocesses.py", - "src/anyio/abc/_resources.py", - "src/anyio/abc/_streams.py", - "src/anyio/abc/_testing.py", - "src/anyio/_core/_typedattr.py", - "src/anyio/_core/_eventloop.py", - "src/anyio/_core/__init__.py", - "src/anyio/_core/_tempfile.py", - "src/anyio/_core/_sockets.py", - "src/anyio/_core/_tasks.py", - "src/anyio/_core/_fileio.py", - "src/anyio/_core/_synchronization.py", - "src/anyio/_core/_subprocesses.py", - "src/anyio/_core/_resources.py", - "src/anyio/_core/_contextmanagers.py", - "src/anyio/_core/_exceptions.py", - "src/anyio/_core/_streams.py", - "src/anyio/_core/_signals.py", - "src/anyio/_core/_asyncio_selector_thread.py", - "src/anyio/_core/_testing.py", - "tests/test_itertools.py", - "tests/test_functools.py", - "tests/test_eventloop.py", - "tests/__init__.py", - "tests/test_to_thread.py", - "tests/test_from_thread.py", - "tests/test_lowlevel.py", - "tests/test_to_interpreter.py", - "tests/test_sockets.py", - "tests/test_typedattr.py", - "tests/test_to_process.py", - "tests/test_all_attributes.py", - "tests/test_synchronization.py", - "tests/test_debugging.py", - "tests/test_contextmanagers.py", - "tests/test_fileio.py", - "tests/conftest.py", - "tests/test_signals.py", - "tests/test_deprecations.py", - "tests/test_tempfile.py", - "tests/test_taskgroups.py", - "tests/test_pytest_plugin.py", - "tests/test_subprocesses.py", - "tests/streams/test_text.py", - "tests/streams/test_memory.py", - "tests/streams/__init__.py", - "tests/streams/test_file.py", - "tests/streams/test_stapled.py", - "tests/streams/test_tls.py", - "tests/streams/test_buffered.py", - ".github/pull_request_template.md", - ".github/dependabot.yml", - ".github/FUNDING.yml", - ".github/ISSUE_TEMPLATE/features_request.yaml", - ".github/ISSUE_TEMPLATE/bug_report.yaml", - ".github/ISSUE_TEMPLATE/config.yml", - ".github/workflows/test.yml", - ".github/workflows/test-downstream.yml", - ".github/workflows/publish.yml" - ] -} diff --git a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/scm_version.json b/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/scm_version.json deleted file mode 100644 index 17978b35..00000000 --- a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/scm_version.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "tag": "4.14.1", - "distance": 0, - "node": "g149b9e907618fadf6840a4d3cebad533b0c7d033", - "dirty": false, - "branch": "HEAD", - "node_date": "2026-06-24" -} diff --git a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/top_level.txt deleted file mode 100644 index c77c069e..00000000 --- a/.venv/lib/python3.12/site-packages/anyio-4.14.1.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -anyio diff --git a/.venv/lib/python3.12/site-packages/anyio/__init__.py b/.venv/lib/python3.12/site-packages/anyio/__init__.py deleted file mode 100644 index 2502c760..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/__init__.py +++ /dev/null @@ -1,115 +0,0 @@ -from __future__ import annotations - -from ._core._contextmanagers import AsyncContextManagerMixin as AsyncContextManagerMixin -from ._core._contextmanagers import ContextManagerMixin as ContextManagerMixin -from ._core._eventloop import current_time as current_time -from ._core._eventloop import get_all_backends as get_all_backends -from ._core._eventloop import get_available_backends as get_available_backends -from ._core._eventloop import get_cancelled_exc_class as get_cancelled_exc_class -from ._core._eventloop import run as run -from ._core._eventloop import sleep as sleep -from ._core._eventloop import sleep_forever as sleep_forever -from ._core._eventloop import sleep_until as sleep_until -from ._core._exceptions import BrokenResourceError as BrokenResourceError -from ._core._exceptions import BrokenWorkerInterpreter as BrokenWorkerInterpreter -from ._core._exceptions import BrokenWorkerProcess as BrokenWorkerProcess -from ._core._exceptions import BusyResourceError as BusyResourceError -from ._core._exceptions import ClosedResourceError as ClosedResourceError -from ._core._exceptions import ConnectionFailed as ConnectionFailed -from ._core._exceptions import DelimiterNotFound as DelimiterNotFound -from ._core._exceptions import EndOfStream as EndOfStream -from ._core._exceptions import IncompleteRead as IncompleteRead -from ._core._exceptions import NoEventLoopError as NoEventLoopError -from ._core._exceptions import RunFinishedError as RunFinishedError -from ._core._exceptions import TaskCancelled as TaskCancelled -from ._core._exceptions import TaskFailed as TaskFailed -from ._core._exceptions import TaskNotFinished as TaskNotFinished -from ._core._exceptions import TypedAttributeLookupError as TypedAttributeLookupError -from ._core._exceptions import WouldBlock as WouldBlock -from ._core._fileio import AsyncFile as AsyncFile -from ._core._fileio import Path as Path -from ._core._fileio import open_file as open_file -from ._core._fileio import wrap_file as wrap_file -from ._core._resources import aclose_forcefully as aclose_forcefully -from ._core._signals import open_signal_receiver as open_signal_receiver -from ._core._sockets import TCPConnectable as TCPConnectable -from ._core._sockets import UNIXConnectable as UNIXConnectable -from ._core._sockets import as_connectable as as_connectable -from ._core._sockets import connect_tcp as connect_tcp -from ._core._sockets import connect_unix as connect_unix -from ._core._sockets import create_connected_udp_socket as create_connected_udp_socket -from ._core._sockets import ( - create_connected_unix_datagram_socket as create_connected_unix_datagram_socket, -) -from ._core._sockets import create_tcp_listener as create_tcp_listener -from ._core._sockets import create_udp_socket as create_udp_socket -from ._core._sockets import create_unix_datagram_socket as create_unix_datagram_socket -from ._core._sockets import create_unix_listener as create_unix_listener -from ._core._sockets import getaddrinfo as getaddrinfo -from ._core._sockets import getnameinfo as getnameinfo -from ._core._sockets import notify_closing as notify_closing -from ._core._sockets import wait_readable as wait_readable -from ._core._sockets import wait_socket_readable as wait_socket_readable -from ._core._sockets import wait_socket_writable as wait_socket_writable -from ._core._sockets import wait_writable as wait_writable -from ._core._streams import create_memory_object_stream as create_memory_object_stream -from ._core._subprocesses import open_process as open_process -from ._core._subprocesses import run_process as run_process -from ._core._synchronization import CapacityLimiter as CapacityLimiter -from ._core._synchronization import ( - CapacityLimiterStatistics as CapacityLimiterStatistics, -) -from ._core._synchronization import Condition as Condition -from ._core._synchronization import ConditionStatistics as ConditionStatistics -from ._core._synchronization import Event as Event -from ._core._synchronization import EventStatistics as EventStatistics -from ._core._synchronization import Lock as Lock -from ._core._synchronization import LockStatistics as LockStatistics -from ._core._synchronization import ResourceGuard as ResourceGuard -from ._core._synchronization import Semaphore as Semaphore -from ._core._synchronization import SemaphoreStatistics as SemaphoreStatistics -from ._core._tasks import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED -from ._core._tasks import CancelScope as CancelScope -from ._core._tasks import TaskHandle as TaskHandle -from ._core._tasks import create_task_group as create_task_group -from ._core._tasks import current_effective_deadline as current_effective_deadline -from ._core._tasks import fail_after as fail_after -from ._core._tasks import move_on_after as move_on_after -from ._core._tempfile import NamedTemporaryFile as NamedTemporaryFile -from ._core._tempfile import SpooledTemporaryFile as SpooledTemporaryFile -from ._core._tempfile import TemporaryDirectory as TemporaryDirectory -from ._core._tempfile import TemporaryFile as TemporaryFile -from ._core._tempfile import gettempdir as gettempdir -from ._core._tempfile import gettempdirb as gettempdirb -from ._core._tempfile import mkdtemp as mkdtemp -from ._core._tempfile import mkstemp as mkstemp -from ._core._testing import TaskInfo as TaskInfo -from ._core._testing import get_current_task as get_current_task -from ._core._testing import get_running_tasks as get_running_tasks -from ._core._testing import wait_all_tasks_blocked as wait_all_tasks_blocked -from ._core._typedattr import TypedAttributeProvider as TypedAttributeProvider -from ._core._typedattr import TypedAttributeSet as TypedAttributeSet -from ._core._typedattr import typed_attribute as typed_attribute - -# Re-export imports so they look like they live directly in this package -for __value in list(locals().values()): - if getattr(__value, "__module__", "").startswith("anyio."): - __value.__module__ = __name__ - - -del __value - - -def __getattr__(attr: str) -> type[BrokenWorkerInterpreter]: - """Support deprecated aliases.""" - if attr == "BrokenWorkerIntepreter": - import warnings - - warnings.warn( - "The 'BrokenWorkerIntepreter' alias is deprecated, use 'BrokenWorkerInterpreter' instead.", - DeprecationWarning, - stacklevel=2, - ) - return BrokenWorkerInterpreter - - raise AttributeError(f"module {__name__!r} has no attribute {attr!r}") diff --git a/.venv/lib/python3.12/site-packages/anyio/_backends/__init__.py b/.venv/lib/python3.12/site-packages/anyio/_backends/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py b/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py deleted file mode 100644 index e6fd9555..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_backends/_asyncio.py +++ /dev/null @@ -1,3077 +0,0 @@ -from __future__ import annotations - -import array -import asyncio -import concurrent.futures -import contextvars -import math -import os -import socket -import sys -import threading -import weakref -from asyncio import ( - AbstractEventLoop, - CancelledError, - all_tasks, - create_task, - current_task, - get_running_loop, - sleep, -) -from asyncio.base_events import _run_until_complete_cb # type: ignore[attr-defined] -from collections import OrderedDict, deque -from collections.abc import ( - AsyncGenerator, - AsyncIterator, - Awaitable, - Callable, - Collection, - Coroutine, - Iterable, - Sequence, -) -from concurrent.futures import Future -from contextlib import AbstractContextManager -from contextvars import Context, copy_context -from dataclasses import dataclass, field -from functools import partial, wraps -from inspect import ( - CORO_RUNNING, - CORO_SUSPENDED, - getcoroutinestate, -) -from io import IOBase -from os import PathLike -from queue import Queue -from signal import Signals -from socket import AddressFamily, SocketKind -from threading import Thread -from types import CodeType, TracebackType -from typing import ( - IO, - TYPE_CHECKING, - Any, - Literal, - ParamSpec, - TypeVar, - cast, -) -from weakref import WeakKeyDictionary - -from .. import ( - CapacityLimiterStatistics, - EventStatistics, - LockStatistics, - TaskInfo, - abc, -) -from .._core._eventloop import ( - claim_worker_thread, - set_current_async_library, - threadlocals, -) -from .._core._exceptions import ( - BrokenResourceError, - BusyResourceError, - ClosedResourceError, - EndOfStream, - RunFinishedError, - WouldBlock, -) -from .._core._sockets import convert_ipv6_sockaddr -from .._core._streams import create_memory_object_stream -from .._core._synchronization import ( - CapacityLimiter as BaseCapacityLimiter, -) -from .._core._synchronization import Event as BaseEvent -from .._core._synchronization import Lock as BaseLock -from .._core._synchronization import ( - ResourceGuard, - SemaphoreStatistics, -) -from .._core._synchronization import Semaphore as BaseSemaphore -from .._core._tasks import CancelScope as BaseCancelScope -from .._core._tasks import TaskHandle -from ..abc import ( - AsyncBackend, - IPSockAddrType, - SocketListener, - UDPPacketType, - UNIXDatagramPacketType, -) -from ..abc._eventloop import StrOrBytesPath -from ..abc._tasks import call_for_coroutine, get_callable_name -from ..lowlevel import RunVar -from ..streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream - -if TYPE_CHECKING: - from _typeshed import FileDescriptorLike -else: - FileDescriptorLike = object - -if sys.version_info >= (3, 11): - from asyncio import Runner - from typing import TypeVarTuple, Unpack -else: - import contextvars - import enum - import signal - from asyncio import coroutines, events, exceptions, tasks - - from exceptiongroup import BaseExceptionGroup - from typing_extensions import TypeVarTuple, Unpack - - class _State(enum.Enum): - CREATED = "created" - INITIALIZED = "initialized" - CLOSED = "closed" - - class Runner: - # Copied from CPython 3.11 - def __init__( - self, - *, - debug: bool | None = None, - loop_factory: Callable[[], AbstractEventLoop] | None = None, - ): - self._state = _State.CREATED - self._debug = debug - self._loop_factory = loop_factory - self._loop: AbstractEventLoop | None = None - self._context = None - self._interrupt_count = 0 - self._set_event_loop = False - - def __enter__(self) -> Runner: - self._lazy_init() - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.close() - - def close(self) -> None: - """Shutdown and close event loop.""" - loop = self._loop - if self._state is not _State.INITIALIZED or loop is None: - return - try: - _cancel_all_tasks(loop) - loop.run_until_complete(loop.shutdown_asyncgens()) - if hasattr(loop, "shutdown_default_executor"): - loop.run_until_complete(loop.shutdown_default_executor()) - else: - loop.run_until_complete(_shutdown_default_executor(loop)) - finally: - if self._set_event_loop: - events.set_event_loop(None) - loop.close() - self._loop = None - self._state = _State.CLOSED - - def get_loop(self) -> AbstractEventLoop: - """Return embedded event loop.""" - self._lazy_init() - return self._loop - - def run(self, coro: Coroutine[T_Retval], *, context=None) -> T_Retval: - """Run a coroutine inside the embedded event loop.""" - if not coroutines.iscoroutine(coro): - raise ValueError(f"a coroutine was expected, got {coro!r}") - - if events._get_running_loop() is not None: - # fail fast with short traceback - raise RuntimeError( - "Runner.run() cannot be called from a running event loop" - ) - - self._lazy_init() - - if context is None: - context = self._context - task = context.run(self._loop.create_task, coro) - - if ( - threading.current_thread() is threading.main_thread() - and signal.getsignal(signal.SIGINT) is signal.default_int_handler - ): - sigint_handler = partial(self._on_sigint, main_task=task) - try: - signal.signal(signal.SIGINT, sigint_handler) - except ValueError: - # `signal.signal` may throw if `threading.main_thread` does - # not support signals (e.g. embedded interpreter with signals - # not registered - see gh-91880) - sigint_handler = None - else: - sigint_handler = None - - self._interrupt_count = 0 - try: - return self._loop.run_until_complete(task) - except exceptions.CancelledError: - if self._interrupt_count > 0: - uncancel = getattr(task, "uncancel", None) - if uncancel is not None and uncancel() == 0: - raise KeyboardInterrupt # noqa: B904 - raise # CancelledError - finally: - if ( - sigint_handler is not None - and signal.getsignal(signal.SIGINT) is sigint_handler - ): - signal.signal(signal.SIGINT, signal.default_int_handler) - - def _lazy_init(self) -> None: - if self._state is _State.CLOSED: - raise RuntimeError("Runner is closed") - if self._state is _State.INITIALIZED: - return - if self._loop_factory is None: - self._loop = events.new_event_loop() - if not self._set_event_loop: - # Call set_event_loop only once to avoid calling - # attach_loop multiple times on child watchers - events.set_event_loop(self._loop) - self._set_event_loop = True - else: - self._loop = self._loop_factory() - if self._debug is not None: - self._loop.set_debug(self._debug) - self._context = contextvars.copy_context() - self._state = _State.INITIALIZED - - def _on_sigint(self, signum, frame, main_task: asyncio.Task) -> None: - self._interrupt_count += 1 - if self._interrupt_count == 1 and not main_task.done(): - main_task.cancel() - # wakeup loop if it is blocked by select() with long timeout - self._loop.call_soon_threadsafe(lambda: None) - return - raise KeyboardInterrupt() - - def _cancel_all_tasks(loop: AbstractEventLoop) -> None: - to_cancel = tasks.all_tasks(loop) - if not to_cancel: - return - - for task in to_cancel: - task.cancel() - - loop.run_until_complete(tasks.gather(*to_cancel, return_exceptions=True)) - - for task in to_cancel: - if task.cancelled(): - continue - if task.exception() is not None: - loop.call_exception_handler( - { - "message": "unhandled exception during asyncio.run() shutdown", - "exception": task.exception(), - "task": task, - } - ) - - async def _shutdown_default_executor(loop: AbstractEventLoop) -> None: - """Schedule the shutdown of the default executor.""" - - def _do_shutdown(future: asyncio.futures.Future) -> None: - try: - loop._default_executor.shutdown(wait=True) # type: ignore[attr-defined] - loop.call_soon_threadsafe(future.set_result, None) - except Exception as ex: - loop.call_soon_threadsafe(future.set_exception, ex) - - loop._executor_shutdown_called = True - if loop._default_executor is None: - return - future = loop.create_future() - thread = threading.Thread(target=_do_shutdown, args=(future,)) - thread.start() - try: - await future - finally: - thread.join() - - -T_Retval = TypeVar("T_Retval") -T_co = TypeVar("T_co", covariant=True) -T_contra = TypeVar("T_contra", contravariant=True) -PosArgsT = TypeVarTuple("PosArgsT") -P = ParamSpec("P") - -_root_task: RunVar[asyncio.Task | None] = RunVar("_root_task") - - -def find_root_task() -> asyncio.Task: - root_task = _root_task.get(None) - if root_task is not None and not root_task.done(): - return root_task - - # Look for a task that has been started via run_until_complete() - for task in all_tasks(): - if task._callbacks and not task.done(): - callbacks = [cb for cb, context in task._callbacks] - for cb in callbacks: - if ( - cb is _run_until_complete_cb - or getattr(cb, "__module__", None) == "uvloop.loop" - ): - _root_task.set(task) - return task - - # Look up the topmost task in the AnyIO task tree, if possible - task = cast(asyncio.Task, current_task()) - state = _task_states.get(task) - if state: - cancel_scope = state.cancel_scope - while cancel_scope and cancel_scope._parent_scope is not None: - cancel_scope = cancel_scope._parent_scope - - if cancel_scope is not None: - return cast(asyncio.Task, cancel_scope._host_task) - - return task - - -# -# Event loop -# - -_run_vars: WeakKeyDictionary[asyncio.AbstractEventLoop, Any] = WeakKeyDictionary() - - -def _task_started(task: asyncio.Task) -> bool: - """Return ``True`` if the task has been started and has not finished.""" - # The task coro should never be None here, as we never add finished tasks to the - # task list - coro = task.get_coro() - assert coro is not None - return getcoroutinestate(coro) in (CORO_RUNNING, CORO_SUSPENDED) - - -# -# Timeouts and cancellation -# - - -def is_anyio_cancellation(exc: CancelledError) -> bool: - # Sometimes third party frameworks catch a CancelledError and raise a new one, so as - # a workaround we have to look at the previous ones in __context__ too for a - # matching cancel message - while True: - if ( - exc.args - and isinstance(exc.args[0], str) - and exc.args[0].startswith("Cancelled via cancel scope ") - ): - return True - - if isinstance(exc.__context__, CancelledError): - exc = exc.__context__ - continue - - return False - - -class CancelScope(BaseCancelScope): - __slots__ = ( - "_active", - "_cancel_called", - "_cancel_handle", - "_cancel_reason", - "_cancelled_caught", - "_child_scopes", - "_deadline", - "_host_task", - "_parent_scope", - "_pending_uncancellations", - "_shield", - "_tasks", - "_timeout_handle", - ) - - def __new__( - cls, *, deadline: float = math.inf, shield: bool = False - ) -> CancelScope: - return object.__new__(cls) - - def __init__(self, deadline: float = math.inf, shield: bool = False): - self._deadline = deadline - self._shield = shield - self._parent_scope: CancelScope | None = None - self._child_scopes: set[CancelScope] = set() - self._cancel_called = False - self._cancel_reason: str | None = None - self._cancelled_caught = False - self._active = False - self._timeout_handle: asyncio.TimerHandle | None = None - self._cancel_handle: asyncio.Handle | None = None - self._tasks: set[asyncio.Task] = set() - self._host_task: asyncio.Task | None = None - if sys.version_info >= (3, 11): - self._pending_uncancellations: int | None = 0 - else: - self._pending_uncancellations = None - - def __enter__(self) -> CancelScope: - if self._active: - raise RuntimeError( - "Each CancelScope may only be used for a single 'with' block" - ) - - self._host_task = host_task = cast(asyncio.Task, current_task()) - self._tasks.add(host_task) - try: - task_state = _task_states[host_task] - except KeyError: - task_state = TaskState(None, self) - _task_states[host_task] = task_state - else: - self._parent_scope = task_state.cancel_scope - task_state.cancel_scope = self - if self._parent_scope is not None: - # If using an eager task factory, the parent scope may not even contain - # the host task - self._parent_scope._child_scopes.add(self) - self._parent_scope._tasks.discard(host_task) - - self._timeout() - self._active = True - - # Start cancelling the host task if the scope was cancelled before entering - if self._cancel_called: - self._deliver_cancellation(self) - - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool: - del exc_tb - - if not self._active: - raise RuntimeError("This cancel scope is not active") - if current_task() is not self._host_task: - raise RuntimeError( - "Attempted to exit cancel scope in a different task than it was " - "entered in" - ) - - assert self._host_task is not None - host_task_state = _task_states.get(self._host_task) - if host_task_state is None or host_task_state.cancel_scope is not self: - raise RuntimeError( - "Attempted to exit a cancel scope that isn't the current tasks's " - "current cancel scope" - ) - - try: - self._active = False - if self._timeout_handle: - self._timeout_handle.cancel() - self._timeout_handle = None - - self._tasks.remove(self._host_task) - if self._parent_scope is not None: - self._parent_scope._child_scopes.remove(self) - self._parent_scope._tasks.add(self._host_task) - - host_task_state.cancel_scope = self._parent_scope - - # Restart the cancellation effort in the closest visible, cancelled parent - # scope if necessary - self._restart_cancellation_in_parent() - - # We only swallow the exception iff it was an AnyIO CancelledError, either - # directly as exc_val or inside an exception group and there are no cancelled - # parent cancel scopes visible to us here - if self._cancel_called and not self._parent_cancellation_is_visible_to_us: - # For each level-cancel() call made on the host task, call uncancel() - while self._pending_uncancellations: - self._host_task.uncancel() - self._pending_uncancellations -= 1 - - # Update cancelled_caught and check for exceptions we must not swallow - if isinstance(exc_val, BaseExceptionGroup): - cancelleds_caught, remaining = exc_val.split( - lambda exc: ( - isinstance(exc, CancelledError) - and is_anyio_cancellation(exc) - ) - ) - - if cancelleds_caught is None: - return False - - self._cancelled_caught = True - - if remaining is None: - return True - - context = remaining.__context__ - try: - # Preserve __cause__ and __suppress_context__ by avoiding `raise - # ... from ...` - raise remaining - finally: - # Preserve __context__ - remaining.__context__ = context - del context - else: - if isinstance(exc_val, CancelledError) and is_anyio_cancellation( - exc_val - ): - self._cancelled_caught = True - return True - else: - return False - else: - if self._pending_uncancellations: - assert self._parent_scope is not None - assert self._parent_scope._pending_uncancellations is not None - self._parent_scope._pending_uncancellations += ( - self._pending_uncancellations - ) - self._pending_uncancellations = 0 - - return False - finally: - self._host_task = None - del exc_val - - @property - def _effectively_cancelled(self) -> bool: - cancel_scope: CancelScope | None = self - while cancel_scope is not None: - if cancel_scope._cancel_called: - return True - - if cancel_scope.shield: - return False - - cancel_scope = cancel_scope._parent_scope - - return False - - @property - def _parent_cancellation_is_visible_to_us(self) -> bool: - return ( - self._parent_scope is not None - and not self.shield - and self._parent_scope._effectively_cancelled - ) - - def _timeout(self) -> None: - if self._deadline != math.inf: - loop = get_running_loop() - if loop.time() >= self._deadline: - self.cancel("deadline exceeded") - else: - self._timeout_handle = loop.call_at(self._deadline, self._timeout) - - def _deliver_cancellation(self, origin: CancelScope) -> bool: - """ - Deliver cancellation to directly contained tasks and nested cancel scopes. - - Schedule another run at the end if we still have tasks eligible for - cancellation. - - :param origin: the cancel scope that originated the cancellation - :return: ``True`` if the delivery needs to be retried on the next cycle - - """ - should_retry = False - current = current_task() - for task in self._tasks: - should_retry = True - if task._must_cancel: # type: ignore[attr-defined] - continue - - # The task is eligible for cancellation if it has started - if task is not current and (task is self._host_task or _task_started(task)): - waiter = task._fut_waiter # type: ignore[attr-defined] - if not isinstance(waiter, asyncio.Future) or not waiter.done(): - task.cancel(origin._cancel_reason) - if ( - task is origin._host_task - and origin._pending_uncancellations is not None - ): - origin._pending_uncancellations += 1 - - # Deliver cancellation to child scopes that aren't shielded or running their own - # cancellation callbacks - for scope in self._child_scopes: - if not scope._shield and not scope.cancel_called: - should_retry = scope._deliver_cancellation(origin) or should_retry - - # Schedule another callback if there are still tasks left - if origin is self: - if should_retry: - self._cancel_handle = get_running_loop().call_soon( - self._deliver_cancellation, origin - ) - else: - self._cancel_handle = None - - return should_retry - - def _restart_cancellation_in_parent(self) -> None: - """ - Restart the cancellation effort in the closest directly cancelled parent scope. - - """ - scope = self._parent_scope - while scope is not None: - if scope._cancel_called: - if scope._cancel_handle is None: - scope._deliver_cancellation(scope) - - break - - # No point in looking beyond any shielded scope - if scope._shield: - break - - scope = scope._parent_scope - - def cancel(self, reason: str | None = None) -> None: - if not self._cancel_called: - if self._timeout_handle: - self._timeout_handle.cancel() - self._timeout_handle = None - - self._cancel_called = True - self._cancel_reason = f"Cancelled via cancel scope {id(self):x}" - if task := current_task(): - self._cancel_reason += f" by {task}" - - if reason: - self._cancel_reason += f"; reason: {reason}" - - if self._host_task is not None: - self._deliver_cancellation(self) - - @property - def deadline(self) -> float: - return self._deadline - - @deadline.setter - def deadline(self, value: float) -> None: - self._deadline = float(value) - if self._timeout_handle is not None: - self._timeout_handle.cancel() - self._timeout_handle = None - - if self._active and not self._cancel_called: - self._timeout() - - @property - def cancel_called(self) -> bool: - return self._cancel_called - - @property - def cancelled_caught(self) -> bool: - return self._cancelled_caught - - @property - def shield(self) -> bool: - return self._shield - - @shield.setter - def shield(self, value: bool) -> None: - if self._shield != value: - self._shield = value - if not value: - self._restart_cancellation_in_parent() - - -# -# Task states -# - - -class TaskState: - """ - Encapsulates auxiliary task information that cannot be added to the Task instance - itself because there are no guarantees about its implementation. - """ - - __slots__ = "parent_id", "cancel_scope", "__weakref__" - - def __init__(self, parent_id: int | None, cancel_scope: CancelScope | None): - self.parent_id = parent_id - self.cancel_scope = cancel_scope - - -_task_states: WeakKeyDictionary[asyncio.Task, TaskState] = WeakKeyDictionary() - - -# -# Task groups -# - - -class _AsyncioTaskStatus(abc.TaskStatus): - def __init__(self, future: asyncio.Future, parent_id: int): - self._future = future - self._parent_id = parent_id - - def started(self, value: T_contra | None = None) -> None: - try: - self._future.set_result(value) - except asyncio.InvalidStateError: - if not self._future.cancelled(): - raise RuntimeError( - "called 'started' twice on the same task status" - ) from None - - task = cast(asyncio.Task, current_task()) - _task_states[task].parent_id = self._parent_id - - -if sys.version_info >= (3, 12): - _eager_task_factory_code: CodeType | None = asyncio.eager_task_factory.__code__ -else: - _eager_task_factory_code = None - - -class TaskGroup(abc.TaskGroup): - def __init__(self) -> None: - self.cancel_scope: CancelScope = CancelScope() - self._entered = False - self._exceptions: list[BaseException] = [] - self._tasks: set[asyncio.Task] = set() - self._on_completed_fut: asyncio.Future[None] | None = None - - async def __aenter__(self) -> TaskGroup: - if self._entered: - raise RuntimeError("TaskGroup cannot be entered more than once") - - self._entered = True - - self.cancel_scope.__enter__() - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool: - try: - if exc_val is not None: - self.cancel_scope.cancel() - if not isinstance(exc_val, CancelledError): - self._exceptions.append(exc_val) - - loop = get_running_loop() - try: - if self._tasks: - with CancelScope() as wait_scope: - while self._tasks: - self._on_completed_fut = loop.create_future() - - try: - await self._on_completed_fut - except CancelledError as exc: - # Shield the scope against further cancellation attempts, - # as they're not productive (#695) - wait_scope.shield = True - self.cancel_scope.cancel() - - # Set exc_val from the cancellation exception if it was - # previously unset. However, we should not replace a native - # cancellation exception with one raise by a cancel scope. - if exc_val is None or ( - isinstance(exc_val, CancelledError) - and not is_anyio_cancellation(exc) - ): - exc_val = exc - - self._on_completed_fut = None - else: - # If there are no child tasks to wait on, run at least one checkpoint - # anyway - await AsyncIOBackend.cancel_shielded_checkpoint() - - if self._exceptions: - # The exception that got us here should already have been - # added to self._exceptions so it's ok to break exception - # chaining and avoid adding a "During handling of above..." - # for each nesting level. - raise BaseExceptionGroup( - "unhandled errors in a TaskGroup", self._exceptions - ) from None - elif exc_val: - raise exc_val - except BaseException as exc: - if self.cancel_scope.__exit__(type(exc), exc, exc.__traceback__): - return True - - raise - - return self.cancel_scope.__exit__(exc_type, exc_val, exc_tb) - finally: - del exc_val, exc_tb, self._exceptions - - def _spawn( - self, - coro: Coroutine[Any, Any, T_co], - name: object, - task_status_future: asyncio.Future | None = None, - ) -> TaskHandle[T_co]: - def task_done(_task: asyncio.Task) -> None: - if sys.version_info >= (3, 14) and self.cancel_scope._host_task is not None: - asyncio.future_discard_from_awaited_by( - _task, self.cancel_scope._host_task - ) - - task_state = _task_states[_task] - assert task_state.cancel_scope is not None - assert _task in task_state.cancel_scope._tasks - task_state.cancel_scope._tasks.remove(_task) - self._tasks.remove(task) - del _task_states[_task] - - if self._on_completed_fut is not None and not self._tasks: - try: - self._on_completed_fut.set_result(None) - except asyncio.InvalidStateError: - pass - - try: - exc = _task.exception() - except CancelledError as e: - while isinstance(e.__context__, CancelledError): - e = e.__context__ - - exc = e - - if exc is not None: - # The future can only be in the cancelled state if the host task was - # cancelled, so return immediately instead of adding one more - # CancelledError to the exceptions list - if task_status_future is not None and task_status_future.cancelled(): - return - - if task_status_future is None or task_status_future.done(): - if not isinstance(exc, CancelledError): - self._exceptions.append(exc) - - if not self.cancel_scope._effectively_cancelled: - self.cancel_scope.cancel() - else: - task_status_future.set_exception(exc) - elif task_status_future is not None and not task_status_future.done(): - task_status_future.set_exception( - RuntimeError("Child exited without calling task_status.started()") - ) - - if task_status_future: - parent_id = id(current_task()) - else: - parent_id = id(self.cancel_scope._host_task) - - handle = TaskHandle(coro, name) - loop = asyncio.get_running_loop() - wrapper_coro = handle._run_coro() - if ( - (factory := loop.get_task_factory()) - and getattr(factory, "__code__", None) is _eager_task_factory_code - and (closure := getattr(factory, "__closure__", None)) - ): - custom_task_constructor = closure[0].cell_contents - task = custom_task_constructor(wrapper_coro, loop=loop, name=handle.name) - else: - task = loop.create_task(wrapper_coro, name=handle.name) - - # Make the spawned task inherit the task group's cancel scope - _task_states[task] = TaskState( - parent_id=parent_id, cancel_scope=self.cancel_scope - ) - self.cancel_scope._tasks.add(task) - self._tasks.add(task) - if sys.version_info >= (3, 14) and self.cancel_scope._host_task is not None: - asyncio.future_add_to_awaited_by(task, self.cancel_scope._host_task) - - task.add_done_callback(task_done) - return handle - - def create_task( - self, - coro: Coroutine[Any, Any, T_co], - *, - name: object = None, - context: Context | None = None, - ) -> TaskHandle[T_co]: - if not isinstance(coro, Coroutine): - raise TypeError(f"expected a coroutine, got {coro.__class__.__qualname__}") - - if not self._entered or not self.cancel_scope._active: - coro.close() - raise RuntimeError( - "This task group is not active; no new tasks can be started." - ) - - if context is not None: - return context.run(self._spawn, coro, name=name) - else: - return self._spawn(coro, name=name) - - async def start( - self, - func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], - *args: Unpack[PosArgsT], - name: object = None, - return_handle: Literal[False] | Literal[True] = False, - ) -> Any: - if not self._entered or not self.cancel_scope._active: - raise RuntimeError( - "This task group is not active; no new tasks can be started." - ) - - future: asyncio.Future = asyncio.Future() - final_name = get_callable_name(func, name) - task_status = _AsyncioTaskStatus(future, id(self.cancel_scope._host_task)) - coro = call_for_coroutine(func, args, task_status=task_status) - handle = self._spawn(coro, final_name, future) - - # If the task raises an exception after sending a start value without a switch - # point between, the task group is cancelled and this method never proceeds to - # process the completed future. That's why we have to have a shielded cancel - # scope here. - try: - await future - except BaseException: - if handle.status is TaskHandle.Status.PENDING: - # Cancel the task and wait for it to exit before returning - handle.cancel() - with CancelScope(shield=True): - await handle.wait() - - raise - - if return_handle: - handle._start_value = future.result() - return handle - else: - return future.result() - - -# -# Threads -# - -_Retval_Queue_Type = tuple[T_Retval | None, BaseException | None] - - -class WorkerThread(Thread): - MAX_IDLE_TIME = 10 # seconds - - def __init__( - self, - root_task: asyncio.Task, - workers: set[WorkerThread], - idle_workers: deque[WorkerThread], - ): - super().__init__(name="AnyIO worker thread") - self.root_task = root_task - self.workers = workers - self.idle_workers = idle_workers - self.loop = root_task._loop - self.queue: Queue[ - tuple[Context, Callable, tuple, asyncio.Future, CancelScope] | None - ] = Queue(2) - self.idle_since = AsyncIOBackend.current_time() - self.stopping = False - - def _report_result( - self, future: asyncio.Future, result: Any, exc: BaseException | None - ) -> None: - self.idle_since = AsyncIOBackend.current_time() - if not self.stopping: - self.idle_workers.append(self) - - if not future.cancelled(): - if exc is not None: - if isinstance(exc, StopIteration): - new_exc = RuntimeError("coroutine raised StopIteration") - new_exc.__cause__ = exc - exc = new_exc - - future.set_exception(exc) - else: - future.set_result(result) - - def run(self) -> None: - with claim_worker_thread(AsyncIOBackend, self.loop): - while True: - item = self.queue.get() - if item is None: - # Shutdown command received - return - - context, func, args, future, cancel_scope = item - if not future.cancelled(): - result = None - exception: BaseException | None = None - threadlocals.current_cancel_scope = cancel_scope - try: - result = context.run(func, *args) - except BaseException as exc: - exception = exc - finally: - del threadlocals.current_cancel_scope - - if not self.loop.is_closed(): - self.loop.call_soon_threadsafe( - self._report_result, future, result, exception - ) - - del result, exception - - self.queue.task_done() - del item, context, func, args, future, cancel_scope - - def stop(self, f: asyncio.Task | None = None) -> None: - self.stopping = True - self.queue.put_nowait(None) - self.workers.discard(self) - try: - self.idle_workers.remove(self) - except ValueError: - pass - - -_threadpool_idle_workers: RunVar[deque[WorkerThread]] = RunVar( - "_threadpool_idle_workers" -) -_threadpool_workers: RunVar[set[WorkerThread]] = RunVar("_threadpool_workers") - - -# -# Subprocesses -# - - -@dataclass(eq=False) -class StreamReaderWrapper(abc.ByteReceiveStream): - _stream: asyncio.StreamReader - - async def receive(self, max_bytes: int = 65536) -> bytes: - data = await self._stream.read(max_bytes) - if data: - return data - else: - raise EndOfStream - - async def aclose(self) -> None: - self._stream.set_exception(ClosedResourceError()) - await AsyncIOBackend.checkpoint() - - -@dataclass(eq=False) -class StreamWriterWrapper(abc.ByteSendStream): - _stream: asyncio.StreamWriter - _closed: bool = field(init=False, default=False) - - async def send(self, item: bytes) -> None: - await AsyncIOBackend.checkpoint_if_cancelled() - stream_paused = self._stream._protocol._paused # type: ignore[attr-defined] - try: - self._stream.write(item) - await self._stream.drain() - except (ConnectionResetError, BrokenPipeError, RuntimeError) as exc: - # If closed by us and/or the peer: - # * on stdlib, drain() raises ConnectionResetError or BrokenPipeError - # * on uvloop and Winloop, write() eventually starts raising RuntimeError - if self._closed: - raise ClosedResourceError from exc - elif self._stream.is_closing(): - raise BrokenResourceError from exc - - raise - - if not stream_paused: - await AsyncIOBackend.cancel_shielded_checkpoint() - - async def aclose(self) -> None: - self._closed = True - self._stream.close() - await AsyncIOBackend.checkpoint() - - -@dataclass(eq=False) -class Process(abc.Process): - _process: asyncio.subprocess.Process - _stdin: StreamWriterWrapper | None - _stdout: StreamReaderWrapper | None - _stderr: StreamReaderWrapper | None - - async def aclose(self) -> None: - with CancelScope(shield=True) as scope: - if self._stdin: - await self._stdin.aclose() - if self._stdout: - await self._stdout.aclose() - if self._stderr: - await self._stderr.aclose() - - scope.shield = False - try: - await self.wait() - except BaseException: - scope.shield = True - self.kill() - await self.wait() - raise - - async def wait(self) -> int: - return await self._process.wait() - - def terminate(self) -> None: - self._process.terminate() - - def kill(self) -> None: - self._process.kill() - - def send_signal(self, signal: int) -> None: - self._process.send_signal(signal) - - @property - def pid(self) -> int: - return self._process.pid - - @property - def returncode(self) -> int | None: - return self._process.returncode - - @property - def stdin(self) -> abc.ByteSendStream | None: - return self._stdin - - @property - def stdout(self) -> abc.ByteReceiveStream | None: - return self._stdout - - @property - def stderr(self) -> abc.ByteReceiveStream | None: - return self._stderr - - -def _forcibly_shutdown_process_pool_on_exit( - workers: set[Process], _task: object -) -> None: - """ - Forcibly shuts down worker processes belonging to this event loop.""" - child_watcher: asyncio.AbstractChildWatcher | None = None # type: ignore[name-defined] - if sys.version_info < (3, 12): - try: - child_watcher = asyncio.get_event_loop_policy().get_child_watcher() - except NotImplementedError: - pass - - # Close as much as possible (w/o async/await) to avoid warnings - for process in workers.copy(): - if process.returncode is not None: - continue - - process._stdin._stream._transport.close() # type: ignore[union-attr] - process._stdout._stream._transport.close() # type: ignore[union-attr] - process._stderr._stream._transport.close() # type: ignore[union-attr] - process.kill() - if child_watcher: - child_watcher.remove_child_handler(process.pid) - - -async def _shutdown_process_pool_on_exit(workers: set[abc.Process]) -> None: - """ - Shuts down worker processes belonging to this event loop. - - NOTE: this only works when the event loop was started using asyncio.run() or - anyio.run(). - - """ - process: abc.Process - try: - await sleep(math.inf) - except asyncio.CancelledError: - workers = workers.copy() - for process in workers: - if process.returncode is None: - process.kill() - - for process in workers: - await process.aclose() - - -# -# Sockets and networking -# - - -class StreamProtocol(asyncio.Protocol): - read_queue: deque[bytes] - read_event: asyncio.Event - write_event: asyncio.Event - exception: Exception | None = None - is_at_eof: bool = False - - def connection_made(self, transport: asyncio.BaseTransport) -> None: - self.read_queue = deque() - self.read_event = asyncio.Event() - self.write_event = asyncio.Event() - self.write_event.set() - cast(asyncio.Transport, transport).set_write_buffer_limits(0) - - def connection_lost(self, exc: Exception | None) -> None: - if exc: - self.exception = exc - - self.read_event.set() - self.write_event.set() - - def data_received(self, data: bytes) -> None: - # ProactorEventloop sometimes sends bytearray instead of bytes - self.read_queue.append(bytes(data)) - self.read_event.set() - - def eof_received(self) -> bool | None: - self.is_at_eof = True - self.read_event.set() - return True - - def pause_writing(self) -> None: - self.write_event = asyncio.Event() - - def resume_writing(self) -> None: - self.write_event.set() - - -class DatagramProtocol(asyncio.DatagramProtocol): - read_queue: deque[tuple[bytes, IPSockAddrType]] - read_event: asyncio.Event - write_event: asyncio.Event - closed_event: asyncio.Event - exception: Exception | None = None - - def connection_made(self, transport: asyncio.BaseTransport) -> None: - self.read_queue = deque(maxlen=100) # arbitrary value - self.read_event = asyncio.Event() - self.write_event = asyncio.Event() - self.closed_event = asyncio.Event() - self.write_event.set() - - def connection_lost(self, exc: Exception | None) -> None: - self.read_event.set() - self.write_event.set() - self.closed_event.set() - - def datagram_received(self, data: bytes, addr: IPSockAddrType) -> None: - addr = convert_ipv6_sockaddr(addr) - self.read_queue.append((data, addr)) - self.read_event.set() - - def error_received(self, exc: Exception) -> None: - self.exception = exc - - def pause_writing(self) -> None: - self.write_event.clear() - - def resume_writing(self) -> None: - self.write_event.set() - - -class SocketStream(abc.SocketStream): - def __init__(self, transport: asyncio.Transport, protocol: StreamProtocol): - self._transport = transport - self._protocol = protocol - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - self._closed = False - - @property - def _raw_socket(self) -> socket.socket: - return self._transport.get_extra_info("socket") - - async def receive(self, max_bytes: int = 65536) -> bytes: - with self._receive_guard: - if ( - not self._protocol.read_event.is_set() - and not self._transport.is_closing() - and not self._protocol.is_at_eof - ): - self._transport.resume_reading() - await self._protocol.read_event.wait() - self._transport.pause_reading() - else: - await AsyncIOBackend.checkpoint() - - try: - chunk = self._protocol.read_queue.popleft() - except IndexError: - if self._closed: - raise ClosedResourceError from None - elif self._protocol.exception: - raise BrokenResourceError from self._protocol.exception - else: - raise EndOfStream from None - - if len(chunk) > max_bytes: - # Split the oversized chunk - chunk, leftover = chunk[:max_bytes], chunk[max_bytes:] - self._protocol.read_queue.appendleft(leftover) - - # If the read queue is empty, clear the flag so that the next call will - # block until data is available - if not self._protocol.read_queue: - self._protocol.read_event.clear() - - return chunk - - async def send(self, item: bytes) -> None: - with self._send_guard: - await AsyncIOBackend.checkpoint() - - if self._closed: - raise ClosedResourceError - elif self._protocol.exception is not None: - raise BrokenResourceError from self._protocol.exception - - try: - self._transport.write(item) - except RuntimeError as exc: - if self._transport.is_closing(): - raise BrokenResourceError from exc - else: - raise - - await self._protocol.write_event.wait() - - async def send_eof(self) -> None: - try: - self._transport.write_eof() - except OSError: - pass - - async def aclose(self) -> None: - self._closed = True - if not self._transport.is_closing(): - try: - self._transport.write_eof() - except OSError: - pass - - self._transport.close() - await sleep(0) - self._transport.abort() - - -class _RawSocketMixin: - _receive_future: asyncio.Future | None = None - _send_future: asyncio.Future | None = None - _closing = False - - def __init__(self, raw_socket: socket.socket): - self.__raw_socket = raw_socket - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - @property - def _raw_socket(self) -> socket.socket: - return self.__raw_socket - - def _wait_until_readable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: - def callback(f: object) -> None: - del self._receive_future - loop.remove_reader(self.__raw_socket) - - f = self._receive_future = asyncio.Future() - loop.add_reader(self.__raw_socket, f.set_result, None) - f.add_done_callback(callback) - return f - - def _wait_until_writable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: - def callback(f: object) -> None: - del self._send_future - loop.remove_writer(self.__raw_socket) - - f = self._send_future = asyncio.Future() - loop.add_writer(self.__raw_socket, f.set_result, None) - f.add_done_callback(callback) - return f - - async def aclose(self) -> None: - if not self._closing: - self._closing = True - if self.__raw_socket.fileno() != -1: - self.__raw_socket.close() - - if self._receive_future: - self._receive_future.set_result(None) - if self._send_future: - self._send_future.set_result(None) - - -class UNIXSocketStream(_RawSocketMixin, abc.UNIXSocketStream): - async def send_eof(self) -> None: - with self._send_guard: - self._raw_socket.shutdown(socket.SHUT_WR) - - async def receive(self, max_bytes: int = 65536) -> bytes: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._receive_guard: - while True: - try: - data = self._raw_socket.recv(max_bytes) - except BlockingIOError: - await self._wait_until_readable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - if not data: - raise EndOfStream - - return data - - async def send(self, item: bytes) -> None: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._send_guard: - view = memoryview(item) - while view: - try: - bytes_sent = self._raw_socket.send(view) - except BlockingIOError: - await self._wait_until_writable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - view = view[bytes_sent:] - - async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: - if not isinstance(msglen, int) or msglen < 0: - raise ValueError("msglen must be a non-negative integer") - if not isinstance(maxfds, int) or maxfds < 1: - raise ValueError("maxfds must be a positive integer") - - loop = get_running_loop() - fds = array.array("i") - await AsyncIOBackend.checkpoint() - with self._receive_guard: - while True: - try: - message, ancdata, flags, addr = self._raw_socket.recvmsg( - msglen, socket.CMSG_LEN(maxfds * fds.itemsize) - ) - except BlockingIOError: - await self._wait_until_readable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - if not message and not ancdata: - raise EndOfStream - - break - - for cmsg_level, cmsg_type, cmsg_data in ancdata: - if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: - raise RuntimeError( - f"Received unexpected ancillary data; message = {message!r}, " - f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" - ) - - fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) - - return message, list(fds) - - async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: - if not message: - raise ValueError("message must not be empty") - if not fds: - raise ValueError("fds must not be empty") - - loop = get_running_loop() - filenos: list[int] = [] - for fd in fds: - if isinstance(fd, int): - filenos.append(fd) - elif isinstance(fd, IOBase): - filenos.append(fd.fileno()) - - fdarray = array.array("i", filenos) - await AsyncIOBackend.checkpoint() - with self._send_guard: - while True: - try: - # The ignore can be removed after mypy picks up - # https://github.com/python/typeshed/pull/5545 - self._raw_socket.sendmsg( - [message], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, fdarray)] - ) - break - except BlockingIOError: - await self._wait_until_writable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - - -class TCPSocketListener(abc.SocketListener): - _accept_scope: CancelScope | None = None - _closed = False - - def __init__(self, raw_socket: socket.socket): - self.__raw_socket = raw_socket - self._loop = cast(asyncio.BaseEventLoop, get_running_loop()) - self._accept_guard = ResourceGuard("accepting connections from") - - @property - def _raw_socket(self) -> socket.socket: - return self.__raw_socket - - async def accept(self) -> abc.SocketStream: - if self._closed: - raise ClosedResourceError - - with self._accept_guard: - await AsyncIOBackend.checkpoint() - with CancelScope() as self._accept_scope: - try: - client_sock, _addr = await self._loop.sock_accept(self._raw_socket) - except asyncio.CancelledError: - # Workaround for https://bugs.python.org/issue41317 - try: - self._loop.remove_reader(self._raw_socket) - except (ValueError, NotImplementedError): - pass - - if self._closed: - raise ClosedResourceError from None - - raise - finally: - self._accept_scope = None - - client_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - transport, protocol = await self._loop.connect_accepted_socket( - StreamProtocol, client_sock - ) - return SocketStream(transport, protocol) - - async def aclose(self) -> None: - if self._closed: - return - - self._closed = True - if self._accept_scope: - # Workaround for https://bugs.python.org/issue41317 - try: - self._loop.remove_reader(self._raw_socket) - except (ValueError, NotImplementedError): - pass - - self._accept_scope.cancel() - await sleep(0) - - self._raw_socket.close() - - -class UNIXSocketListener(abc.SocketListener): - def __init__(self, raw_socket: socket.socket): - self.__raw_socket = raw_socket - self._loop = get_running_loop() - self._accept_guard = ResourceGuard("accepting connections from") - self._closed = False - - async def accept(self) -> abc.SocketStream: - await AsyncIOBackend.checkpoint() - with self._accept_guard: - while True: - try: - client_sock, _ = self.__raw_socket.accept() - client_sock.setblocking(False) - return UNIXSocketStream(client_sock) - except BlockingIOError: - f: asyncio.Future = asyncio.Future() - self._loop.add_reader(self.__raw_socket, f.set_result, None) - f.add_done_callback( - lambda _: self._loop.remove_reader(self.__raw_socket) - ) - await f - except OSError as exc: - if self._closed: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - - async def aclose(self) -> None: - self._closed = True - self.__raw_socket.close() - - @property - def _raw_socket(self) -> socket.socket: - return self.__raw_socket - - -class UDPSocket(abc.UDPSocket): - def __init__( - self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol - ): - self._transport = transport - self._protocol = protocol - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - self._closed = False - - @property - def _raw_socket(self) -> socket.socket: - return self._transport.get_extra_info("socket") - - async def aclose(self) -> None: - self._closed = True - if not self._transport.is_closing(): - self._transport.close() - - await self._protocol.closed_event.wait() - - async def receive(self) -> tuple[bytes, IPSockAddrType]: - with self._receive_guard: - await AsyncIOBackend.checkpoint() - - # If the buffer is empty, ask for more data - if not self._protocol.read_queue and not self._transport.is_closing(): - self._protocol.read_event.clear() - await self._protocol.read_event.wait() - - try: - return self._protocol.read_queue.popleft() - except IndexError: - if self._closed: - raise ClosedResourceError from None - else: - raise BrokenResourceError from None - - async def send(self, item: UDPPacketType) -> None: - with self._send_guard: - await AsyncIOBackend.checkpoint() - await self._protocol.write_event.wait() - if self._closed: - raise ClosedResourceError - elif self._transport.is_closing(): - raise BrokenResourceError - else: - self._transport.sendto(*item) - - -class ConnectedUDPSocket(abc.ConnectedUDPSocket): - def __init__( - self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol - ): - self._transport = transport - self._protocol = protocol - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - self._closed = False - - @property - def _raw_socket(self) -> socket.socket: - return self._transport.get_extra_info("socket") - - async def aclose(self) -> None: - self._closed = True - if not self._transport.is_closing(): - self._transport.close() - - await self._protocol.closed_event.wait() - - async def receive(self) -> bytes: - with self._receive_guard: - await AsyncIOBackend.checkpoint() - - # If the buffer is empty, ask for more data - if not self._protocol.read_queue and not self._transport.is_closing(): - self._protocol.read_event.clear() - await self._protocol.read_event.wait() - - try: - packet = self._protocol.read_queue.popleft() - except IndexError: - if self._closed: - raise ClosedResourceError from None - else: - raise BrokenResourceError from None - - return packet[0] - - async def send(self, item: bytes) -> None: - with self._send_guard: - await AsyncIOBackend.checkpoint() - await self._protocol.write_event.wait() - if self._closed: - raise ClosedResourceError - elif self._transport.is_closing(): - raise BrokenResourceError - else: - self._transport.sendto(item) - - -class UNIXDatagramSocket(_RawSocketMixin, abc.UNIXDatagramSocket): - async def receive(self) -> UNIXDatagramPacketType: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._receive_guard: - while True: - try: - data = self._raw_socket.recvfrom(65536) - except BlockingIOError: - await self._wait_until_readable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - return data - - async def send(self, item: UNIXDatagramPacketType) -> None: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._send_guard: - while True: - try: - self._raw_socket.sendto(*item) - except BlockingIOError: - await self._wait_until_writable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - return - - -class ConnectedUNIXDatagramSocket(_RawSocketMixin, abc.ConnectedUNIXDatagramSocket): - async def receive(self) -> bytes: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._receive_guard: - while True: - try: - data = self._raw_socket.recv(65536) - except BlockingIOError: - await self._wait_until_readable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - return data - - async def send(self, item: bytes) -> None: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._send_guard: - while True: - try: - self._raw_socket.send(item) - except BlockingIOError: - await self._wait_until_writable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - return - - -_read_events: RunVar[dict[int, asyncio.Future[bool]]] = RunVar("read_events") -_write_events: RunVar[dict[int, asyncio.Future[bool]]] = RunVar("write_events") - - -# -# Synchronization -# - - -class Event(BaseEvent): - __slots__ = ("_event",) - - def __new__(cls) -> Event: - return object.__new__(cls) - - def __init__(self) -> None: - self._event = asyncio.Event() - - def set(self) -> None: - self._event.set() - - def is_set(self) -> bool: - return self._event.is_set() - - async def wait(self) -> None: - if self.is_set(): - await AsyncIOBackend.checkpoint() - else: - await self._event.wait() - - def statistics(self) -> EventStatistics: - return EventStatistics(len(self._event._waiters)) - - -class Lock(BaseLock): - __slots__ = "_fast_acquire", "_owner_task", "_waiters" - - def __new__(cls, *, fast_acquire: bool = False) -> Lock: - return object.__new__(cls) - - def __init__(self, *, fast_acquire: bool = False) -> None: - self._fast_acquire = fast_acquire - self._owner_task: asyncio.Task | None = None - self._waiters: deque[tuple[asyncio.Task, asyncio.Future]] = deque() - - async def acquire(self) -> None: - task = cast(asyncio.Task, current_task()) - if self._owner_task is None and not self._waiters: - await AsyncIOBackend.checkpoint_if_cancelled() - self._owner_task = task - - # Unless on the "fast path", yield control of the event loop so that other - # tasks can run too - if not self._fast_acquire: - try: - await AsyncIOBackend.cancel_shielded_checkpoint() - except CancelledError: - self.release() - raise - - return - - if self._owner_task == task: - raise RuntimeError("Attempted to acquire an already held Lock") - - fut: asyncio.Future[None] = asyncio.Future() - item = task, fut - self._waiters.append(item) - try: - await fut - except CancelledError: - if fut.cancelled(): - try: - self._waiters.remove(item) - except ValueError: - pass - else: - self.release() - - raise - - def acquire_nowait(self) -> None: - task = cast(asyncio.Task, current_task()) - if self._owner_task is None and not self._waiters: - self._owner_task = task - return - - if self._owner_task is task: - raise RuntimeError("Attempted to acquire an already held Lock") - - raise WouldBlock - - def locked(self) -> bool: - return self._owner_task is not None - - def release(self) -> None: - if self._owner_task != current_task(): - raise RuntimeError("The current task is not holding this lock") - - # A cancelled waiter that already received ownership removes itself from - # _waiters before calling release(); any cancelled waiter still queued here - # was cancelled before being woken, so drop it. - while self._waiters: - task, fut = self._waiters.popleft() - if fut.cancelled(): - continue - - self._owner_task = task - fut.set_result(None) - return - - self._owner_task = None - - def statistics(self) -> LockStatistics: - task_info = AsyncIOTaskInfo(self._owner_task) if self._owner_task else None - return LockStatistics(self.locked(), task_info, len(self._waiters)) - - -class Semaphore(BaseSemaphore): - __slots__ = "_value", "_max_value", "_fast_acquire", "_waiters" - - def __new__( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> Semaphore: - return object.__new__(cls) - - def __init__( - self, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ): - super().__init__(initial_value, max_value=max_value) - self._value = initial_value - self._max_value = max_value - self._fast_acquire = fast_acquire - self._waiters: deque[asyncio.Future[None]] = deque() - - async def acquire(self) -> None: - if self._value > 0 and not self._waiters: - await AsyncIOBackend.checkpoint_if_cancelled() - self._value -= 1 - - # Unless on the "fast path", yield control of the event loop so that other - # tasks can run too - if not self._fast_acquire: - try: - await AsyncIOBackend.cancel_shielded_checkpoint() - except CancelledError: - self.release() - raise - - return - - fut: asyncio.Future[None] = asyncio.Future() - self._waiters.append(fut) - try: - await fut - except CancelledError: - if fut.cancelled(): - try: - self._waiters.remove(fut) - except ValueError: - pass - else: - self.release() - - raise - - def acquire_nowait(self) -> None: - if self._value == 0: - raise WouldBlock - - self._value -= 1 - - def release(self) -> None: - if self._max_value is not None and self._value == self._max_value: - raise ValueError("semaphore released too many times") - - while self._waiters: - fut = self._waiters.popleft() - if fut.cancelled(): - continue - - fut.set_result(None) - return - - self._value += 1 - - @property - def value(self) -> int: - return self._value - - @property - def max_value(self) -> int | None: - return self._max_value - - def statistics(self) -> SemaphoreStatistics: - return SemaphoreStatistics(len(self._waiters)) - - -class CapacityLimiter(BaseCapacityLimiter): - __slots__ = "_total_tokens", "_borrowers", "_wait_queue" - - def __new__(cls, total_tokens: float) -> CapacityLimiter: - return object.__new__(cls) - - def __init__(self, total_tokens: float): - self._total_tokens: float = 0 - self._borrowers: set[Any] = set() - self._wait_queue: OrderedDict[Any, asyncio.Event] = OrderedDict() - self.total_tokens = total_tokens - - async def __aenter__(self) -> None: - await self.acquire() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.release() - - @property - def total_tokens(self) -> float: - return self._total_tokens - - @total_tokens.setter - def total_tokens(self, value: float) -> None: - if not isinstance(value, int) and not math.isinf(value): - raise TypeError("total_tokens must be an int or math.inf") - - if value < 0: - raise ValueError("total_tokens must be >= 0") - - waiters_to_notify = max(value - self._total_tokens, 0) - self._total_tokens = value - - # Notify waiting tasks that they have acquired the limiter - while self._wait_queue and waiters_to_notify: - event = self._wait_queue.popitem(last=False)[1] - event.set() - waiters_to_notify -= 1 - - @property - def borrowed_tokens(self) -> int: - return len(self._borrowers) - - @property - def available_tokens(self) -> float: - return self._total_tokens - len(self._borrowers) - - def _notify_next_waiter(self) -> None: - """Notify the next task in line if this limiter has free capacity now.""" - if self._wait_queue and len(self._borrowers) < self._total_tokens: - event = self._wait_queue.popitem(last=False)[1] - event.set() - - def acquire_nowait(self) -> None: - self.acquire_on_behalf_of_nowait(current_task()) - - def acquire_on_behalf_of_nowait(self, borrower: object) -> None: - if borrower in self._borrowers: - raise RuntimeError( - "this borrower is already holding one of this CapacityLimiter's tokens" - ) - - if self._wait_queue or len(self._borrowers) >= self._total_tokens: - raise WouldBlock - - self._borrowers.add(borrower) - - async def acquire(self) -> None: - return await self.acquire_on_behalf_of(current_task()) - - async def acquire_on_behalf_of(self, borrower: object) -> None: - await AsyncIOBackend.checkpoint_if_cancelled() - try: - self.acquire_on_behalf_of_nowait(borrower) - except WouldBlock: - event = asyncio.Event() - self._wait_queue[borrower] = event - try: - await event.wait() - except BaseException: - self._wait_queue.pop(borrower, None) - if event.is_set(): - self._notify_next_waiter() - - raise - - self._borrowers.add(borrower) - else: - try: - await AsyncIOBackend.cancel_shielded_checkpoint() - except BaseException: - self.release() - raise - - def release(self) -> None: - self.release_on_behalf_of(current_task()) - - def release_on_behalf_of(self, borrower: object) -> None: - try: - self._borrowers.remove(borrower) - except KeyError: - raise RuntimeError( - "this borrower isn't holding any of this CapacityLimiter's tokens" - ) from None - - self._notify_next_waiter() - - def statistics(self) -> CapacityLimiterStatistics: - return CapacityLimiterStatistics( - self.borrowed_tokens, - self.total_tokens, - tuple(self._borrowers), - len(self._wait_queue), - ) - - -_default_thread_limiter: RunVar[CapacityLimiter] = RunVar("_default_thread_limiter") - - -# -# Operating system signals -# - - -class _SignalReceiver: - def __init__(self, signals: tuple[Signals, ...]): - self._signals = signals - self._loop = get_running_loop() - self._signal_queue: deque[Signals] = deque() - self._future: asyncio.Future = asyncio.Future() - self._handled_signals: set[Signals] = set() - - def _deliver(self, signum: Signals) -> None: - self._signal_queue.append(signum) - if not self._future.done(): - self._future.set_result(None) - - def __enter__(self) -> _SignalReceiver: - for sig in set(self._signals): - self._loop.add_signal_handler(sig, self._deliver, sig) - self._handled_signals.add(sig) - - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - for sig in self._handled_signals: - self._loop.remove_signal_handler(sig) - - def __aiter__(self) -> _SignalReceiver: - return self - - async def __anext__(self) -> Signals: - await AsyncIOBackend.checkpoint() - if not self._signal_queue: - self._future = asyncio.Future() - await self._future - - return self._signal_queue.popleft() - - -# -# Testing and debugging -# - - -class AsyncIOTaskInfo(TaskInfo): - def __init__(self, task: asyncio.Task): - task_state = _task_states.get(task) - if task_state is None: - parent_id = None - else: - parent_id = task_state.parent_id - - coro = task.get_coro() - assert coro is not None, "created TaskInfo from a completed Task" - super().__init__(id(task), parent_id, task.get_name(), coro) - self._task = weakref.ref(task) - - def has_pending_cancellation(self) -> bool: - if not (task := self._task()): - # If the task isn't around anymore, it won't have a pending cancellation - return False - - if task._must_cancel: # type: ignore[attr-defined] - return True - elif ( - isinstance(task._fut_waiter, asyncio.Future) # type: ignore[attr-defined] - and task._fut_waiter.cancelled() # type: ignore[attr-defined] - ): - return True - - if task_state := _task_states.get(task): - if cancel_scope := task_state.cancel_scope: - return cancel_scope._effectively_cancelled - - return False - - -class TestRunner(abc.TestRunner): - _send_stream: MemoryObjectSendStream[tuple[Awaitable[Any], asyncio.Future[Any]]] - - def __init__( - self, - *, - debug: bool | None = None, - use_uvloop: bool = False, - loop_factory: Callable[[], AbstractEventLoop] | None = None, - ) -> None: - if use_uvloop and loop_factory is None: - if sys.platform != "win32": - import uvloop - - loop_factory = uvloop.new_event_loop - else: - import winloop - - loop_factory = winloop.new_event_loop - - self._runner = Runner(debug=debug, loop_factory=loop_factory) - self._exceptions: list[BaseException] = [] - self._runner_task: asyncio.Task | None = None - - def __enter__(self) -> TestRunner: - self._runner.__enter__() - self.get_loop().set_exception_handler(self._exception_handler) - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self._runner.__exit__(exc_type, exc_val, exc_tb) - - def get_loop(self) -> AbstractEventLoop: - return self._runner.get_loop() - - def is_running(self) -> bool: - try: - asyncio.get_running_loop() - return True - except RuntimeError: - return False - - def _exception_handler( - self, loop: asyncio.AbstractEventLoop, context: dict[str, Any] - ) -> None: - if isinstance(context.get("exception"), Exception): - self._exceptions.append(context["exception"]) - else: - loop.default_exception_handler(context) - - def _raise_async_exceptions(self) -> None: - # Re-raise any exceptions raised in asynchronous callbacks - if self._exceptions: - exceptions, self._exceptions = self._exceptions, [] - if len(exceptions) == 1: - raise exceptions[0] - elif exceptions: - raise BaseExceptionGroup( - "Multiple exceptions occurred in asynchronous callbacks", exceptions - ) - - async def _run_tests_and_fixtures( - self, - receive_stream: MemoryObjectReceiveStream[ - tuple[Awaitable[T_Retval], asyncio.Future[T_Retval]] - ], - ) -> None: - from _pytest.outcomes import OutcomeException - - with receive_stream, self._send_stream: - async for coro, future in receive_stream: - try: - retval = await coro - except CancelledError as exc: - if not future.cancelled(): - future.cancel(*exc.args) - - raise - except BaseException as exc: - if not future.cancelled(): - future.set_exception(exc) - - if not isinstance(exc, (Exception, OutcomeException)): - raise - else: - if not future.cancelled(): - future.set_result(retval) - - async def _call_in_runner_task( - self, - func: Callable[P, Awaitable[T_Retval]], - /, - *args: P.args, - **kwargs: P.kwargs, - ) -> T_Retval: - if not self._runner_task: - self._send_stream, receive_stream = create_memory_object_stream[ - tuple[Awaitable[Any], asyncio.Future] - ](1) - self._runner_task = self.get_loop().create_task( - self._run_tests_and_fixtures(receive_stream) - ) - - coro = func(*args, **kwargs) - future: asyncio.Future[T_Retval] = self.get_loop().create_future() - self._send_stream.send_nowait((coro, future)) - return await future - - def run_asyncgen_fixture( - self, - fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], - kwargs: dict[str, Any], - ) -> Iterable[T_Retval]: - asyncgen = fixture_func(**kwargs) - fixturevalue: T_Retval = self.get_loop().run_until_complete( - self._call_in_runner_task(asyncgen.asend, None) - ) - self._raise_async_exceptions() - - yield fixturevalue - - try: - self.get_loop().run_until_complete( - self._call_in_runner_task(asyncgen.asend, None) - ) - except StopAsyncIteration: - self._raise_async_exceptions() - else: - self.get_loop().run_until_complete(asyncgen.aclose()) - raise RuntimeError("Async generator fixture did not stop") - - def run_fixture( - self, - fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], - kwargs: dict[str, Any], - ) -> T_Retval: - retval = self.get_loop().run_until_complete( - self._call_in_runner_task(fixture_func, **kwargs) - ) - self._raise_async_exceptions() - return retval - - def run_test( - self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] - ) -> None: - from _pytest.outcomes import OutcomeException - - try: - self.get_loop().run_until_complete( - self._call_in_runner_task(test_func, **kwargs) - ) - except Exception as exc: - self._exceptions.append(exc) - except OutcomeException: - raise - except BaseException: - # A BaseException (e.g. KeyboardInterrupt, SystemExit) interrupted the event loop before - # the test completed. Cancel _runner_task so it does not resume when the event - # loop is re-entered during async generator fixture teardown. - if self._runner_task is not None and not self._runner_task.done(): - self._runner_task.cancel() - self._send_stream.close() - try: - self.get_loop().run_until_complete(self._runner_task) - except CancelledError: - pass - finally: - self._runner_task = None - raise - self._raise_async_exceptions() - - -class AsyncIOBackend(AsyncBackend): - @classmethod - def run( - cls, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - options: dict[str, Any], - ) -> T_Retval: - @wraps(func) - async def wrapper() -> T_Retval: - task = cast(asyncio.Task, current_task()) - task.set_name(get_callable_name(func)) - _task_states[task] = TaskState(None, None) - - try: - return await func(*args) - finally: - del _task_states[task] - - debug = options.get("debug", None) - loop_factory = options.get("loop_factory", None) - if loop_factory is None and options.get("use_uvloop", False): - if sys.platform != "win32": - import uvloop - - loop_factory = uvloop.new_event_loop - else: - import winloop - - loop_factory = winloop.new_event_loop - - with Runner(debug=debug, loop_factory=loop_factory) as runner: - return runner.run(wrapper()) - - @classmethod - def current_token(cls) -> object: - return get_running_loop() - - @classmethod - def current_time(cls) -> float: - return get_running_loop().time() - - @classmethod - def cancelled_exception_class(cls) -> type[BaseException]: - return CancelledError - - @classmethod - async def checkpoint(cls) -> None: - await sleep(0) - - @classmethod - async def checkpoint_if_cancelled(cls) -> None: - task = current_task() - if task is None: - return - - try: - cancel_scope = _task_states[task].cancel_scope - except KeyError: - return - - while cancel_scope: - if cancel_scope.cancel_called: - await sleep(0) - elif cancel_scope.shield: - break - else: - cancel_scope = cancel_scope._parent_scope - - @classmethod - async def cancel_shielded_checkpoint(cls) -> None: - with CancelScope(shield=True): - await sleep(0) - - @classmethod - async def sleep(cls, delay: float) -> None: - await sleep(delay) - - @classmethod - def create_cancel_scope( - cls, *, deadline: float = math.inf, shield: bool = False - ) -> CancelScope: - return CancelScope(deadline=deadline, shield=shield) - - @classmethod - def current_effective_deadline(cls) -> float: - if (task := current_task()) is None: - return math.inf - - try: - cancel_scope = _task_states[task].cancel_scope - except KeyError: - return math.inf - - deadline = math.inf - while cancel_scope: - deadline = min(deadline, cancel_scope.deadline) - if cancel_scope._cancel_called: - deadline = -math.inf - break - elif cancel_scope.shield: - break - else: - cancel_scope = cancel_scope._parent_scope - - return deadline - - @classmethod - def create_task_group(cls) -> abc.TaskGroup: - return TaskGroup() - - @classmethod - def create_event(cls) -> abc.Event: - return Event() - - @classmethod - def create_lock(cls, *, fast_acquire: bool) -> abc.Lock: - return Lock(fast_acquire=fast_acquire) - - @classmethod - def create_semaphore( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> abc.Semaphore: - return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire) - - @classmethod - def create_capacity_limiter(cls, total_tokens: float) -> abc.CapacityLimiter: - return CapacityLimiter(total_tokens) - - @classmethod - async def run_sync_in_worker_thread( # type: ignore[return] - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - abandon_on_cancel: bool = False, - limiter: abc.CapacityLimiter | None = None, - ) -> T_Retval: - await cls.checkpoint() - - # If this is the first run in this event loop thread, set up the necessary - # variables - try: - idle_workers = _threadpool_idle_workers.get() - workers = _threadpool_workers.get() - except LookupError: - idle_workers = deque() - workers = set() - _threadpool_idle_workers.set(idle_workers) - _threadpool_workers.set(workers) - - async with limiter or cls.current_default_thread_limiter(): - with CancelScope(shield=not abandon_on_cancel) as scope: - future = asyncio.Future[T_Retval]() - root_task = find_root_task() - if not idle_workers: - worker = WorkerThread(root_task, workers, idle_workers) - worker.start() - workers.add(worker) - root_task.add_done_callback( - worker.stop, context=contextvars.Context() - ) - else: - worker = idle_workers.pop() - - # Prune any other workers that have been idle for MAX_IDLE_TIME - # seconds or longer - now = cls.current_time() - while idle_workers: - if ( - now - idle_workers[0].idle_since - < WorkerThread.MAX_IDLE_TIME - ): - break - - expired_worker = idle_workers.popleft() - expired_worker.root_task.remove_done_callback( - expired_worker.stop - ) - expired_worker.stop() - - context = copy_context() - context.run(set_current_async_library, None) - if abandon_on_cancel or scope._parent_scope is None: - worker_scope = scope - else: - worker_scope = scope._parent_scope - - worker.queue.put_nowait((context, func, args, future, worker_scope)) - return await future - - @classmethod - def check_cancelled(cls) -> None: - scope: CancelScope | None = threadlocals.current_cancel_scope - while scope is not None: - if scope.cancel_called: - raise CancelledError(f"Cancelled via cancel scope {id(scope):x}") - - if scope.shield: - return - - scope = scope._parent_scope - - @classmethod - def run_async_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_co: - async def task_wrapper() -> T_co: - __tracebackhide__ = True - if scope is not None: - task = cast(asyncio.Task, current_task()) - _task_states[task] = TaskState(None, scope) - scope._tasks.add(task) - try: - return await func(*args) - except CancelledError as exc: - raise concurrent.futures.CancelledError(str(exc)) from None - finally: - if scope is not None: - scope._tasks.discard(task) - - loop = cast( - "AbstractEventLoop", token or threadlocals.current_token.native_token - ) - if loop.is_closed(): - raise RunFinishedError - - context = copy_context() - context.run(set_current_async_library, "asyncio") - scope = getattr(threadlocals, "current_cancel_scope", None) - f: concurrent.futures.Future[T_co] = context.run( - asyncio.run_coroutine_threadsafe, task_wrapper(), loop=loop - ) - return f.result() - - @classmethod - def run_sync_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_Retval: - @wraps(func) - def wrapper() -> None: - try: - set_current_async_library("asyncio") - f.set_result(func(*args)) - except BaseException as exc: - f.set_exception(exc) - if not isinstance(exc, Exception): - raise - - loop = cast( - "AbstractEventLoop", token or threadlocals.current_token.native_token - ) - if loop.is_closed(): - raise RunFinishedError - - f: concurrent.futures.Future[T_Retval] = Future() - loop.call_soon_threadsafe(wrapper) - return f.result() - - @classmethod - async def open_process( - cls, - command: StrOrBytesPath | Sequence[StrOrBytesPath], - *, - stdin: int | IO[Any] | None, - stdout: int | IO[Any] | None, - stderr: int | IO[Any] | None, - **kwargs: Any, - ) -> Process: - await cls.checkpoint() - if isinstance(command, PathLike): - command = os.fspath(command) - - if isinstance(command, (str, bytes)): - process = await asyncio.create_subprocess_shell( - command, - stdin=stdin, - stdout=stdout, - stderr=stderr, - **kwargs, - ) - else: - process = await asyncio.create_subprocess_exec( - *command, - stdin=stdin, - stdout=stdout, - stderr=stderr, - **kwargs, - ) - - stdin_stream = StreamWriterWrapper(process.stdin) if process.stdin else None - stdout_stream = StreamReaderWrapper(process.stdout) if process.stdout else None - stderr_stream = StreamReaderWrapper(process.stderr) if process.stderr else None - return Process(process, stdin_stream, stdout_stream, stderr_stream) - - @classmethod - def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: - create_task( - _shutdown_process_pool_on_exit(workers), - name="AnyIO process pool shutdown task", - ) - find_root_task().add_done_callback( - partial(_forcibly_shutdown_process_pool_on_exit, workers) # type:ignore[arg-type] - ) - - @classmethod - async def connect_tcp( - cls, host: str, port: int, local_address: IPSockAddrType | None = None - ) -> abc.SocketStream: - transport, protocol = cast( - tuple[asyncio.Transport, StreamProtocol], - await get_running_loop().create_connection( - StreamProtocol, host, port, local_addr=local_address - ), - ) - transport.pause_reading() - return SocketStream(transport, protocol) - - @classmethod - async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: - await cls.checkpoint() - loop = get_running_loop() - raw_socket = socket.socket(socket.AF_UNIX) - raw_socket.setblocking(False) - while True: - try: - raw_socket.connect(path) - except BlockingIOError: - f: asyncio.Future = asyncio.Future() - loop.add_writer(raw_socket, f.set_result, None) - f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) - await f - except BaseException: - raw_socket.close() - raise - else: - return UNIXSocketStream(raw_socket) - - @classmethod - def create_tcp_listener(cls, sock: socket.socket) -> SocketListener: - return TCPSocketListener(sock) - - @classmethod - def create_unix_listener(cls, sock: socket.socket) -> SocketListener: - return UNIXSocketListener(sock) - - @classmethod - async def create_udp_socket( - cls, - family: AddressFamily, - local_address: IPSockAddrType | None, - remote_address: IPSockAddrType | None, - reuse_port: bool, - ) -> UDPSocket | ConnectedUDPSocket: - transport, protocol = await get_running_loop().create_datagram_endpoint( - DatagramProtocol, - local_addr=local_address, - remote_addr=remote_address, - family=family, - reuse_port=reuse_port, - ) - if protocol.exception: - transport.close() - raise protocol.exception - - if not remote_address: - return UDPSocket(transport, protocol) - else: - return ConnectedUDPSocket(transport, protocol) - - @classmethod - async def create_unix_datagram_socket( # type: ignore[override] - cls, raw_socket: socket.socket, remote_path: str | bytes | None - ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: - await cls.checkpoint() - loop = get_running_loop() - - if remote_path: - while True: - try: - raw_socket.connect(remote_path) - except BlockingIOError: - f: asyncio.Future = asyncio.Future() - loop.add_writer(raw_socket, f.set_result, None) - f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) - await f - except BaseException: - raw_socket.close() - raise - else: - return ConnectedUNIXDatagramSocket(raw_socket) - else: - return UNIXDatagramSocket(raw_socket) - - @classmethod - async def getaddrinfo( - cls, - host: bytes | str | None, - port: str | int | None, - *, - family: int | AddressFamily = 0, - type: int | SocketKind = 0, - proto: int = 0, - flags: int = 0, - ) -> Sequence[ - tuple[ - AddressFamily, - SocketKind, - int, - str, - tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], - ] - ]: - return await get_running_loop().getaddrinfo( - host, port, family=family, type=type, proto=proto, flags=flags - ) - - @classmethod - async def getnameinfo( - cls, sockaddr: IPSockAddrType, flags: int = 0 - ) -> tuple[str, str]: - return await get_running_loop().getnameinfo(sockaddr, flags) - - @classmethod - async def wait_readable(cls, obj: FileDescriptorLike) -> None: - try: - read_events = _read_events.get() - except LookupError: - read_events = {} - _read_events.set(read_events) - - fd = obj if isinstance(obj, int) else obj.fileno() - if read_events.get(fd): - raise BusyResourceError("reading from") - - loop = get_running_loop() - fut: asyncio.Future[bool] = loop.create_future() - - def cb() -> None: - try: - del read_events[fd] - except KeyError: - pass - else: - remove_reader(fd) - - try: - fut.set_result(True) - except asyncio.InvalidStateError: - pass - - try: - loop.add_reader(fd, cb) - except NotImplementedError: - from anyio._core._asyncio_selector_thread import get_selector - - selector = get_selector() - selector.add_reader(fd, cb) - remove_reader = selector.remove_reader - else: - remove_reader = loop.remove_reader - - read_events[fd] = fut - try: - success = await fut - finally: - try: - del read_events[fd] - except KeyError: - pass - else: - remove_reader(fd) - - if not success: - raise ClosedResourceError - - @classmethod - async def wait_writable(cls, obj: FileDescriptorLike) -> None: - try: - write_events = _write_events.get() - except LookupError: - write_events = {} - _write_events.set(write_events) - - fd = obj if isinstance(obj, int) else obj.fileno() - if write_events.get(fd): - raise BusyResourceError("writing to") - - loop = get_running_loop() - fut: asyncio.Future[bool] = loop.create_future() - - def cb() -> None: - try: - del write_events[fd] - except KeyError: - pass - else: - remove_writer(fd) - - try: - fut.set_result(True) - except asyncio.InvalidStateError: - pass - - try: - loop.add_writer(fd, cb) - except NotImplementedError: - from anyio._core._asyncio_selector_thread import get_selector - - selector = get_selector() - selector.add_writer(fd, cb) - remove_writer = selector.remove_writer - else: - remove_writer = loop.remove_writer - - write_events[fd] = fut - try: - success = await fut - finally: - try: - del write_events[fd] - except KeyError: - pass - else: - remove_writer(fd) - - if not success: - raise ClosedResourceError - - @classmethod - def notify_closing(cls, obj: FileDescriptorLike) -> None: - fd = obj if isinstance(obj, int) else obj.fileno() - loop = get_running_loop() - - try: - write_events = _write_events.get() - except LookupError: - pass - else: - try: - fut = write_events.pop(fd) - except KeyError: - pass - else: - try: - fut.set_result(False) - except asyncio.InvalidStateError: - pass - - try: - loop.remove_writer(fd) - except NotImplementedError: - from anyio._core._asyncio_selector_thread import get_selector - - get_selector().remove_writer(fd) - - try: - read_events = _read_events.get() - except LookupError: - pass - else: - try: - fut = read_events.pop(fd) - except KeyError: - pass - else: - try: - fut.set_result(False) - except asyncio.InvalidStateError: - pass - - try: - loop.remove_reader(fd) - except NotImplementedError: - from anyio._core._asyncio_selector_thread import get_selector - - get_selector().remove_reader(fd) - - @classmethod - async def wrap_listener_socket(cls, sock: socket.socket) -> SocketListener: - if hasattr(socket, "AF_UNIX") and sock.family == socket.AF_UNIX: - return UNIXSocketListener(sock) - - return TCPSocketListener(sock) - - @classmethod - async def wrap_stream_socket(cls, sock: socket.socket) -> SocketStream: - transport, protocol = await get_running_loop().create_connection( - StreamProtocol, sock=sock - ) - return SocketStream(transport, protocol) - - @classmethod - async def wrap_unix_stream_socket(cls, sock: socket.socket) -> UNIXSocketStream: - return UNIXSocketStream(sock) - - @classmethod - async def wrap_udp_socket(cls, sock: socket.socket) -> UDPSocket: - transport, protocol = await get_running_loop().create_datagram_endpoint( - DatagramProtocol, sock=sock - ) - return UDPSocket(transport, protocol) - - @classmethod - async def wrap_connected_udp_socket(cls, sock: socket.socket) -> ConnectedUDPSocket: - transport, protocol = await get_running_loop().create_datagram_endpoint( - DatagramProtocol, sock=sock - ) - return ConnectedUDPSocket(transport, protocol) - - @classmethod - async def wrap_unix_datagram_socket(cls, sock: socket.socket) -> UNIXDatagramSocket: - return UNIXDatagramSocket(sock) - - @classmethod - async def wrap_connected_unix_datagram_socket( - cls, sock: socket.socket - ) -> ConnectedUNIXDatagramSocket: - return ConnectedUNIXDatagramSocket(sock) - - @classmethod - def current_default_thread_limiter(cls) -> CapacityLimiter: - try: - return _default_thread_limiter.get() - except LookupError: - limiter = CapacityLimiter(40) - _default_thread_limiter.set(limiter) - return limiter - - @classmethod - def open_signal_receiver( - cls, *signals: Signals - ) -> AbstractContextManager[AsyncIterator[Signals]]: - return _SignalReceiver(signals) - - @classmethod - def get_current_task(cls) -> TaskInfo: - return AsyncIOTaskInfo(current_task()) # type: ignore[arg-type] - - @classmethod - def get_running_tasks(cls) -> Sequence[TaskInfo]: - return [AsyncIOTaskInfo(task) for task in all_tasks() if not task.done()] - - @classmethod - async def wait_all_tasks_blocked(cls) -> None: - await cls.checkpoint() - this_task = current_task() - while True: - for task in all_tasks(): - if task is this_task: - continue - - waiter = task._fut_waiter # type: ignore[attr-defined] - if waiter is None or waiter.done(): - await sleep(0.1) - break - else: - return - - @classmethod - def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: - return TestRunner(**options) - - -backend_class = AsyncIOBackend diff --git a/.venv/lib/python3.12/site-packages/anyio/_backends/_trio.py b/.venv/lib/python3.12/site-packages/anyio/_backends/_trio.py deleted file mode 100644 index 091c78c5..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_backends/_trio.py +++ /dev/null @@ -1,1456 +0,0 @@ -from __future__ import annotations - -import array -import math -import os -import socket -import sys -import types -import weakref -from collections.abc import ( - AsyncGenerator, - AsyncIterator, - Awaitable, - Callable, - Collection, - Coroutine, - Iterable, - Sequence, -) -from contextlib import AbstractContextManager -from contextvars import Context -from dataclasses import dataclass -from functools import partial, wraps -from io import IOBase -from os import PathLike -from signal import Signals -from socket import AddressFamily, SocketKind -from types import TracebackType -from typing import ( - IO, - TYPE_CHECKING, - Any, - Generic, - Literal, - NoReturn, - ParamSpec, - TypeVar, - cast, - overload, -) - -import trio.from_thread -import trio.lowlevel -from outcome import Error, Outcome, Value -from trio.lowlevel import ( - current_root_task, - current_task, - notify_closing, - wait_readable, - wait_writable, -) -from trio.socket import SocketType as TrioSocketType -from trio.to_thread import run_sync - -from .. import ( - CapacityLimiterStatistics, - EventStatistics, - LockStatistics, - RunFinishedError, - TaskInfo, - WouldBlock, - abc, -) -from .._core._eventloop import claim_worker_thread -from .._core._exceptions import ( - BrokenResourceError, - BusyResourceError, - ClosedResourceError, - EndOfStream, -) -from .._core._sockets import convert_ipv6_sockaddr -from .._core._streams import create_memory_object_stream -from .._core._synchronization import ( - CapacityLimiter as BaseCapacityLimiter, -) -from .._core._synchronization import Event as BaseEvent -from .._core._synchronization import Lock as BaseLock -from .._core._synchronization import ( - ResourceGuard, - SemaphoreStatistics, -) -from .._core._synchronization import Semaphore as BaseSemaphore -from .._core._tasks import CancelScope as BaseCancelScope -from .._core._tasks import TaskHandle -from ..abc import IPSockAddrType, UDPPacketType, UNIXDatagramPacketType -from ..abc._eventloop import AsyncBackend, StrOrBytesPath -from ..abc._tasks import T_contra, call_for_coroutine, get_callable_name -from ..streams.memory import MemoryObjectSendStream - -if TYPE_CHECKING: - from _typeshed import FileDescriptorLike - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from exceptiongroup import BaseExceptionGroup - from typing_extensions import TypeVarTuple, Unpack - -T = TypeVar("T") -T_Retval = TypeVar("T_Retval") -T_co = TypeVar("T_co", covariant=True) -T_SockAddr = TypeVar("T_SockAddr", str, IPSockAddrType) -PosArgsT = TypeVarTuple("PosArgsT") -P = ParamSpec("P") - - -def ensure_returns_coro( - func: Callable[P, Awaitable[T_Retval]], -) -> Callable[P, Coroutine[Any, Any, T_Retval]]: - @wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> Coroutine[Any, Any, T_Retval]: - awaitable = func(*args, **kwargs) - # Check the common case first. - if isinstance(awaitable, Coroutine): - return awaitable - elif not isinstance(awaitable, Awaitable): - # The user violated the type annotations. Still, we should pass this on to - # Trio so it can raise with an appropriate message. - return awaitable - else: - - @wraps(func) - async def inner_wrapper() -> T_Retval: - return await awaitable - - return inner_wrapper() - - return wrapper - - -# -# Event loop -# - -RunVar = trio.lowlevel.RunVar - - -# -# Timeouts and cancellation -# - - -class CancelScope(BaseCancelScope): - __slots__ = ("__original",) - - def __new__( - cls, original: trio.CancelScope | None = None, **kwargs: object - ) -> CancelScope: - return object.__new__(cls) - - def __init__(self, original: trio.CancelScope | None = None, **kwargs: Any) -> None: - self.__original = original or trio.CancelScope(**kwargs) - - def __enter__(self) -> CancelScope: - self.__original.__enter__() - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool: - return self.__original.__exit__(exc_type, exc_val, exc_tb) - - def cancel(self, reason: str | None = None) -> None: - self.__original.cancel(reason) - - @property - def deadline(self) -> float: - return self.__original.deadline - - @deadline.setter - def deadline(self, value: float) -> None: - self.__original.deadline = value - - @property - def cancel_called(self) -> bool: - return self.__original.cancel_called - - @property - def cancelled_caught(self) -> bool: - return self.__original.cancelled_caught - - @property - def shield(self) -> bool: - return self.__original.shield - - @shield.setter - def shield(self, value: bool) -> None: - self.__original.shield = value - - -# -# Task groups -# - -empty_start_value = object() - - -class _TrioTaskStatus(Generic[T_contra], abc.TaskStatus[T_contra]): - early_start_value: T_contra | object = empty_start_value - real_task_status: trio.TaskStatus[T_contra | None] | None = None - - def started(self, value: T_contra | None = None) -> None: - if self.real_task_status is None: - if self.early_start_value is not empty_start_value: - raise RuntimeError("called 'started' twice on the same task status") - - self.early_start_value = value - else: - self.real_task_status.started(value) - - -class TaskGroup(abc.TaskGroup): - def __init__(self) -> None: - self._entered = False - self._active = False - self._nursery_manager = trio.open_nursery(strict_exception_groups=True) - self.cancel_scope = None # type: ignore[assignment] - - async def __aenter__(self) -> TaskGroup: - if self._entered: - raise RuntimeError("TaskGroup cannot be entered more than once") - - self._entered = True - self._active = True - self._nursery = await self._nursery_manager.__aenter__() - self.cancel_scope = CancelScope(self._nursery.cancel_scope) - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool: - try: - # trio.Nursery.__exit__ returns bool; .open_nursery has wrong type - return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb) # type: ignore[return-value] - except BaseExceptionGroup as exc: - if not exc.split(trio.Cancelled)[1]: - raise trio.Cancelled._create() from exc - - raise - finally: - del exc_val, exc_tb - self._active = False - - def _check_active(self, coro: Coroutine | None = None) -> None: - if not self._active: - if coro is not None: - coro.close() - - raise RuntimeError( - "This task group is not active; no new tasks can be started." - ) - - def create_task( - self, - coro: Coroutine[Any, Any, T_co], - *, - name: object = None, - context: Context | None = None, - ) -> TaskHandle[T_co]: - if not isinstance(coro, Coroutine): - raise TypeError(f"expected a coroutine, got {coro.__class__.__qualname__}") - - self._check_active(coro) - handle = TaskHandle(coro, name) - if context is not None: - context.run( - partial(self._nursery.start_soon, handle._run_coro, name=handle.name) - ) - else: - self._nursery.start_soon(handle._run_coro, name=handle.name) - - return handle - - async def start( - self, - func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], - *args: Unpack[PosArgsT], - name: object = None, - return_handle: Literal[False] | Literal[True] = False, - ) -> Any: - handle: TaskHandle[T_co] - - async def run_coro_with_task_status( - *, task_status: trio.TaskStatus[Any] - ) -> None: - nonlocal handle - wrapper_task_status = _TrioTaskStatus() - coro = call_for_coroutine(func, args, task_status=wrapper_task_status) - if wrapper_task_status.early_start_value is not empty_start_value: - task_status.started(wrapper_task_status.early_start_value) - else: - wrapper_task_status.real_task_status = task_status - - handle = TaskHandle(coro, name) - await handle._run_coro() - - self._check_active() - final_name = get_callable_name(func, name) - start_value = await self._nursery.start( - run_coro_with_task_status, name=final_name - ) - if return_handle: - handle._start_value = start_value - return handle - else: - return start_value - - -# -# Subprocesses -# - - -@dataclass(eq=False) -class ReceiveStreamWrapper(abc.ByteReceiveStream): - _stream: trio.abc.ReceiveStream - - async def receive(self, max_bytes: int | None = None) -> bytes: - try: - data = await self._stream.receive_some(max_bytes) - except trio.ClosedResourceError as exc: - raise ClosedResourceError from exc.__cause__ - except trio.BrokenResourceError as exc: - raise BrokenResourceError from exc.__cause__ - - if data: - return bytes(data) - else: - raise EndOfStream - - async def aclose(self) -> None: - await self._stream.aclose() - - -@dataclass(eq=False) -class SendStreamWrapper(abc.ByteSendStream): - _stream: trio.abc.SendStream - - async def send(self, item: bytes) -> None: - try: - await self._stream.send_all(item) - except trio.ClosedResourceError as exc: - raise ClosedResourceError from exc.__cause__ - except trio.BrokenResourceError as exc: - raise BrokenResourceError from exc.__cause__ - - async def aclose(self) -> None: - await self._stream.aclose() - - -@dataclass(eq=False) -class Process(abc.Process): - _process: trio.Process - _stdin: abc.ByteSendStream | None - _stdout: abc.ByteReceiveStream | None - _stderr: abc.ByteReceiveStream | None - - async def aclose(self) -> None: - with CancelScope(shield=True): - if self._stdin: - await self._stdin.aclose() - if self._stdout: - await self._stdout.aclose() - if self._stderr: - await self._stderr.aclose() - - try: - await self.wait() - except BaseException: - self.kill() - with CancelScope(shield=True): - await self.wait() - raise - - async def wait(self) -> int: - return await self._process.wait() - - def terminate(self) -> None: - self._process.terminate() - - def kill(self) -> None: - self._process.kill() - - def send_signal(self, signal: Signals) -> None: - self._process.send_signal(signal) - - @property - def pid(self) -> int: - return self._process.pid - - @property - def returncode(self) -> int | None: - return self._process.returncode - - @property - def stdin(self) -> abc.ByteSendStream | None: - return self._stdin - - @property - def stdout(self) -> abc.ByteReceiveStream | None: - return self._stdout - - @property - def stderr(self) -> abc.ByteReceiveStream | None: - return self._stderr - - -class _ProcessPoolShutdownInstrument(trio.abc.Instrument): - def after_run(self) -> None: - super().after_run() - - -current_default_worker_process_limiter: trio.lowlevel.RunVar = RunVar( - "current_default_worker_process_limiter" -) - - -async def _shutdown_process_pool(workers: set[abc.Process]) -> None: - try: - await trio.sleep(math.inf) - except trio.Cancelled: - for process in workers: - if process.returncode is None: - process.kill() - - with CancelScope(shield=True): - for process in workers: - await process.aclose() - - -# -# Sockets and networking -# - - -class _TrioSocketMixin(Generic[T_SockAddr]): - def __init__(self, trio_socket: TrioSocketType) -> None: - self._trio_socket = trio_socket - self._closed = False - - def _check_closed(self) -> None: - if self._closed: - raise ClosedResourceError - if self._trio_socket.fileno() < 0: - raise BrokenResourceError - - @property - def _raw_socket(self) -> socket.socket: - return self._trio_socket._sock # type: ignore[attr-defined] - - async def aclose(self) -> None: - if self._trio_socket.fileno() >= 0: - self._closed = True - self._trio_socket.close() - - def _convert_socket_error(self, exc: BaseException) -> NoReturn: - if isinstance(exc, trio.ClosedResourceError): - raise ClosedResourceError from exc - elif self._trio_socket.fileno() < 0 and self._closed: - raise ClosedResourceError from None - elif isinstance(exc, OSError): - raise BrokenResourceError from exc - else: - raise exc - - -class SocketStream(_TrioSocketMixin, abc.SocketStream): - def __init__(self, trio_socket: TrioSocketType) -> None: - super().__init__(trio_socket) - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - async def receive(self, max_bytes: int = 65536) -> bytes: - with self._receive_guard: - try: - data = await self._trio_socket.recv(max_bytes) - except BaseException as exc: - self._convert_socket_error(exc) - - if data: - return data - else: - raise EndOfStream - - async def send(self, item: bytes) -> None: - with self._send_guard: - view = memoryview(item) - while view: - try: - bytes_sent = await self._trio_socket.send(view) - except BaseException as exc: - self._convert_socket_error(exc) - - view = view[bytes_sent:] - - async def send_eof(self) -> None: - self._trio_socket.shutdown(socket.SHUT_WR) - - -class UNIXSocketStream(SocketStream, abc.UNIXSocketStream): - async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: - if not isinstance(msglen, int) or msglen < 0: - raise ValueError("msglen must be a non-negative integer") - if not isinstance(maxfds, int) or maxfds < 1: - raise ValueError("maxfds must be a positive integer") - - fds = array.array("i") - await trio.lowlevel.checkpoint() - with self._receive_guard: - while True: - try: - message, ancdata, flags, addr = await self._trio_socket.recvmsg( - msglen, socket.CMSG_LEN(maxfds * fds.itemsize) - ) - except BaseException as exc: - self._convert_socket_error(exc) - else: - if not message and not ancdata: - raise EndOfStream - - break - - for cmsg_level, cmsg_type, cmsg_data in ancdata: - if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: - raise RuntimeError( - f"Received unexpected ancillary data; message = {message!r}, " - f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" - ) - - fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) - - return message, list(fds) - - async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: - if not message: - raise ValueError("message must not be empty") - if not fds: - raise ValueError("fds must not be empty") - - filenos: list[int] = [] - for fd in fds: - if isinstance(fd, int): - filenos.append(fd) - elif isinstance(fd, IOBase): - filenos.append(fd.fileno()) - - fdarray = array.array("i", filenos) - await trio.lowlevel.checkpoint() - with self._send_guard: - while True: - try: - await self._trio_socket.sendmsg( - [message], - [ - ( - socket.SOL_SOCKET, - socket.SCM_RIGHTS, - fdarray, - ) - ], - ) - break - except BaseException as exc: - self._convert_socket_error(exc) - - -class TCPSocketListener(_TrioSocketMixin, abc.SocketListener): - def __init__(self, raw_socket: socket.socket): - super().__init__(trio.socket.from_stdlib_socket(raw_socket)) - self._accept_guard = ResourceGuard("accepting connections from") - - async def accept(self) -> SocketStream: - with self._accept_guard: - try: - trio_socket, _addr = await self._trio_socket.accept() - except BaseException as exc: - self._convert_socket_error(exc) - - trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - return SocketStream(trio_socket) - - -class UNIXSocketListener(_TrioSocketMixin, abc.SocketListener): - def __init__(self, raw_socket: socket.socket): - super().__init__(trio.socket.from_stdlib_socket(raw_socket)) - self._accept_guard = ResourceGuard("accepting connections from") - - async def accept(self) -> UNIXSocketStream: - with self._accept_guard: - try: - trio_socket, _addr = await self._trio_socket.accept() - except BaseException as exc: - self._convert_socket_error(exc) - - return UNIXSocketStream(trio_socket) - - -class UDPSocket(_TrioSocketMixin[IPSockAddrType], abc.UDPSocket): - def __init__(self, trio_socket: TrioSocketType) -> None: - super().__init__(trio_socket) - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - async def receive(self) -> tuple[bytes, IPSockAddrType]: - with self._receive_guard: - try: - data, addr = await self._trio_socket.recvfrom(65536) - return data, convert_ipv6_sockaddr(addr) - except BaseException as exc: - self._convert_socket_error(exc) - - async def send(self, item: UDPPacketType) -> None: - with self._send_guard: - try: - await self._trio_socket.sendto(*item) - except BaseException as exc: - self._convert_socket_error(exc) - - -class ConnectedUDPSocket(_TrioSocketMixin[IPSockAddrType], abc.ConnectedUDPSocket): - def __init__(self, trio_socket: TrioSocketType) -> None: - super().__init__(trio_socket) - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - async def receive(self) -> bytes: - with self._receive_guard: - try: - return await self._trio_socket.recv(65536) - except BaseException as exc: - self._convert_socket_error(exc) - - async def send(self, item: bytes) -> None: - with self._send_guard: - try: - await self._trio_socket.send(item) - except BaseException as exc: - self._convert_socket_error(exc) - - -class UNIXDatagramSocket(_TrioSocketMixin[str], abc.UNIXDatagramSocket): - def __init__(self, trio_socket: TrioSocketType) -> None: - super().__init__(trio_socket) - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - async def receive(self) -> UNIXDatagramPacketType: - with self._receive_guard: - try: - data, addr = await self._trio_socket.recvfrom(65536) - return data, addr - except BaseException as exc: - self._convert_socket_error(exc) - - async def send(self, item: UNIXDatagramPacketType) -> None: - with self._send_guard: - try: - await self._trio_socket.sendto(*item) - except BaseException as exc: - self._convert_socket_error(exc) - - -class ConnectedUNIXDatagramSocket( - _TrioSocketMixin[str], abc.ConnectedUNIXDatagramSocket -): - def __init__(self, trio_socket: TrioSocketType) -> None: - super().__init__(trio_socket) - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - async def receive(self) -> bytes: - with self._receive_guard: - try: - return await self._trio_socket.recv(65536) - except BaseException as exc: - self._convert_socket_error(exc) - - async def send(self, item: bytes) -> None: - with self._send_guard: - try: - await self._trio_socket.send(item) - except BaseException as exc: - self._convert_socket_error(exc) - - -# -# Synchronization -# - - -class Event(BaseEvent): - __slots__ = ("__original",) - - def __new__(cls) -> Event: - return object.__new__(cls) - - def __init__(self) -> None: - self.__original = trio.Event() - - def is_set(self) -> bool: - return self.__original.is_set() - - async def wait(self) -> None: - return await self.__original.wait() - - def statistics(self) -> EventStatistics: - orig_statistics = self.__original.statistics() - return EventStatistics(tasks_waiting=orig_statistics.tasks_waiting) - - def set(self) -> None: - self.__original.set() - - -class Lock(BaseLock): - __slots__ = "_fast_acquire", "__original" - - def __new__(cls, *, fast_acquire: bool = False) -> Lock: - return object.__new__(cls) - - def __init__(self, *, fast_acquire: bool = False) -> None: - self._fast_acquire = fast_acquire - self.__original = trio.Lock() - - @staticmethod - def _convert_runtime_error_msg(exc: RuntimeError) -> None: - if exc.args == ("attempt to re-acquire an already held Lock",): - exc.args = ("Attempted to acquire an already held Lock",) - - async def acquire(self) -> None: - if not self._fast_acquire: - try: - await self.__original.acquire() - except RuntimeError as exc: - self._convert_runtime_error_msg(exc) - raise - - return - - # This is the "fast path" where we don't let other tasks run - await trio.lowlevel.checkpoint_if_cancelled() - try: - self.__original.acquire_nowait() - except trio.WouldBlock: - await self.__original._lot.park() - except RuntimeError as exc: - self._convert_runtime_error_msg(exc) - raise - - def acquire_nowait(self) -> None: - try: - self.__original.acquire_nowait() - except trio.WouldBlock: - raise WouldBlock from None - except RuntimeError as exc: - self._convert_runtime_error_msg(exc) - raise - - def locked(self) -> bool: - return self.__original.locked() - - def release(self) -> None: - self.__original.release() - - def statistics(self) -> LockStatistics: - orig_statistics = self.__original.statistics() - owner = TrioTaskInfo(orig_statistics.owner) if orig_statistics.owner else None - return LockStatistics( - orig_statistics.locked, owner, orig_statistics.tasks_waiting - ) - - -class Semaphore(BaseSemaphore): - __slots__ = ("__original",) - - def __new__( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> Semaphore: - return object.__new__(cls) - - def __init__( - self, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> None: - super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire) - self.__original = trio.Semaphore(initial_value, max_value=max_value) - - async def acquire(self) -> None: - if not self._fast_acquire: - await self.__original.acquire() - return - - # This is the "fast path" where we don't let other tasks run - await trio.lowlevel.checkpoint_if_cancelled() - try: - self.__original.acquire_nowait() - except trio.WouldBlock: - await self.__original._lot.park() - - def acquire_nowait(self) -> None: - try: - self.__original.acquire_nowait() - except trio.WouldBlock: - raise WouldBlock from None - - @property - def max_value(self) -> int | None: - return self.__original.max_value - - @property - def value(self) -> int: - return self.__original.value - - def release(self) -> None: - self.__original.release() - - def statistics(self) -> SemaphoreStatistics: - orig_statistics = self.__original.statistics() - return SemaphoreStatistics(orig_statistics.tasks_waiting) - - -class CapacityLimiter(BaseCapacityLimiter): - __slots__ = ("__original",) - - def __new__( - cls, - total_tokens: float | None = None, - *, - original: trio.CapacityLimiter | None = None, - ) -> CapacityLimiter: - return object.__new__(cls) - - def __init__( - self, - total_tokens: float | None = None, - *, - original: trio.CapacityLimiter | None = None, - ) -> None: - if original is not None: - self.__original = original - else: - assert total_tokens is not None - self.__original = trio.CapacityLimiter(total_tokens) - - async def __aenter__(self) -> None: - return await self.__original.__aenter__() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - await self.__original.__aexit__(exc_type, exc_val, exc_tb) - - @property - def total_tokens(self) -> float: - return self.__original.total_tokens - - @total_tokens.setter - def total_tokens(self, value: float) -> None: - self.__original.total_tokens = value - - @property - def borrowed_tokens(self) -> int: - return self.__original.borrowed_tokens - - @property - def available_tokens(self) -> float: - return self.__original.available_tokens - - def acquire_nowait(self) -> None: - self.__original.acquire_nowait() - - def acquire_on_behalf_of_nowait(self, borrower: object) -> None: - self.__original.acquire_on_behalf_of_nowait(borrower) - - async def acquire(self) -> None: - await self.__original.acquire() - - async def acquire_on_behalf_of(self, borrower: object) -> None: - await self.__original.acquire_on_behalf_of(borrower) - - def release(self) -> None: - return self.__original.release() - - def release_on_behalf_of(self, borrower: object) -> None: - return self.__original.release_on_behalf_of(borrower) - - def statistics(self) -> CapacityLimiterStatistics: - orig = self.__original.statistics() - return CapacityLimiterStatistics( - borrowed_tokens=orig.borrowed_tokens, - total_tokens=orig.total_tokens, - borrowers=tuple(orig.borrowers), - tasks_waiting=orig.tasks_waiting, - ) - - -_capacity_limiter_wrapper: trio.lowlevel.RunVar = RunVar("_capacity_limiter_wrapper") - - -# -# Signal handling -# - - -class _SignalReceiver: - _iterator: AsyncIterator[int] - - def __init__(self, signals: tuple[Signals, ...]): - self._signals = signals - - def __enter__(self) -> _SignalReceiver: - self._cm = trio.open_signal_receiver(*self._signals) - self._iterator = self._cm.__enter__() - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - return self._cm.__exit__(exc_type, exc_val, exc_tb) - - def __aiter__(self) -> _SignalReceiver: - return self - - async def __anext__(self) -> Signals: - signum = await self._iterator.__anext__() - return Signals(signum) - - -# -# Testing and debugging -# - - -class TestRunner(abc.TestRunner): - def __init__(self, **options: Any) -> None: - from queue import Queue - - self._call_queue: Queue[Callable[[], object]] = Queue() - self._send_stream: ( - MemoryObjectSendStream[tuple[Awaitable[Any], list[Outcome]]] | None - ) = None - self._options = options - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: types.TracebackType | None, - ) -> None: - if self._send_stream: - self._send_stream.close() - while self._send_stream is not None: - self._call_queue.get()() - - def is_running(self) -> bool: - return trio.lowlevel.in_trio_task() - - async def _run_tests_and_fixtures(self) -> None: - self._send_stream, receive_stream = create_memory_object_stream[ - tuple[Awaitable[Any], list[Outcome]] - ](1) - with receive_stream: - async for awaitable, outcome_holder in receive_stream: - try: - retval = await awaitable - except BaseException as exc: - outcome_holder.append(Error(exc)) - else: - outcome_holder.append(Value(retval)) - - def _main_task_finished(self, outcome: object) -> None: - self._send_stream = None - - def _call_in_runner_task( - self, - func: Callable[P, Awaitable[T_Retval]], - /, - *args: P.args, - **kwargs: P.kwargs, - ) -> T_Retval: - if self._send_stream is None: - trio.lowlevel.start_guest_run( - self._run_tests_and_fixtures, - run_sync_soon_threadsafe=self._call_queue.put, - done_callback=self._main_task_finished, - **self._options, - ) - while self._send_stream is None: - self._call_queue.get()() - - outcome_holder: list[Outcome] = [] - self._send_stream.send_nowait((func(*args, **kwargs), outcome_holder)) - while not outcome_holder: - self._call_queue.get()() - - return outcome_holder[0].unwrap() - - def run_asyncgen_fixture( - self, - fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], - kwargs: dict[str, Any], - ) -> Iterable[T_Retval]: - asyncgen = fixture_func(**kwargs) - fixturevalue: T_Retval = self._call_in_runner_task(asyncgen.asend, None) - - yield fixturevalue - - try: - self._call_in_runner_task(asyncgen.asend, None) - except StopAsyncIteration: - pass - else: - self._call_in_runner_task(asyncgen.aclose) - raise RuntimeError("Async generator fixture did not stop") - - def run_fixture( - self, - fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], - kwargs: dict[str, Any], - ) -> T_Retval: - return self._call_in_runner_task(fixture_func, **kwargs) - - def run_test( - self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] - ) -> None: - self._call_in_runner_task(test_func, **kwargs) - - -class TrioTaskInfo(TaskInfo): - def __init__(self, task: trio.lowlevel.Task): - parent_id = None - if task.parent_nursery and task.parent_nursery.parent_task: - parent_id = id(task.parent_nursery.parent_task) - - super().__init__(id(task), parent_id, task.name, task.coro) - self._task = weakref.proxy(task) - - def has_pending_cancellation(self) -> bool: - try: - return self._task._cancel_status.effectively_cancelled - except ReferenceError: - # If the task is no longer around, it surely doesn't have a cancellation - # pending - return False - - -class TrioBackend(AsyncBackend): - @classmethod - def run( - cls, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - options: dict[str, Any], - ) -> T_Retval: - assert not kwargs, "unreachable, and not supported by Trio" - return trio.run(ensure_returns_coro(func), *args, **options) - - @classmethod - def current_token(cls) -> object: - return trio.lowlevel.current_trio_token() - - @classmethod - def current_time(cls) -> float: - return trio.current_time() - - @classmethod - def cancelled_exception_class(cls) -> type[BaseException]: - return trio.Cancelled - - @classmethod - async def checkpoint(cls) -> None: - await trio.lowlevel.checkpoint() - - @classmethod - async def checkpoint_if_cancelled(cls) -> None: - await trio.lowlevel.checkpoint_if_cancelled() - - @classmethod - async def cancel_shielded_checkpoint(cls) -> None: - await trio.lowlevel.cancel_shielded_checkpoint() - - @classmethod - async def sleep(cls, delay: float) -> None: - await trio.sleep(delay) - - @classmethod - def create_cancel_scope( - cls, *, deadline: float = math.inf, shield: bool = False - ) -> abc.CancelScope: - return CancelScope(deadline=deadline, shield=shield) - - @classmethod - def current_effective_deadline(cls) -> float: - return trio.current_effective_deadline() - - @classmethod - def create_task_group(cls) -> abc.TaskGroup: - return TaskGroup() - - @classmethod - def create_event(cls) -> abc.Event: - return Event() - - @classmethod - def create_lock(cls, *, fast_acquire: bool) -> Lock: - return Lock(fast_acquire=fast_acquire) - - @classmethod - def create_semaphore( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> abc.Semaphore: - return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire) - - @classmethod - def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: - return CapacityLimiter(total_tokens) - - @classmethod - async def run_sync_in_worker_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - abandon_on_cancel: bool = False, - limiter: abc.CapacityLimiter | None = None, - ) -> T_Retval: - def wrapper() -> T_Retval: - with claim_worker_thread(TrioBackend, token): - return func(*args) - - token = TrioBackend.current_token() - return await run_sync( - wrapper, - abandon_on_cancel=abandon_on_cancel, - limiter=cast(trio.CapacityLimiter, limiter), - ) - - @classmethod - def check_cancelled(cls) -> None: - trio.from_thread.check_cancelled() - - @classmethod - def run_async_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_co: - trio_token = cast("trio.lowlevel.TrioToken | None", token) - try: - return trio.from_thread.run(func, *args, trio_token=trio_token) - except trio.RunFinishedError: - raise RunFinishedError from None - - @classmethod - def run_sync_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_Retval: - trio_token = cast("trio.lowlevel.TrioToken | None", token) - try: - return trio.from_thread.run_sync(func, *args, trio_token=trio_token) - except trio.RunFinishedError: - raise RunFinishedError from None - - @classmethod - async def open_process( - cls, - command: StrOrBytesPath | Sequence[StrOrBytesPath], - *, - stdin: int | IO[Any] | None, - stdout: int | IO[Any] | None, - stderr: int | IO[Any] | None, - **kwargs: Any, - ) -> Process: - def convert_item(item: StrOrBytesPath) -> str: - str_or_bytes = os.fspath(item) - if isinstance(str_or_bytes, str): - return str_or_bytes - else: - return os.fsdecode(str_or_bytes) - - if isinstance(command, (str, bytes, PathLike)): - process = await trio.lowlevel.open_process( - convert_item(command), - stdin=stdin, - stdout=stdout, - stderr=stderr, - shell=True, - **kwargs, - ) - else: - process = await trio.lowlevel.open_process( - [convert_item(item) for item in command], - stdin=stdin, - stdout=stdout, - stderr=stderr, - shell=False, - **kwargs, - ) - - stdin_stream = SendStreamWrapper(process.stdin) if process.stdin else None - stdout_stream = ReceiveStreamWrapper(process.stdout) if process.stdout else None - stderr_stream = ReceiveStreamWrapper(process.stderr) if process.stderr else None - return Process(process, stdin_stream, stdout_stream, stderr_stream) - - @classmethod - def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: - trio.lowlevel.spawn_system_task(_shutdown_process_pool, workers) - - @classmethod - async def connect_tcp( - cls, host: str, port: int, local_address: IPSockAddrType | None = None - ) -> SocketStream: - family = socket.AF_INET6 if ":" in host else socket.AF_INET - trio_socket = trio.socket.socket(family) - trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - if local_address: - await trio_socket.bind(local_address) - - try: - await trio_socket.connect((host, port)) - except BaseException: - trio_socket.close() - raise - - return SocketStream(trio_socket) - - @classmethod - async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: - trio_socket = trio.socket.socket(socket.AF_UNIX) - try: - await trio_socket.connect(path) - except BaseException: - trio_socket.close() - raise - - return UNIXSocketStream(trio_socket) - - @classmethod - def create_tcp_listener(cls, sock: socket.socket) -> abc.SocketListener: - return TCPSocketListener(sock) - - @classmethod - def create_unix_listener(cls, sock: socket.socket) -> abc.SocketListener: - return UNIXSocketListener(sock) - - @classmethod - async def create_udp_socket( - cls, - family: socket.AddressFamily, - local_address: IPSockAddrType | None, - remote_address: IPSockAddrType | None, - reuse_port: bool, - ) -> UDPSocket | ConnectedUDPSocket: - trio_socket = trio.socket.socket(family=family, type=socket.SOCK_DGRAM) - - if reuse_port: - trio_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - - if local_address: - await trio_socket.bind(local_address) - - if remote_address: - await trio_socket.connect(remote_address) - return ConnectedUDPSocket(trio_socket) - else: - return UDPSocket(trio_socket) - - @classmethod - @overload - async def create_unix_datagram_socket( - cls, raw_socket: socket.socket, remote_path: None - ) -> abc.UNIXDatagramSocket: ... - - @classmethod - @overload - async def create_unix_datagram_socket( - cls, raw_socket: socket.socket, remote_path: str | bytes - ) -> abc.ConnectedUNIXDatagramSocket: ... - - @classmethod - async def create_unix_datagram_socket( - cls, raw_socket: socket.socket, remote_path: str | bytes | None - ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: - trio_socket = trio.socket.from_stdlib_socket(raw_socket) - - if remote_path: - await trio_socket.connect(remote_path) - return ConnectedUNIXDatagramSocket(trio_socket) - else: - return UNIXDatagramSocket(trio_socket) - - @classmethod - async def getaddrinfo( - cls, - host: bytes | str | None, - port: str | int | None, - *, - family: int | AddressFamily = 0, - type: int | SocketKind = 0, - proto: int = 0, - flags: int = 0, - ) -> Sequence[ - tuple[ - AddressFamily, - SocketKind, - int, - str, - tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], - ] - ]: - return await trio.socket.getaddrinfo(host, port, family, type, proto, flags) - - @classmethod - async def getnameinfo( - cls, sockaddr: IPSockAddrType, flags: int = 0 - ) -> tuple[str, str]: - return await trio.socket.getnameinfo(sockaddr, flags) - - @classmethod - async def wait_readable(cls, obj: FileDescriptorLike) -> None: - try: - await wait_readable(obj) - except trio.ClosedResourceError as exc: - raise ClosedResourceError().with_traceback(exc.__traceback__) from None - except trio.BusyResourceError: - raise BusyResourceError("reading from") from None - - @classmethod - async def wait_writable(cls, obj: FileDescriptorLike) -> None: - try: - await wait_writable(obj) - except trio.ClosedResourceError as exc: - raise ClosedResourceError().with_traceback(exc.__traceback__) from None - except trio.BusyResourceError: - raise BusyResourceError("writing to") from None - - @classmethod - def notify_closing(cls, obj: FileDescriptorLike) -> None: - notify_closing(obj) - - @classmethod - async def wrap_listener_socket(cls, sock: socket.socket) -> abc.SocketListener: - if hasattr(socket, "AF_UNIX") and sock.family == socket.AF_UNIX: - return UNIXSocketListener(sock) - - return TCPSocketListener(sock) - - @classmethod - async def wrap_stream_socket(cls, sock: socket.socket) -> SocketStream: - trio_sock = trio.socket.from_stdlib_socket(sock) - return SocketStream(trio_sock) - - @classmethod - async def wrap_unix_stream_socket(cls, sock: socket.socket) -> UNIXSocketStream: - trio_sock = trio.socket.from_stdlib_socket(sock) - return UNIXSocketStream(trio_sock) - - @classmethod - async def wrap_udp_socket(cls, sock: socket.socket) -> UDPSocket: - trio_sock = trio.socket.from_stdlib_socket(sock) - return UDPSocket(trio_sock) - - @classmethod - async def wrap_connected_udp_socket(cls, sock: socket.socket) -> ConnectedUDPSocket: - trio_sock = trio.socket.from_stdlib_socket(sock) - return ConnectedUDPSocket(trio_sock) - - @classmethod - async def wrap_unix_datagram_socket(cls, sock: socket.socket) -> UNIXDatagramSocket: - trio_sock = trio.socket.from_stdlib_socket(sock) - return UNIXDatagramSocket(trio_sock) - - @classmethod - async def wrap_connected_unix_datagram_socket( - cls, sock: socket.socket - ) -> ConnectedUNIXDatagramSocket: - trio_sock = trio.socket.from_stdlib_socket(sock) - return ConnectedUNIXDatagramSocket(trio_sock) - - @classmethod - def current_default_thread_limiter(cls) -> CapacityLimiter: - try: - return _capacity_limiter_wrapper.get() - except LookupError: - limiter = CapacityLimiter( - original=trio.to_thread.current_default_thread_limiter() - ) - _capacity_limiter_wrapper.set(limiter) - return limiter - - @classmethod - def open_signal_receiver( - cls, *signals: Signals - ) -> AbstractContextManager[AsyncIterator[Signals]]: - return _SignalReceiver(signals) - - @classmethod - def get_current_task(cls) -> TaskInfo: - task = current_task() - return TrioTaskInfo(task) - - @classmethod - def get_running_tasks(cls) -> Sequence[TaskInfo]: - root_task = current_root_task() - assert root_task - task_infos = [TrioTaskInfo(root_task)] - nurseries = root_task.child_nurseries - while nurseries: - new_nurseries: list[trio.Nursery] = [] - for nursery in nurseries: - for task in nursery.child_tasks: - task_infos.append(TrioTaskInfo(task)) - new_nurseries.extend(task.child_nurseries) - - nurseries = new_nurseries - - return task_infos - - @classmethod - async def wait_all_tasks_blocked(cls) -> None: - from trio.testing import wait_all_tasks_blocked - - await wait_all_tasks_blocked() - - @classmethod - def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: - return TestRunner(**options) - - -backend_class = TrioBackend diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/__init__.py b/.venv/lib/python3.12/site-packages/anyio/_core/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py b/.venv/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py deleted file mode 100644 index 9f35bae5..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_asyncio_selector_thread.py +++ /dev/null @@ -1,167 +0,0 @@ -from __future__ import annotations - -import asyncio -import socket -import threading -from collections.abc import Callable -from selectors import EVENT_READ, EVENT_WRITE, DefaultSelector -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from _typeshed import FileDescriptorLike - -_selector_lock = threading.Lock() -_selector: Selector | None = None - - -class Selector: - def __init__(self) -> None: - self._thread = threading.Thread(target=self.run, name="AnyIO socket selector") - self._selector = DefaultSelector() - self._send, self._receive = socket.socketpair() - self._send.setblocking(False) - self._receive.setblocking(False) - # This somewhat reduces the amount of memory wasted queueing up data - # for wakeups. With these settings, maximum number of 1-byte sends - # before getting BlockingIOError: - # Linux 4.8: 6 - # macOS (darwin 15.5): 1 - # Windows 10: 525347 - # Windows you're weird. (And on Windows setting SNDBUF to 0 makes send - # blocking, even on non-blocking sockets, so don't do that.) - self._receive.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1) - self._send.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1) - # On Windows this is a TCP socket so this might matter. On other - # platforms this fails b/c AF_UNIX sockets aren't actually TCP. - try: - self._send.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - except OSError: - pass - - self._selector.register(self._receive, EVENT_READ) - self._closed = False - - def start(self) -> None: - self._thread.start() - threading._register_atexit(self._stop) # type: ignore[attr-defined] - - def _stop(self) -> None: - global _selector - self._closed = True - self._notify_self() - self._send.close() - self._thread.join() - self._selector.unregister(self._receive) - self._receive.close() - self._selector.close() - _selector = None - assert not self._selector.get_map(), ( - "selector still has registered file descriptors after shutdown" - ) - - def _notify_self(self) -> None: - try: - self._send.send(b"\x00") - except BlockingIOError: - pass - - def add_reader(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None: - loop = asyncio.get_running_loop() - try: - key = self._selector.get_key(fd) - except KeyError: - self._selector.register(fd, EVENT_READ, {EVENT_READ: (loop, callback)}) - else: - if EVENT_READ in key.data: - raise ValueError( - "this file descriptor is already registered for reading" - ) - - key.data[EVENT_READ] = loop, callback - self._selector.modify(fd, key.events | EVENT_READ, key.data) - - self._notify_self() - - def add_writer(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None: - loop = asyncio.get_running_loop() - try: - key = self._selector.get_key(fd) - except KeyError: - self._selector.register(fd, EVENT_WRITE, {EVENT_WRITE: (loop, callback)}) - else: - if EVENT_WRITE in key.data: - raise ValueError( - "this file descriptor is already registered for writing" - ) - - key.data[EVENT_WRITE] = loop, callback - self._selector.modify(fd, key.events | EVENT_WRITE, key.data) - - self._notify_self() - - def remove_reader(self, fd: FileDescriptorLike) -> bool: - try: - key = self._selector.get_key(fd) - except KeyError: - return False - - if new_events := key.events ^ EVENT_READ: - del key.data[EVENT_READ] - self._selector.modify(fd, new_events, key.data) - else: - self._selector.unregister(fd) - - return True - - def remove_writer(self, fd: FileDescriptorLike) -> bool: - try: - key = self._selector.get_key(fd) - except KeyError: - return False - - if new_events := key.events ^ EVENT_WRITE: - del key.data[EVENT_WRITE] - self._selector.modify(fd, new_events, key.data) - else: - self._selector.unregister(fd) - - return True - - def run(self) -> None: - while not self._closed: - for key, events in self._selector.select(): - if key.fileobj is self._receive: - try: - while self._receive.recv(4096): - pass - except BlockingIOError: - pass - - continue - - if events & EVENT_READ: - loop, callback = key.data[EVENT_READ] - self.remove_reader(key.fd) - try: - loop.call_soon_threadsafe(callback) - except RuntimeError: - pass # the loop was already closed - - if events & EVENT_WRITE: - loop, callback = key.data[EVENT_WRITE] - self.remove_writer(key.fd) - try: - loop.call_soon_threadsafe(callback) - except RuntimeError: - pass # the loop was already closed - - -def get_selector() -> Selector: - global _selector - - with _selector_lock: - if _selector is None: - _selector = Selector() - _selector.start() - - return _selector diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_contextmanagers.py b/.venv/lib/python3.12/site-packages/anyio/_core/_contextmanagers.py deleted file mode 100644 index 302f32b0..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_contextmanagers.py +++ /dev/null @@ -1,200 +0,0 @@ -from __future__ import annotations - -from abc import abstractmethod -from contextlib import AbstractAsyncContextManager, AbstractContextManager -from inspect import isasyncgen, iscoroutine, isgenerator -from types import TracebackType -from typing import Protocol, TypeVar, cast, final - -_T_co = TypeVar("_T_co", covariant=True) -_ExitT_co = TypeVar("_ExitT_co", covariant=True, bound="bool | None") - - -class _SupportsCtxMgr(Protocol[_T_co, _ExitT_co]): - def __contextmanager__(self) -> AbstractContextManager[_T_co, _ExitT_co]: ... - - -class _SupportsAsyncCtxMgr(Protocol[_T_co, _ExitT_co]): - def __asynccontextmanager__( - self, - ) -> AbstractAsyncContextManager[_T_co, _ExitT_co]: ... - - -class ContextManagerMixin: - """ - Mixin class providing context manager functionality via a generator-based - implementation. - - This class allows you to implement a context manager via :meth:`__contextmanager__` - which should return a generator. The mechanics are meant to mirror those of - :func:`@contextmanager `. - - .. note:: Classes using this mix-in are not reentrant as context managers, meaning - that once you enter it, you can't re-enter before first exiting it. - - .. seealso:: :doc:`contextmanagers` - """ - - __cm: AbstractContextManager[object, bool | None] | None = None - - @final - def __enter__(self: _SupportsCtxMgr[_T_co, bool | None]) -> _T_co: - # Needed for mypy to assume self still has the __cm member - assert isinstance(self, ContextManagerMixin) - if self.__cm is not None: - raise RuntimeError( - f"this {self.__class__.__qualname__} has already been entered" - ) - - cm = self.__contextmanager__() - if not isinstance(cm, AbstractContextManager): - if isgenerator(cm): - raise TypeError( - "__contextmanager__() returned a generator object instead of " - "a context manager. Did you forget to add the @contextmanager " - "decorator?" - ) - - raise TypeError( - f"__contextmanager__() did not return a context manager object, " - f"but {cm.__class__!r}" - ) - - if cm is self: - raise TypeError( - f"{self.__class__.__qualname__}.__contextmanager__() returned " - f"self. Did you forget to add the @contextmanager decorator and a " - f"'yield' statement?" - ) - - value = cm.__enter__() - self.__cm = cm - return value - - @final - def __exit__( - self: _SupportsCtxMgr[object, _ExitT_co], - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> _ExitT_co: - # Needed for mypy to assume self still has the __cm member - assert isinstance(self, ContextManagerMixin) - if self.__cm is None: - raise RuntimeError( - f"this {self.__class__.__qualname__} has not been entered yet" - ) - - # Prevent circular references - cm = self.__cm - del self.__cm - - return cast(_ExitT_co, cm.__exit__(exc_type, exc_val, exc_tb)) - - @abstractmethod - def __contextmanager__(self) -> AbstractContextManager[object, bool | None]: - """ - Implement your context manager logic here. - - This method **must** be decorated with - :func:`@contextmanager `. - - .. note:: Remember that the ``yield`` will raise any exception raised in the - enclosed context block, so use a ``finally:`` block to clean up resources! - - :return: a context manager object - """ - - -class AsyncContextManagerMixin: - """ - Mixin class providing async context manager functionality via a generator-based - implementation. - - This class allows you to implement a context manager via - :meth:`__asynccontextmanager__`. The mechanics are meant to mirror those of - :func:`@asynccontextmanager `. - - .. note:: Classes using this mix-in are not reentrant as context managers, meaning - that once you enter it, you can't re-enter before first exiting it. - - .. seealso:: :doc:`contextmanagers` - """ - - __cm: AbstractAsyncContextManager[object, bool | None] | None = None - - @final - async def __aenter__(self: _SupportsAsyncCtxMgr[_T_co, bool | None]) -> _T_co: - # Needed for mypy to assume self still has the __cm member - assert isinstance(self, AsyncContextManagerMixin) - if self.__cm is not None: - raise RuntimeError( - f"this {self.__class__.__qualname__} has already been entered" - ) - - cm = self.__asynccontextmanager__() - if not isinstance(cm, AbstractAsyncContextManager): - if isasyncgen(cm): - raise TypeError( - "__asynccontextmanager__() returned an async generator instead of " - "an async context manager. Did you forget to add the " - "@asynccontextmanager decorator?" - ) - elif iscoroutine(cm): - cm.close() - raise TypeError( - "__asynccontextmanager__() returned a coroutine object instead of " - "an async context manager. Did you forget to add the " - "@asynccontextmanager decorator and a 'yield' statement?" - ) - - raise TypeError( - f"__asynccontextmanager__() did not return an async context manager, " - f"but {cm.__class__!r}" - ) - - if cm is self: - raise TypeError( - f"{self.__class__.__qualname__}.__asynccontextmanager__() returned " - f"self. Did you forget to add the @asynccontextmanager decorator and a " - f"'yield' statement?" - ) - - value = await cm.__aenter__() - self.__cm = cm - return value - - @final - async def __aexit__( - self: _SupportsAsyncCtxMgr[object, _ExitT_co], - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> _ExitT_co: - assert isinstance(self, AsyncContextManagerMixin) - if self.__cm is None: - raise RuntimeError( - f"this {self.__class__.__qualname__} has not been entered yet" - ) - - # Prevent circular references - cm = self.__cm - del self.__cm - - return cast(_ExitT_co, await cm.__aexit__(exc_type, exc_val, exc_tb)) - - @abstractmethod - def __asynccontextmanager__( - self, - ) -> AbstractAsyncContextManager[object, bool | None]: - """ - Implement your async context manager logic here. - - This method **must** be decorated with - :func:`@asynccontextmanager `. - - .. note:: Remember that the ``yield`` will raise any exception raised in the - enclosed context block, so use a ``finally:`` block to clean up resources! - - :return: an async context manager object - """ diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_eventloop.py b/.venv/lib/python3.12/site-packages/anyio/_core/_eventloop.py deleted file mode 100644 index a3e2ab1c..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_eventloop.py +++ /dev/null @@ -1,240 +0,0 @@ -from __future__ import annotations - -import math -import sys -import threading -from collections.abc import Awaitable, Callable, Generator -from contextlib import contextmanager -from contextvars import Token -from importlib import import_module -from typing import TYPE_CHECKING, Any, TypeVar - -from ._exceptions import NoEventLoopError - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -sniffio: Any -try: - import sniffio -except ModuleNotFoundError: - sniffio = None - -if TYPE_CHECKING: - from ..abc import AsyncBackend - -# This must be updated when new backends are introduced -BACKENDS = "asyncio", "trio" - -T_Retval = TypeVar("T_Retval") -PosArgsT = TypeVarTuple("PosArgsT") - -threadlocals = threading.local() -loaded_backends: dict[str, type[AsyncBackend]] = {} - - -def run( - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - *args: Unpack[PosArgsT], - backend: str = "asyncio", - backend_options: dict[str, Any] | None = None, -) -> T_Retval: - """ - Run the given coroutine function in an asynchronous event loop. - - The current thread must not be already running an event loop. - - :param func: a coroutine function - :param args: positional arguments to ``func`` - :param backend: name of the asynchronous event loop implementation – currently - either ``asyncio`` or ``trio`` - :param backend_options: keyword arguments to call the backend ``run()`` - implementation with (documented :ref:`here `) - :return: the return value of the coroutine function - :raises RuntimeError: if an asynchronous event loop is already running in this - thread - :raises LookupError: if the named backend is not found - - """ - if asynclib_name := current_async_library(): - raise RuntimeError(f"Already running {asynclib_name} in this thread") - - try: - async_backend = get_async_backend(backend) - except ImportError as exc: - if backend in BACKENDS: - raise LookupError( - f"Backend {backend!r} is not available. " - f"Install it with: pip install anyio[{backend}]" - ) from exc - - raise LookupError(f"No such backend: {backend}") from exc - - token = None - if asynclib_name is None: - # Since we're in control of the event loop, we can cache the name of the async - # library - token = set_current_async_library(backend) - - try: - backend_options = backend_options or {} - return async_backend.run(func, args, {}, backend_options) - finally: - reset_current_async_library(token) - - -async def sleep(delay: float) -> None: - """ - Pause the current task for the specified duration. - - :param delay: the duration, in seconds - - """ - return await get_async_backend().sleep(delay) - - -async def sleep_forever() -> None: - """ - Pause the current task until it's cancelled. - - This is a shortcut for ``sleep(math.inf)``. - - .. versionadded:: 3.1 - - """ - await sleep(math.inf) - - -async def sleep_until(deadline: float) -> None: - """ - Pause the current task until the given time. - - :param deadline: the absolute time to wake up at (according to the internal - monotonic clock of the event loop) - - .. versionadded:: 3.1 - - """ - now = current_time() - await sleep(max(deadline - now, 0)) - - -def current_time() -> float: - """ - Return the current value of the event loop's internal clock. - - :return: the clock value (seconds) - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - return get_async_backend().current_time() - - -def get_all_backends() -> tuple[str, ...]: - """Return a tuple of the names of all built-in backends.""" - return BACKENDS - - -def get_available_backends() -> tuple[str, ...]: - """ - Test for the availability of built-in backends. - - :return a tuple of the built-in backend names that were successfully imported - - .. versionadded:: 4.12 - - """ - available_backends: list[str] = [] - for backend_name in get_all_backends(): - try: - get_async_backend(backend_name) - except ImportError: - continue - - available_backends.append(backend_name) - - return tuple(available_backends) - - -def get_cancelled_exc_class() -> type[BaseException]: - """ - Return the current async library's cancellation exception class. - - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - return get_async_backend().cancelled_exception_class() - - -# -# Private API -# - - -@contextmanager -def claim_worker_thread( - backend_class: type[AsyncBackend], token: object -) -> Generator[Any, None, None]: - from ..lowlevel import EventLoopToken - - threadlocals.current_token = EventLoopToken(backend_class, token) - try: - yield - finally: - del threadlocals.current_token - - -def get_async_backend(asynclib_name: str | None = None) -> type[AsyncBackend]: - if asynclib_name is None: - asynclib_name = current_async_library() - if not asynclib_name: - raise NoEventLoopError( - f"Not currently running on any asynchronous event loop. " - f"Available async backends: {', '.join(get_all_backends())}" - ) - - # We use our own dict instead of sys.modules to get the already imported back-end - # class because the appropriate modules in sys.modules could potentially be only - # partially initialized - try: - return loaded_backends[asynclib_name] - except KeyError: - module = import_module(f"anyio._backends._{asynclib_name}") - loaded_backends[asynclib_name] = module.backend_class - return module.backend_class - - -def current_async_library() -> str | None: - if sniffio is None: - # If sniffio is not installed, we assume we're either running asyncio or nothing - import asyncio - - try: - asyncio.get_running_loop() - return "asyncio" - except RuntimeError: - pass - else: - try: - return sniffio.current_async_library() - except sniffio.AsyncLibraryNotFoundError: - pass - - return None - - -def set_current_async_library(asynclib_name: str | None) -> Token | None: - # no-op if sniffio is not installed - if sniffio is None: - return None - - return sniffio.current_async_library_cvar.set(asynclib_name) - - -def reset_current_async_library(token: Token | None) -> None: - if token is not None: - sniffio.current_async_library_cvar.reset(token) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_exceptions.py b/.venv/lib/python3.12/site-packages/anyio/_core/_exceptions.py deleted file mode 100644 index cd6eb9b5..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_exceptions.py +++ /dev/null @@ -1,177 +0,0 @@ -from __future__ import annotations - -import sys -from collections.abc import Generator -from textwrap import dedent -from typing import Any - -if sys.version_info < (3, 11): - from exceptiongroup import BaseExceptionGroup - - -class BrokenResourceError(Exception): - """ - Raised when trying to use a resource that has been rendered unusable due to external - causes (e.g. a send stream whose peer has disconnected). - """ - - -class BrokenWorkerProcess(Exception): - """ - Raised by :meth:`~anyio.to_process.run_sync` if the worker process terminates abruptly or - otherwise misbehaves. - """ - - -class BrokenWorkerInterpreter(Exception): - """ - Raised by :meth:`~anyio.to_interpreter.run_sync` if an unexpected exception is - raised in the subinterpreter. - """ - - def __init__(self, excinfo: Any): - # This was adapted from concurrent.futures.interpreter.ExecutionFailed - msg = excinfo.formatted - if not msg: - if excinfo.type and excinfo.msg: - msg = f"{excinfo.type.__name__}: {excinfo.msg}" - else: - msg = excinfo.type.__name__ or excinfo.msg - - super().__init__(msg) - self.excinfo = excinfo - - def __str__(self) -> str: - try: - formatted = self.excinfo.errdisplay - except Exception: - return super().__str__() - else: - return dedent( - f""" - {super().__str__()} - - Uncaught in the interpreter: - - {formatted} - """.strip() - ) - - -class BusyResourceError(Exception): - """ - Raised when two tasks are trying to read from or write to the same resource - concurrently. - """ - - def __init__(self, action: str): - super().__init__(f"Another task is already {action} this resource") - - -class ClosedResourceError(Exception): - """Raised when trying to use a resource that has been closed.""" - - -class ConnectionFailed(OSError): - """ - Raised when a connection attempt fails. - - .. note:: This class inherits from :exc:`OSError` for backwards compatibility. - """ - - -def iterate_exceptions( - exception: BaseException, -) -> Generator[BaseException, None, None]: - if isinstance(exception, BaseExceptionGroup): - for exc in exception.exceptions: - yield from iterate_exceptions(exc) - else: - yield exception - - -class DelimiterNotFound(Exception): - """ - Raised during - :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the - maximum number of bytes has been read without the delimiter being found. - """ - - def __init__(self, max_bytes: int) -> None: - super().__init__( - f"The delimiter was not found among the first {max_bytes} bytes" - ) - - -class EndOfStream(Exception): - """ - Raised when trying to read from a stream that has been closed from the other end. - """ - - -class IncompleteRead(Exception): - """ - Raised during - :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_exactly` or - :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the - connection is closed before the requested amount of bytes has been read. - """ - - def __init__(self) -> None: - super().__init__( - "The stream was closed before the read operation could be completed" - ) - - -class TypedAttributeLookupError(LookupError): - """ - Raised by :meth:`~anyio.TypedAttributeProvider.extra` when the given typed attribute - is not found and no default value has been given. - """ - - -class WouldBlock(Exception): - """Raised by ``X_nowait`` functions if ``X()`` would block.""" - - -class NoEventLoopError(RuntimeError): - """ - Raised by several functions that require an event loop to be running in the current - thread when there is no running event loop. - - This is also raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync` - if not calling from an AnyIO worker thread, and no ``token`` was passed. - """ - - -class RunFinishedError(RuntimeError): - """ - Raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync` if the event - loop associated with the explicitly passed token has already finished. - """ - - def __init__(self) -> None: - super().__init__( - "The event loop associated with the given token has already finished" - ) - - -class TaskFailed(Exception): - """ - Raised when awaiting on, or attempting to access the return value of, a - :class:`.TaskHandle` that raised an exception. - """ - - -class TaskCancelled(TaskFailed): - """ - Raised when awaiting on, or attempting to access the return value of, a - :class:`.TaskHandle` that was cancelled. - """ - - -class TaskNotFinished(Exception): - """ - Raised when attempting to access the return value or exception of a - :class:`.TaskHandle` that is still pending completion. - """ diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_fileio.py b/.venv/lib/python3.12/site-packages/anyio/_core/_fileio.py deleted file mode 100644 index 692c754b..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_fileio.py +++ /dev/null @@ -1,960 +0,0 @@ -from __future__ import annotations - -import os -import pathlib -import sys -from collections.abc import ( - AsyncIterator, - Callable, - Iterable, - Iterator, - Sequence, -) -from dataclasses import dataclass -from functools import partial -from os import PathLike -from typing import ( - IO, - TYPE_CHECKING, - Any, - AnyStr, - ClassVar, - Final, - Generic, - TypeVar, - overload, -) - -from .. import to_thread -from ..abc import AsyncResource -from ._synchronization import CapacityLimiter - -if sys.version_info >= (3, 11): - from typing import Self -else: - from typing_extensions import Self - -if sys.version_info >= (3, 14): - from pathlib.types import PathInfo - -if TYPE_CHECKING: - from types import ModuleType - - from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer -else: - ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object - - -T = TypeVar("T", bound="Path") - - -class AsyncFile(AsyncResource, Generic[AnyStr]): - """ - An asynchronous file object. - - This class wraps a standard file object and provides async friendly versions of the - following blocking methods (where available on the original file object): - - * read - * read1 - * readline - * readlines - * readinto - * readinto1 - * write - * writelines - * truncate - * seek - * tell - * flush - - All other methods are directly passed through. - - This class supports the asynchronous context manager protocol which closes the - underlying file at the end of the context block. - - This class also supports asynchronous iteration:: - - async with await open_file(...) as f: - async for line in f: - print(line) - """ - - def __init__( - self, fp: IO[AnyStr], *, limiter: CapacityLimiter | None = None - ) -> None: - if limiter is not None and not isinstance(limiter, CapacityLimiter): - raise TypeError( - f"limiter must be a CapacityLimiter or None, not " - f"{limiter.__class__.__name__}" - ) - - self._fp: Any = fp - self._limiter = limiter - - def __getattr__(self, name: str) -> object: - return getattr(self._fp, name) - - @property - def limiter(self) -> CapacityLimiter | None: - """The capacity limiter used by this file object, if not the global limiter.""" - return self._limiter - - @property - def wrapped(self) -> IO[AnyStr]: - """The wrapped file object.""" - return self._fp - - async def __aiter__(self) -> AsyncIterator[AnyStr]: - while True: - line = await self.readline() - if line: - yield line - else: - break - - async def aclose(self) -> None: - return await to_thread.run_sync(self._fp.close, limiter=self._limiter) - - async def read(self, size: int = -1) -> AnyStr: - return await to_thread.run_sync(self._fp.read, size, limiter=self._limiter) - - async def read1(self: AsyncFile[bytes], size: int = -1) -> bytes: - return await to_thread.run_sync(self._fp.read1, size, limiter=self._limiter) - - async def readline(self) -> AnyStr: - return await to_thread.run_sync(self._fp.readline, limiter=self._limiter) - - async def readlines(self) -> list[AnyStr]: - return await to_thread.run_sync(self._fp.readlines, limiter=self._limiter) - - async def readinto(self: AsyncFile[bytes], b: WriteableBuffer) -> int: - return await to_thread.run_sync(self._fp.readinto, b, limiter=self._limiter) - - async def readinto1(self: AsyncFile[bytes], b: WriteableBuffer) -> int: - return await to_thread.run_sync(self._fp.readinto1, b, limiter=self._limiter) - - @overload - async def write(self: AsyncFile[bytes], b: ReadableBuffer) -> int: ... - - @overload - async def write(self: AsyncFile[str], b: str) -> int: ... - - async def write(self, b: ReadableBuffer | str) -> int: - return await to_thread.run_sync(self._fp.write, b, limiter=self._limiter) - - @overload - async def writelines( - self: AsyncFile[bytes], lines: Iterable[ReadableBuffer] - ) -> None: ... - - @overload - async def writelines(self: AsyncFile[str], lines: Iterable[str]) -> None: ... - - async def writelines(self, lines: Iterable[ReadableBuffer] | Iterable[str]) -> None: - return await to_thread.run_sync( - self._fp.writelines, lines, limiter=self._limiter - ) - - async def truncate(self, size: int | None = None) -> int: - return await to_thread.run_sync(self._fp.truncate, size, limiter=self._limiter) - - async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: - return await to_thread.run_sync( - self._fp.seek, offset, whence, limiter=self._limiter - ) - - async def tell(self) -> int: - return await to_thread.run_sync(self._fp.tell, limiter=self._limiter) - - async def flush(self) -> None: - return await to_thread.run_sync(self._fp.flush, limiter=self._limiter) - - -@overload -async def open_file( - file: str | PathLike[str] | int, - mode: OpenBinaryMode, - buffering: int = ..., - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - closefd: bool = ..., - opener: Callable[[str, int], int] | None = ..., - *, - limiter: CapacityLimiter | None = ..., -) -> AsyncFile[bytes]: ... - - -@overload -async def open_file( - file: str | PathLike[str] | int, - mode: OpenTextMode = ..., - buffering: int = ..., - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - closefd: bool = ..., - opener: Callable[[str, int], int] | None = ..., - *, - limiter: CapacityLimiter | None = ..., -) -> AsyncFile[str]: ... - - -async def open_file( - file: str | PathLike[str] | int, - mode: str = "r", - buffering: int = -1, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - closefd: bool = True, - opener: Callable[[str, int], int] | None = None, - *, - limiter: CapacityLimiter | None = None, -) -> AsyncFile[Any]: - """ - Open a file asynchronously. - - Except for ``limiter``, the arguments are exactly the same as for the builtin :func:`open`. - - :param limiter: an optional capacity limiter to use with the file - instead of the default one - :return: an asynchronous file object - - .. versionchanged:: 4.14.0 - Added the ``limiter`` keyword argument. - - """ - fp = await to_thread.run_sync( - open, - file, - mode, - buffering, - encoding, - errors, - newline, - closefd, - opener, - limiter=limiter, - ) - return AsyncFile(fp, limiter=limiter) - - -def wrap_file( - file: IO[AnyStr], *, limiter: CapacityLimiter | None = None -) -> AsyncFile[AnyStr]: - """ - Wrap an existing file as an asynchronous file. - - :param file: an existing file-like object - :param limiter: an optional capacity limiter to use with the file - instead of the default one - :return: an asynchronous file object - - .. versionchanged:: 4.14.0 - Added the ``limiter`` keyword argument. - - """ - return AsyncFile(file, limiter=limiter) - - -@dataclass(eq=False) -class _PathIterator(AsyncIterator[T]): - iterator: Iterator[PathLike[str]] - limiter: CapacityLimiter | None - # This was added to ensure that iterating over a subclass of Path yields instances - # of that subclass rather than the base Path class. - path_cls: type[T] - - async def __anext__(self) -> T: - nextval = await to_thread.run_sync( - next, self.iterator, None, abandon_on_cancel=True, limiter=self.limiter - ) - if nextval is None: - raise StopAsyncIteration from None - - return self.path_cls(nextval, limiter=self.limiter) - - -class Path: - """ - An asynchronous version of :class:`pathlib.Path`. - - This class cannot be substituted for :class:`pathlib.Path` or - :class:`pathlib.PurePath`, but it is compatible with the :class:`os.PathLike` - interface. - - It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for - the deprecated :meth:`~pathlib.Path.link_to` method. - - Some methods may be unavailable or have limited functionality, based on the Python - version: - - * :meth:`~pathlib.Path.copy` (available on Python 3.14 or later) - * :meth:`~pathlib.Path.copy_into` (available on Python 3.14 or later) - * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later) - * :meth:`~pathlib.PurePath.full_match` (available on Python 3.13 or later) - * :attr:`~pathlib.Path.info` (available on Python 3.14 or later) - * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later) - * :meth:`~pathlib.PurePath.match` (the ``case_sensitive`` parameter is only - available on Python 3.13 or later) - * :meth:`~pathlib.Path.move` (available on Python 3.14 or later) - * :meth:`~pathlib.Path.move_into` (available on Python 3.14 or later) - * :meth:`~pathlib.PurePath.relative_to` (the ``walk_up`` parameter is only available - on Python 3.12 or later) - * :meth:`~pathlib.Path.walk` (available on Python 3.12 or later) - - Any methods that do disk I/O need to be awaited on. These methods are: - - * :meth:`~pathlib.Path.absolute` - * :meth:`~pathlib.Path.chmod` - * :meth:`~pathlib.Path.cwd` - * :meth:`~pathlib.Path.exists` - * :meth:`~pathlib.Path.expanduser` - * :meth:`~pathlib.Path.group` - * :meth:`~pathlib.Path.hardlink_to` - * :meth:`~pathlib.Path.home` - * :meth:`~pathlib.Path.is_block_device` - * :meth:`~pathlib.Path.is_char_device` - * :meth:`~pathlib.Path.is_dir` - * :meth:`~pathlib.Path.is_fifo` - * :meth:`~pathlib.Path.is_file` - * :meth:`~pathlib.Path.is_junction` - * :meth:`~pathlib.Path.is_mount` - * :meth:`~pathlib.Path.is_socket` - * :meth:`~pathlib.Path.is_symlink` - * :meth:`~pathlib.Path.lchmod` - * :meth:`~pathlib.Path.lstat` - * :meth:`~pathlib.Path.mkdir` - * :meth:`~pathlib.Path.open` - * :meth:`~pathlib.Path.owner` - * :meth:`~pathlib.Path.read_bytes` - * :meth:`~pathlib.Path.read_text` - * :meth:`~pathlib.Path.readlink` - * :meth:`~pathlib.Path.rename` - * :meth:`~pathlib.Path.replace` - * :meth:`~pathlib.Path.resolve` - * :meth:`~pathlib.Path.rmdir` - * :meth:`~pathlib.Path.samefile` - * :meth:`~pathlib.Path.stat` - * :meth:`~pathlib.Path.symlink_to` - * :meth:`~pathlib.Path.touch` - * :meth:`~pathlib.Path.unlink` - * :meth:`~pathlib.Path.walk` - * :meth:`~pathlib.Path.write_bytes` - * :meth:`~pathlib.Path.write_text` - - Additionally, the following methods return an async iterator yielding - :class:`~.Path` objects: - - * :meth:`~pathlib.Path.glob` - * :meth:`~pathlib.Path.iterdir` - * :meth:`~pathlib.Path.rglob` - - .. versionchanged:: 4.14.0 - Added the ``limiter`` keyword argument. - """ - - __slots__ = "_path", "_limiter", "__weakref__" - - __weakref__: Any - - def __init__( - self, *args: str | PathLike[str], limiter: CapacityLimiter | None = None - ) -> None: - if limiter is not None and not isinstance(limiter, CapacityLimiter): - raise TypeError( - f"limiter must be a CapacityLimiter or None, not " - f"{limiter.__class__.__name__}" - ) - - self._path: Final[pathlib.Path] = pathlib.Path(*args) - self._limiter = limiter - - def __fspath__(self) -> str: - return self._path.__fspath__() - - if sys.version_info >= (3, 15): - - def __vfspath__(self) -> str: - return self._path.__vfspath__() - - def __str__(self) -> str: - return self._path.__str__() - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.as_posix()!r})" - - def __bytes__(self) -> bytes: - return self._path.__bytes__() - - def __hash__(self) -> int: - return self._path.__hash__() - - def __eq__(self, other: object) -> bool: - target = other._path if isinstance(other, Path) else other - return self._path.__eq__(target) - - def __lt__(self, other: pathlib.PurePath | Path) -> bool: - target = other._path if isinstance(other, Path) else other - return self._path.__lt__(target) - - def __le__(self, other: pathlib.PurePath | Path) -> bool: - target = other._path if isinstance(other, Path) else other - return self._path.__le__(target) - - def __gt__(self, other: pathlib.PurePath | Path) -> bool: - target = other._path if isinstance(other, Path) else other - return self._path.__gt__(target) - - def __ge__(self, other: pathlib.PurePath | Path) -> bool: - target = other._path if isinstance(other, Path) else other - return self._path.__ge__(target) - - def __truediv__(self, other: str | PathLike[str]) -> Self: - return type(self)(self._path / other, limiter=self._limiter) - - def __rtruediv__(self, other: str | PathLike[str]) -> Self: - return type(self)(other, limiter=self._limiter) / self - - @property - def limiter(self) -> CapacityLimiter | None: - """The capacity limiter used by this path, if not the global limiter.""" - return self._limiter - - @property - def parts(self) -> tuple[str, ...]: - return self._path.parts - - @property - def drive(self) -> str: - return self._path.drive - - @property - def root(self) -> str: - return self._path.root - - @property - def anchor(self) -> str: - return self._path.anchor - - @property - def parents(self) -> Sequence[Self]: - return tuple(type(self)(p, limiter=self._limiter) for p in self._path.parents) - - @property - def parent(self) -> Self: - return type(self)(self._path.parent, limiter=self._limiter) - - @property - def name(self) -> str: - return self._path.name - - @property - def suffix(self) -> str: - return self._path.suffix - - @property - def suffixes(self) -> list[str]: - return self._path.suffixes - - @property - def stem(self) -> str: - return self._path.stem - - async def absolute(self) -> Self: - path = await to_thread.run_sync(self._path.absolute, limiter=self._limiter) - return type(self)(path, limiter=self._limiter) - - def as_posix(self) -> str: - return self._path.as_posix() - - def as_uri(self) -> str: - return self._path.as_uri() - - if sys.version_info >= (3, 13): - parser: ClassVar[ModuleType] = pathlib.Path.parser - - @classmethod - def from_uri(cls, uri: str, *, limiter: CapacityLimiter | None = None) -> Self: - return cls(pathlib.Path.from_uri(uri), limiter=limiter) - - def full_match( - self, path_pattern: str, *, case_sensitive: bool | None = None - ) -> bool: - return self._path.full_match(path_pattern, case_sensitive=case_sensitive) - - def match( - self, path_pattern: str, *, case_sensitive: bool | None = None - ) -> bool: - return self._path.match(path_pattern, case_sensitive=case_sensitive) - else: - - def match(self, path_pattern: str) -> bool: - return self._path.match(path_pattern) - - if sys.version_info >= (3, 14): - - @property - def info(self) -> PathInfo: - return self._path.info - - async def copy( - self, - target: str | os.PathLike[str], - *, - follow_symlinks: bool = True, - preserve_metadata: bool = False, - ) -> Self: - func = partial( - self._path.copy, - follow_symlinks=follow_symlinks, - preserve_metadata=preserve_metadata, - ) - return type(self)( - await to_thread.run_sync( - func, pathlib.Path(target), limiter=self._limiter - ), - limiter=self._limiter, - ) - - async def copy_into( - self, - target_dir: str | os.PathLike[str], - *, - follow_symlinks: bool = True, - preserve_metadata: bool = False, - ) -> Self: - func = partial( - self._path.copy_into, - follow_symlinks=follow_symlinks, - preserve_metadata=preserve_metadata, - ) - return type(self)( - await to_thread.run_sync( - func, pathlib.Path(target_dir), limiter=self._limiter - ), - limiter=self._limiter, - ) - - async def move(self, target: str | os.PathLike[str]) -> Self: - # Upstream does not handle anyio.Path properly as a PathLike - target = pathlib.Path(target) - return type(self)( - await to_thread.run_sync( - self._path.move, target, limiter=self._limiter - ), - limiter=self._limiter, - ) - - async def move_into( - self, - target_dir: str | os.PathLike[str], - ) -> Self: - return type(self)( - await to_thread.run_sync( - self._path.move_into, target_dir, limiter=self._limiter - ), - limiter=self._limiter, - ) - - def is_relative_to(self, other: str | PathLike[str]) -> bool: - try: - self.relative_to(other) - return True - except ValueError: - return False - - async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: - func = partial(os.chmod, follow_symlinks=follow_symlinks) - return await to_thread.run_sync(func, self._path, mode, limiter=self._limiter) - - @classmethod - async def cwd(cls, *, limiter: CapacityLimiter | None = None) -> Self: - path = await to_thread.run_sync(pathlib.Path.cwd, limiter=limiter) - return cls(path, limiter=limiter) - - async def exists(self) -> bool: - return await to_thread.run_sync( - self._path.exists, abandon_on_cancel=True, limiter=self._limiter - ) - - async def expanduser(self) -> Self: - return type(self)( - await to_thread.run_sync( - self._path.expanduser, abandon_on_cancel=True, limiter=self._limiter - ), - limiter=self._limiter, - ) - - if sys.version_info < (3, 12): - # Python 3.11 and earlier - def glob(self, pattern: str) -> AsyncIterator[Self]: - gen = self._path.glob(pattern) - return _PathIterator(gen, self._limiter, type(self)) - elif (3, 12) <= sys.version_info < (3, 13): - # changed in Python 3.12: - # - The case_sensitive parameter was added. - def glob( - self, - pattern: str, - *, - case_sensitive: bool | None = None, - ) -> AsyncIterator[Self]: - gen = self._path.glob(pattern, case_sensitive=case_sensitive) - return _PathIterator(gen, self._limiter, type(self)) - elif sys.version_info >= (3, 13): - # Changed in Python 3.13: - # - The recurse_symlinks parameter was added. - # - The pattern parameter accepts a path-like object. - def glob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block - self, - pattern: str | PathLike[str], - *, - case_sensitive: bool | None = None, - recurse_symlinks: bool = False, - ) -> AsyncIterator[Self]: - gen = self._path.glob( - pattern, # type: ignore[arg-type] - case_sensitive=case_sensitive, - recurse_symlinks=recurse_symlinks, - ) - return _PathIterator(gen, self._limiter, type(self)) - - async def group(self) -> str: - return await to_thread.run_sync( - self._path.group, abandon_on_cancel=True, limiter=self._limiter - ) - - async def hardlink_to( - self, target: str | bytes | PathLike[str] | PathLike[bytes] - ) -> None: - if isinstance(target, Path): - target = target._path - - await to_thread.run_sync(os.link, target, self, limiter=self._limiter) - - @classmethod - async def home(cls, *, limiter: CapacityLimiter | None = None) -> Self: - home_path = await to_thread.run_sync(pathlib.Path.home, limiter=limiter) - return cls(home_path, limiter=limiter) - - def is_absolute(self) -> bool: - return self._path.is_absolute() - - async def is_block_device(self) -> bool: - return await to_thread.run_sync( - self._path.is_block_device, abandon_on_cancel=True, limiter=self._limiter - ) - - async def is_char_device(self) -> bool: - return await to_thread.run_sync( - self._path.is_char_device, abandon_on_cancel=True, limiter=self._limiter - ) - - async def is_dir(self) -> bool: - return await to_thread.run_sync( - self._path.is_dir, abandon_on_cancel=True, limiter=self._limiter - ) - - async def is_fifo(self) -> bool: - return await to_thread.run_sync( - self._path.is_fifo, abandon_on_cancel=True, limiter=self._limiter - ) - - async def is_file(self) -> bool: - return await to_thread.run_sync( - self._path.is_file, abandon_on_cancel=True, limiter=self._limiter - ) - - if sys.version_info >= (3, 12): - - async def is_junction(self) -> bool: - return await to_thread.run_sync( - self._path.is_junction, limiter=self._limiter - ) - - async def is_mount(self) -> bool: - return await to_thread.run_sync( - os.path.ismount, self._path, abandon_on_cancel=True, limiter=self._limiter - ) - - if sys.version_info < (3, 15): - - def is_reserved(self) -> bool: - return self._path.is_reserved() - - async def is_socket(self) -> bool: - return await to_thread.run_sync( - self._path.is_socket, abandon_on_cancel=True, limiter=self._limiter - ) - - async def is_symlink(self) -> bool: - return await to_thread.run_sync( - self._path.is_symlink, abandon_on_cancel=True, limiter=self._limiter - ) - - async def iterdir(self) -> AsyncIterator[Self]: - gen = ( - self._path.iterdir() - if sys.version_info < (3, 13) - else await to_thread.run_sync( - self._path.iterdir, abandon_on_cancel=True, limiter=self._limiter - ) - ) - async for path in _PathIterator(gen, self._limiter, type(self)): - yield path - - def joinpath(self, *args: str | PathLike[str]) -> Self: - return type(self)(self._path.joinpath(*args), limiter=self._limiter) - - async def lchmod(self, mode: int) -> None: - await to_thread.run_sync(self._path.lchmod, mode, limiter=self._limiter) - - async def lstat(self) -> os.stat_result: - return await to_thread.run_sync( - self._path.lstat, abandon_on_cancel=True, limiter=self._limiter - ) - - async def mkdir( - self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False - ) -> None: - await to_thread.run_sync( - self._path.mkdir, mode, parents, exist_ok, limiter=self._limiter - ) - - @overload - async def open( - self, - mode: OpenBinaryMode, - buffering: int = ..., - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - ) -> AsyncFile[bytes]: ... - - @overload - async def open( - self, - mode: OpenTextMode = ..., - buffering: int = ..., - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - ) -> AsyncFile[str]: ... - - async def open( - self, - mode: str = "r", - buffering: int = -1, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - ) -> AsyncFile[Any]: - fp = await to_thread.run_sync( - self._path.open, - mode, - buffering, - encoding, - errors, - newline, - limiter=self._limiter, - ) - return AsyncFile(fp, limiter=self._limiter) - - async def owner(self) -> str: - return await to_thread.run_sync( - self._path.owner, abandon_on_cancel=True, limiter=self._limiter - ) - - async def read_bytes(self) -> bytes: - return await to_thread.run_sync(self._path.read_bytes, limiter=self._limiter) - - async def read_text( - self, encoding: str | None = None, errors: str | None = None - ) -> str: - return await to_thread.run_sync( - self._path.read_text, encoding, errors, limiter=self._limiter - ) - - if sys.version_info >= (3, 12): - - def relative_to( - self, *other: str | PathLike[str], walk_up: bool = False - ) -> Self: - # relative_to() should work with any PathLike but it doesn't - others = [pathlib.Path(other) for other in other] - return type(self)( - self._path.relative_to(*others, walk_up=walk_up), limiter=self._limiter - ) - - else: - - def relative_to(self, *other: str | PathLike[str]) -> Self: - return type(self)(self._path.relative_to(*other), limiter=self._limiter) - - async def readlink(self) -> Self: - target = await to_thread.run_sync( - os.readlink, self._path, limiter=self._limiter - ) - return type(self)(target, limiter=self._limiter) - - async def rename(self, target: str | pathlib.PurePath | Path) -> Self: - if isinstance(target, Path): - target = target._path - - await to_thread.run_sync(self._path.rename, target, limiter=self._limiter) - return type(self)(target, limiter=self._limiter) - - async def replace(self, target: str | pathlib.PurePath | Path) -> Self: - if isinstance(target, Path): - target = target._path - - await to_thread.run_sync(self._path.replace, target, limiter=self._limiter) - return type(self)(target, limiter=self._limiter) - - async def resolve(self, strict: bool = False) -> Self: - func = partial(self._path.resolve, strict=strict) - return type(self)( - await to_thread.run_sync( - func, abandon_on_cancel=True, limiter=self._limiter - ), - limiter=self._limiter, - ) - - if sys.version_info < (3, 12): - # Pre Python 3.12 - def rglob(self, pattern: str) -> AsyncIterator[Self]: - gen = self._path.rglob(pattern) - return _PathIterator(gen, self._limiter, type(self)) - elif (3, 12) <= sys.version_info < (3, 13): - # Changed in Python 3.12: - # - The case_sensitive parameter was added. - def rglob( - self, pattern: str, *, case_sensitive: bool | None = None - ) -> AsyncIterator[Self]: - gen = self._path.rglob(pattern, case_sensitive=case_sensitive) - return _PathIterator(gen, self._limiter, type(self)) - elif sys.version_info >= (3, 13): - # Changed in Python 3.13: - # - The recurse_symlinks parameter was added. - # - The pattern parameter accepts a path-like object. - def rglob( # type: ignore[misc] # mypy doesn't allow for differing signatures in a conditional block - self, - pattern: str | PathLike[str], - *, - case_sensitive: bool | None = None, - recurse_symlinks: bool = False, - ) -> AsyncIterator[Self]: - gen = self._path.rglob( - pattern, # type: ignore[arg-type] - case_sensitive=case_sensitive, - recurse_symlinks=recurse_symlinks, - ) - return _PathIterator(gen, self._limiter, type(self)) - - async def rmdir(self) -> None: - await to_thread.run_sync(self._path.rmdir, limiter=self._limiter) - - async def samefile(self, other_path: str | PathLike[str]) -> bool: - if isinstance(other_path, Path): - other_path = other_path._path - - return await to_thread.run_sync( - self._path.samefile, - other_path, - abandon_on_cancel=True, - limiter=self._limiter, - ) - - async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result: - func = partial(os.stat, follow_symlinks=follow_symlinks) - return await to_thread.run_sync( - func, self._path, abandon_on_cancel=True, limiter=self._limiter - ) - - async def symlink_to( - self, - target: str | bytes | PathLike[str] | PathLike[bytes], - target_is_directory: bool = False, - ) -> None: - if isinstance(target, Path): - target = target._path - - await to_thread.run_sync( - self._path.symlink_to, target, target_is_directory, limiter=self._limiter - ) - - async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: - await to_thread.run_sync( - self._path.touch, mode, exist_ok, limiter=self._limiter - ) - - async def unlink(self, missing_ok: bool = False) -> None: - try: - await to_thread.run_sync(self._path.unlink, limiter=self._limiter) - except FileNotFoundError: - if not missing_ok: - raise - - if sys.version_info >= (3, 12): - - async def walk( - self, - top_down: bool = True, - on_error: Callable[[OSError], object] | None = None, - follow_symlinks: bool = False, - ) -> AsyncIterator[tuple[Self, list[str], list[str]]]: - def get_next_value() -> tuple[pathlib.Path, list[str], list[str]] | None: - try: - return next(gen) - except StopIteration: - return None - - gen = self._path.walk(top_down, on_error, follow_symlinks) - while True: - value = await to_thread.run_sync(get_next_value, limiter=self._limiter) - if value is None: - return - - root, dirs, paths = value - yield type(self)(root, limiter=self._limiter), dirs, paths - - def with_name(self, name: str) -> Self: - return type(self)(self._path.with_name(name), limiter=self._limiter) - - def with_stem(self, stem: str) -> Self: - return type(self)( - self._path.with_name(stem + self._path.suffix), limiter=self._limiter - ) - - def with_suffix(self, suffix: str) -> Self: - return type(self)(self._path.with_suffix(suffix), limiter=self._limiter) - - def with_segments(self, *pathsegments: str | PathLike[str]) -> Self: - return type(self)(*pathsegments, limiter=self._limiter) - - async def write_bytes(self, data: ReadableBuffer) -> int: - return await to_thread.run_sync( - self._path.write_bytes, data, limiter=self._limiter - ) - - async def write_text( - self, - data: str, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - ) -> int: - return await to_thread.run_sync( - self._path.write_text, - data, - encoding, - errors, - newline, - limiter=self._limiter, - ) - - -PathLike.register(Path) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_resources.py b/.venv/lib/python3.12/site-packages/anyio/_core/_resources.py deleted file mode 100644 index b9a5344a..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_resources.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import annotations - -from ..abc import AsyncResource -from ._tasks import CancelScope - - -async def aclose_forcefully(resource: AsyncResource) -> None: - """ - Close an asynchronous resource in a cancelled scope. - - Doing this closes the resource without waiting on anything. - - :param resource: the resource to close - - """ - with CancelScope() as scope: - scope.cancel() - await resource.aclose() diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_signals.py b/.venv/lib/python3.12/site-packages/anyio/_core/_signals.py deleted file mode 100644 index e24c79e1..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_signals.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -from collections.abc import AsyncIterator -from contextlib import AbstractContextManager -from signal import Signals - -from ._eventloop import get_async_backend - - -def open_signal_receiver( - *signals: Signals, -) -> AbstractContextManager[AsyncIterator[Signals]]: - """ - Start receiving operating system signals. - - :param signals: signals to receive (e.g. ``signal.SIGINT``) - :return: an asynchronous context manager for an asynchronous iterator which yields - signal numbers - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - .. warning:: Windows does not support signals natively so it is best to avoid - relying on this in cross-platform applications. - - .. warning:: On asyncio, this permanently replaces any previous signal handler for - the given signals, as set via :meth:`~asyncio.loop.add_signal_handler`. - - """ - return get_async_backend().open_signal_receiver(*signals) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_sockets.py b/.venv/lib/python3.12/site-packages/anyio/_core/_sockets.py deleted file mode 100644 index 29f73324..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_sockets.py +++ /dev/null @@ -1,1011 +0,0 @@ -from __future__ import annotations - -import errno -import os -import socket -import ssl -import stat -import sys -from collections.abc import Awaitable -from dataclasses import dataclass -from ipaddress import IPv4Address, IPv6Address, ip_address -from os import PathLike, chmod -from socket import AddressFamily, SocketKind -from typing import TYPE_CHECKING, Any, Literal, cast, overload - -from .. import ConnectionFailed, to_thread -from ..abc import ( - ByteStreamConnectable, - ConnectedUDPSocket, - ConnectedUNIXDatagramSocket, - IPAddressType, - IPSockAddrType, - SocketListener, - SocketStream, - UDPSocket, - UNIXDatagramSocket, - UNIXSocketStream, -) -from ..streams.stapled import MultiListener -from ..streams.tls import TLSConnectable, TLSStream -from ._eventloop import get_async_backend -from ._resources import aclose_forcefully -from ._synchronization import Event -from ._tasks import create_task_group, move_on_after - -if TYPE_CHECKING: - from _typeshed import FileDescriptorLike -else: - FileDescriptorLike = object - -if sys.version_info < (3, 11): - from exceptiongroup import ExceptionGroup - -if sys.version_info >= (3, 12): - from typing import override -else: - from typing_extensions import override - -if sys.version_info < (3, 13): - from typing_extensions import deprecated -else: - from warnings import deprecated - -IPPROTO_IPV6 = getattr(socket, "IPPROTO_IPV6", 41) # https://bugs.python.org/issue29515 - -AnyIPAddressFamily = Literal[ - AddressFamily.AF_UNSPEC, AddressFamily.AF_INET, AddressFamily.AF_INET6 -] -IPAddressFamily = Literal[AddressFamily.AF_INET, AddressFamily.AF_INET6] - - -# tls_hostname given -@overload -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = ..., - local_port: int | None = ..., - ssl_context: ssl.SSLContext | None = ..., - tls_standard_compatible: bool = ..., - tls_hostname: str, - happy_eyeballs_delay: float = ..., -) -> TLSStream: ... - - -# ssl_context given -@overload -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = ..., - local_port: int | None = ..., - ssl_context: ssl.SSLContext, - tls_standard_compatible: bool = ..., - tls_hostname: str | None = ..., - happy_eyeballs_delay: float = ..., -) -> TLSStream: ... - - -# tls=True -@overload -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = ..., - local_port: int | None = ..., - tls: Literal[True], - ssl_context: ssl.SSLContext | None = ..., - tls_standard_compatible: bool = ..., - tls_hostname: str | None = ..., - happy_eyeballs_delay: float = ..., -) -> TLSStream: ... - - -# tls=False -@overload -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = ..., - local_port: int | None = ..., - tls: Literal[False], - ssl_context: ssl.SSLContext | None = ..., - tls_standard_compatible: bool = ..., - tls_hostname: str | None = ..., - happy_eyeballs_delay: float = ..., -) -> SocketStream: ... - - -# No TLS arguments -@overload -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = ..., - local_port: int | None = ..., - happy_eyeballs_delay: float = ..., -) -> SocketStream: ... - - -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = None, - local_port: int | None = None, - tls: bool = False, - ssl_context: ssl.SSLContext | None = None, - tls_standard_compatible: bool = True, - tls_hostname: str | None = None, - happy_eyeballs_delay: float = 0.25, -) -> SocketStream | TLSStream: - """ - Connect to a host using the TCP protocol. - - This function implements the stateless version of the Happy Eyeballs algorithm (RFC - 6555). If ``remote_host`` is a host name that resolves to multiple IP addresses, - each one is tried until one connection attempt succeeds. If the first attempt does - not connected within 250 milliseconds, a second attempt is started using the next - address in the list, and so on. On IPv6 enabled systems, an IPv6 address (if - available) is tried first. - - When the connection has been established, a TLS handshake will be done if either - ``ssl_context`` or ``tls_hostname`` is not ``None``, or if ``tls`` is ``True``. - - :param remote_host: the IP address or host name to connect to - :param remote_port: port on the target host to connect to - :param local_host: the interface address or name to bind the socket to before - connecting - :param local_port: the local port to bind to (requires ``local_host`` to also be - set) - :param tls: ``True`` to do a TLS handshake with the connected stream and return a - :class:`~anyio.streams.tls.TLSStream` instead - :param ssl_context: the SSL context object to use (if omitted, a default context is - created) - :param tls_standard_compatible: If ``True``, performs the TLS shutdown handshake - before closing the stream and requires that the server does this as well. - Otherwise, :exc:`~ssl.SSLEOFError` may be raised during reads from the stream. - Some protocols, such as HTTP, require this option to be ``False``. - See :meth:`~ssl.SSLContext.wrap_socket` for details. - :param tls_hostname: host name to check the server certificate against (defaults to - the value of ``remote_host``) - :param happy_eyeballs_delay: delay (in seconds) before starting the next connection - attempt - :return: a socket stream object if no TLS handshake was done, otherwise a TLS stream - :raises ConnectionFailed: if the connection fails - - """ - # Placed here due to https://github.com/python/mypy/issues/7057 - connected_stream: SocketStream | None = None - - async def try_connect(remote_host: str, event: Event) -> None: - nonlocal connected_stream - try: - stream = await asynclib.connect_tcp(remote_host, remote_port, local_address) - except OSError as exc: - oserrors.append(exc) - return - else: - if connected_stream is None: - connected_stream = stream - tg.cancel_scope.cancel() - else: - await stream.aclose() - finally: - event.set() - - asynclib = get_async_backend() - local_address: IPSockAddrType | None = None - family = socket.AF_UNSPEC - if local_host: - gai_res = await getaddrinfo(str(local_host), local_port) - family, *_, local_address = gai_res[0] - - target_host = str(remote_host) - try: - addr_obj = ip_address(remote_host) - except ValueError: - addr_obj = None - - if addr_obj is not None: - if isinstance(addr_obj, IPv6Address): - target_addrs = [(socket.AF_INET6, addr_obj.compressed)] - else: - target_addrs = [(socket.AF_INET, addr_obj.compressed)] - else: - # getaddrinfo() will raise an exception if name resolution fails - gai_res = await getaddrinfo( - target_host, remote_port, family=family, type=socket.SOCK_STREAM - ) - - # Organize the list so that the first address is an IPv6 address (if available) - # and the second one is an IPv4 addresses. The rest can be in whatever order. - v6_found = v4_found = False - target_addrs = [] - for af, *_, sa in gai_res: - if af == socket.AF_INET6 and not v6_found: - v6_found = True - target_addrs.insert(0, (af, sa[0])) - elif af == socket.AF_INET and not v4_found and v6_found: - v4_found = True - target_addrs.insert(1, (af, sa[0])) - else: - target_addrs.append((af, sa[0])) - - oserrors: list[OSError] = [] - try: - async with create_task_group() as tg: - for _af, addr in target_addrs: - event = Event() - tg.start_soon(try_connect, addr, event) - with move_on_after(happy_eyeballs_delay): - await event.wait() - - if connected_stream is None: - cause = ( - oserrors[0] - if len(oserrors) == 1 - else ExceptionGroup("multiple connection attempts failed", oserrors) - ) - raise OSError("All connection attempts failed") from cause - finally: - oserrors.clear() - - if tls or tls_hostname or ssl_context: - try: - return await TLSStream.wrap( - connected_stream, - server_side=False, - hostname=tls_hostname or str(remote_host), - ssl_context=ssl_context, - standard_compatible=tls_standard_compatible, - ) - except BaseException: - await aclose_forcefully(connected_stream) - raise - - return connected_stream - - -async def connect_unix(path: str | bytes | PathLike[Any]) -> UNIXSocketStream: - """ - Connect to the given UNIX socket. - - Not available on Windows. - - :param path: path to the socket - :return: a socket stream object - :raises ConnectionFailed: if the connection fails - - """ - path = os.fspath(path) - return await get_async_backend().connect_unix(path) - - -async def create_tcp_listener( - *, - local_host: IPAddressType | None = None, - local_port: int = 0, - family: AnyIPAddressFamily = socket.AddressFamily.AF_UNSPEC, - backlog: int = 65536, - reuse_port: bool = False, -) -> MultiListener[SocketStream]: - """ - Create a TCP socket listener. - - :param local_port: port number to listen on - :param local_host: IP address of the interface to listen on. If omitted, listen on - all IPv4 and IPv6 interfaces. To listen on all interfaces on a specific address - family, use ``0.0.0.0`` for IPv4 or ``::`` for IPv6. - :param family: address family (used if ``local_host`` was omitted) - :param backlog: maximum number of queued incoming connections (up to a maximum of - 2**16, or 65536) - :param reuse_port: ``True`` to allow multiple sockets to bind to the same - address/port (not supported on Windows) - :return: a multi-listener object containing one or more socket listeners - :raises OSError: if there's an error creating a socket, or binding to one or more - interfaces failed - - """ - asynclib = get_async_backend() - backlog = min(backlog, 65536) - local_host = str(local_host) if local_host is not None else None - - def setup_raw_socket( - fam: AddressFamily, - bind_addr: tuple[str, int] | tuple[str, int, int, int], - *, - v6only: bool = True, - ) -> socket.socket: - sock = socket.socket(fam) - try: - sock.setblocking(False) - - if fam == AddressFamily.AF_INET6: - sock.setsockopt(IPPROTO_IPV6, socket.IPV6_V6ONLY, v6only) - - # For Windows, enable exclusive address use. For others, enable address - # reuse. - if sys.platform == "win32": - sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) - else: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - - if reuse_port: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - - # Workaround for #554 - if fam == socket.AF_INET6 and "%" in bind_addr[0]: - addr, scope_id = bind_addr[0].split("%", 1) - bind_addr = (addr, bind_addr[1], 0, int(scope_id)) - - sock.bind(bind_addr) - sock.listen(backlog) - except BaseException: - sock.close() - raise - - return sock - - # We passing type=0 on non-Windows platforms as a workaround for a uvloop bug - # where we don't get the correct scope ID for IPv6 link-local addresses when passing - # type=socket.SOCK_STREAM to getaddrinfo(): - # https://github.com/MagicStack/uvloop/issues/539 - gai_res = await getaddrinfo( - local_host, - local_port, - family=family, - type=socket.SOCK_STREAM if sys.platform == "win32" else 0, - flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, - ) - - # The set comprehension is here to work around a glibc bug: - # https://sourceware.org/bugzilla/show_bug.cgi?id=14969 - sockaddrs = sorted({res for res in gai_res if res[1] == SocketKind.SOCK_STREAM}) - - # Special case for dual-stack binding on the "any" interface - if ( - local_host is None - and family == AddressFamily.AF_UNSPEC - and socket.has_dualstack_ipv6() - and any(fam == AddressFamily.AF_INET6 for fam, *_ in gai_res) - ): - raw_socket = setup_raw_socket( - AddressFamily.AF_INET6, ("::", local_port), v6only=False - ) - listener = asynclib.create_tcp_listener(raw_socket) - return MultiListener([listener]) - - errors: list[OSError] = [] - try: - for _ in range(len(sockaddrs)): - listeners: list[SocketListener] = [] - bound_ephemeral_port = local_port - try: - for fam, *_, sockaddr in sockaddrs: - sockaddr = sockaddr[0], bound_ephemeral_port, *sockaddr[2:] - raw_socket = setup_raw_socket(fam, sockaddr) - - # Store the assigned port if an ephemeral port was requested, so - # we'll bind to the same port on all interfaces - if local_port == 0 and len(gai_res) > 1: - bound_ephemeral_port = raw_socket.getsockname()[1] - - listeners.append(asynclib.create_tcp_listener(raw_socket)) - except BaseException as exc: - for listener in listeners: - await listener.aclose() - - # If an ephemeral port was requested but binding the assigned port - # failed for another interface, rotate the address list and try again - if ( - isinstance(exc, OSError) - and exc.errno == errno.EADDRINUSE - and local_port == 0 - and bound_ephemeral_port - ): - errors.append(exc) - sockaddrs.append(sockaddrs.pop(0)) - continue - - raise - - return MultiListener(listeners) - - raise OSError( - f"Could not create {len(sockaddrs)} listeners with a consistent port" - ) from ExceptionGroup("Several bind attempts failed", errors) - finally: - del errors # Prevent reference cycles - - -async def create_unix_listener( - path: str | bytes | PathLike[Any], - *, - mode: int | None = None, - backlog: int = 65536, -) -> SocketListener: - """ - Create a UNIX socket listener. - - Not available on Windows. - - :param path: path of the socket - :param mode: permissions to set on the socket - :param backlog: maximum number of queued incoming connections (up to a maximum of - 2**16, or 65536) - :return: a listener object - - .. versionchanged:: 3.0 - If a socket already exists on the file system in the given path, it will be - removed first. - - """ - backlog = min(backlog, 65536) - raw_socket = await setup_unix_local_socket(path, mode, socket.SOCK_STREAM) - try: - raw_socket.listen(backlog) - return get_async_backend().create_unix_listener(raw_socket) - except BaseException: - raw_socket.close() - raise - - -async def create_udp_socket( - family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, - *, - local_host: IPAddressType | None = None, - local_port: int = 0, - reuse_port: bool = False, -) -> UDPSocket: - """ - Create a UDP socket. - - If ``port`` has been given, the socket will be bound to this port on the local - machine, making this socket suitable for providing UDP based services. - - :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically - determined from ``local_host`` if omitted - :param local_host: IP address or host name of the local interface to bind to - :param local_port: local port to bind to - :param reuse_port: ``True`` to allow multiple sockets to bind to the same - address/port (not supported on Windows) - :return: a UDP socket - - """ - if family is AddressFamily.AF_UNSPEC and not local_host: - raise ValueError('Either "family" or "local_host" must be given') - - if local_host: - gai_res = await getaddrinfo( - str(local_host), - local_port, - family=family, - type=socket.SOCK_DGRAM, - flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, - ) - family = cast(AnyIPAddressFamily, gai_res[0][0]) - local_address = gai_res[0][-1] - elif family is AddressFamily.AF_INET6: - local_address = ("::", 0) - else: - local_address = ("0.0.0.0", 0) - - sock = await get_async_backend().create_udp_socket( - family, local_address, None, reuse_port - ) - return cast(UDPSocket, sock) - - -async def create_connected_udp_socket( - remote_host: IPAddressType, - remote_port: int, - *, - family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, - local_host: IPAddressType | None = None, - local_port: int = 0, - reuse_port: bool = False, -) -> ConnectedUDPSocket: - """ - Create a connected UDP socket. - - Connected UDP sockets can only communicate with the specified remote host/port, an - any packets sent from other sources are dropped. - - :param remote_host: remote host to set as the default target - :param remote_port: port on the remote host to set as the default target - :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically - determined from ``local_host`` or ``remote_host`` if omitted - :param local_host: IP address or host name of the local interface to bind to - :param local_port: local port to bind to - :param reuse_port: ``True`` to allow multiple sockets to bind to the same - address/port (not supported on Windows) - :return: a connected UDP socket - - """ - local_address = None - if local_host: - gai_res = await getaddrinfo( - str(local_host), - local_port, - family=family, - type=socket.SOCK_DGRAM, - flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, - ) - family = cast(AnyIPAddressFamily, gai_res[0][0]) - local_address = gai_res[0][-1] - - gai_res = await getaddrinfo( - str(remote_host), remote_port, family=family, type=socket.SOCK_DGRAM - ) - family = cast(AnyIPAddressFamily, gai_res[0][0]) - remote_address = gai_res[0][-1] - - sock = await get_async_backend().create_udp_socket( - family, local_address, remote_address, reuse_port - ) - return cast(ConnectedUDPSocket, sock) - - -async def create_unix_datagram_socket( - *, - local_path: None | str | bytes | PathLike[Any] = None, - local_mode: int | None = None, -) -> UNIXDatagramSocket: - """ - Create a UNIX datagram socket. - - Not available on Windows. - - If ``local_path`` has been given, the socket will be bound to this path, making this - socket suitable for receiving datagrams from other processes. Other processes can - send datagrams to this socket only if ``local_path`` is set. - - If a socket already exists on the file system in the ``local_path``, it will be - removed first. - - :param local_path: the path on which to bind to - :param local_mode: permissions to set on the local socket - :return: a UNIX datagram socket - - """ - raw_socket = await setup_unix_local_socket( - local_path, local_mode, socket.SOCK_DGRAM - ) - return await get_async_backend().create_unix_datagram_socket(raw_socket, None) - - -async def create_connected_unix_datagram_socket( - remote_path: str | bytes | PathLike[Any], - *, - local_path: None | str | bytes | PathLike[Any] = None, - local_mode: int | None = None, -) -> ConnectedUNIXDatagramSocket: - """ - Create a connected UNIX datagram socket. - - Connected datagram sockets can only communicate with the specified remote path. - - If ``local_path`` has been given, the socket will be bound to this path, making - this socket suitable for receiving datagrams from other processes. Other processes - can send datagrams to this socket only if ``local_path`` is set. - - If a socket already exists on the file system in the ``local_path``, it will be - removed first. - - :param remote_path: the path to set as the default target - :param local_path: the path on which to bind to - :param local_mode: permissions to set on the local socket - :return: a connected UNIX datagram socket - - """ - remote_path = os.fspath(remote_path) - raw_socket = await setup_unix_local_socket( - local_path, local_mode, socket.SOCK_DGRAM - ) - return await get_async_backend().create_unix_datagram_socket( - raw_socket, remote_path - ) - - -async def getaddrinfo( - host: bytes | str | None, - port: str | int | None, - *, - family: int | AddressFamily = 0, - type: int | SocketKind = 0, - proto: int = 0, - flags: int = 0, -) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int]]]: - """ - Look up a numeric IP address given a host name. - - Internationalized domain names are translated according to the (non-transitional) - IDNA 2008 standard. - - .. note:: 4-tuple IPv6 socket addresses are automatically converted to 2-tuples of - (host, port), unlike what :func:`socket.getaddrinfo` does. - - :param host: host name - :param port: port number - :param family: socket family (`'AF_INET``, ...) - :param type: socket type (``SOCK_STREAM``, ...) - :param proto: protocol number - :param flags: flags to pass to upstream ``getaddrinfo()`` - :return: list of tuples containing (family, type, proto, canonname, sockaddr) - - .. seealso:: :func:`socket.getaddrinfo` - - """ - # Handle unicode hostnames - if isinstance(host, str): - try: - encoded_host: bytes | None = host.encode("ascii") - except UnicodeEncodeError: - import idna - - encoded_host = idna.encode(host, uts46=True) - else: - encoded_host = host - - gai_res = await get_async_backend().getaddrinfo( - encoded_host, port, family=family, type=type, proto=proto, flags=flags - ) - return [ - (family, type, proto, canonname, convert_ipv6_sockaddr(sockaddr)) - for family, type, proto, canonname, sockaddr in gai_res - # filter out IPv6 results when IPv6 is disabled - if not isinstance(sockaddr[0], int) - ] - - -def getnameinfo(sockaddr: IPSockAddrType, flags: int = 0) -> Awaitable[tuple[str, str]]: - """ - Look up the host name of an IP address. - - :param sockaddr: socket address (e.g. (ipaddress, port) for IPv4) - :param flags: flags to pass to upstream ``getnameinfo()`` - :return: a tuple of (host name, service name) - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - .. seealso:: :func:`socket.getnameinfo` - - """ - return get_async_backend().getnameinfo(sockaddr, flags) - - -@deprecated("This function is deprecated; use `wait_readable` instead") -def wait_socket_readable(sock: socket.socket) -> Awaitable[None]: - """ - .. deprecated:: 4.7.0 - Use :func:`wait_readable` instead. - - Wait until the given socket has data to be read. - - .. warning:: Only use this on raw sockets that have not been wrapped by any higher - level constructs like socket streams! - - :param sock: a socket object - :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the - socket to become readable - :raises ~anyio.BusyResourceError: if another task is already waiting for the socket - to become readable - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - return get_async_backend().wait_readable(sock.fileno()) - - -@deprecated("This function is deprecated; use `wait_writable` instead") -def wait_socket_writable(sock: socket.socket) -> Awaitable[None]: - """ - .. deprecated:: 4.7.0 - Use :func:`wait_writable` instead. - - Wait until the given socket can be written to. - - This does **NOT** work on Windows when using the asyncio backend with a proactor - event loop (default on py3.8+). - - .. warning:: Only use this on raw sockets that have not been wrapped by any higher - level constructs like socket streams! - - :param sock: a socket object - :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the - socket to become writable - :raises ~anyio.BusyResourceError: if another task is already waiting for the socket - to become writable - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - return get_async_backend().wait_writable(sock.fileno()) - - -def wait_readable(obj: FileDescriptorLike) -> Awaitable[None]: - """ - Wait until the given object has data to be read. - - On Unix systems, ``obj`` must either be an integer file descriptor, or else an - object with a ``.fileno()`` method which returns an integer file descriptor. Any - kind of file descriptor can be passed, though the exact semantics will depend on - your kernel. For example, this probably won't do anything useful for on-disk files. - - On Windows systems, ``obj`` must either be an integer ``SOCKET`` handle, or else an - object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File - descriptors aren't supported, and neither are handles that refer to anything besides - a ``SOCKET``. - - On backends where this functionality is not natively provided (asyncio - ``ProactorEventLoop`` on Windows), it is provided using a separate selector thread - which is set to shut down when the interpreter shuts down. - - .. warning:: Don't use this on raw sockets that have been wrapped by any higher - level constructs like socket streams! - - :param obj: an object with a ``.fileno()`` method or an integer handle - :raises ~anyio.ClosedResourceError: if the object was closed while waiting for the - object to become readable - :raises ~anyio.BusyResourceError: if another task is already waiting for the object - to become readable - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - return get_async_backend().wait_readable(obj) - - -def wait_writable(obj: FileDescriptorLike) -> Awaitable[None]: - """ - Wait until the given object can be written to. - - :param obj: an object with a ``.fileno()`` method or an integer handle - :raises ~anyio.ClosedResourceError: if the object was closed while waiting for the - object to become writable - :raises ~anyio.BusyResourceError: if another task is already waiting for the object - to become writable - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - .. seealso:: See the documentation of :func:`wait_readable` for the definition of - ``obj`` and notes on backend compatibility. - - .. warning:: Don't use this on raw sockets that have been wrapped by any higher - level constructs like socket streams! - - """ - return get_async_backend().wait_writable(obj) - - -def notify_closing(obj: FileDescriptorLike) -> None: - """ - Call this before closing a file descriptor (on Unix) or socket (on - Windows). This will cause any `wait_readable` or `wait_writable` - calls on the given object to immediately wake up and raise - `~anyio.ClosedResourceError`. - - This doesn't actually close the object – you still have to do that - yourself afterwards. Also, you want to be careful to make sure no - new tasks start waiting on the object in between when you call this - and when it's actually closed. So to close something properly, you - usually want to do these steps in order: - - 1. Explicitly mark the object as closed, so that any new attempts - to use it will abort before they start. - 2. Call `notify_closing` to wake up any already-existing users. - 3. Actually close the object. - - It's also possible to do them in a different order if that's more - convenient, *but only if* you make sure not to have any checkpoints in - between the steps. This way they all happen in a single atomic - step, so other tasks won't be able to tell what order they happened - in anyway. - - :param obj: an object with a ``.fileno()`` method or an integer handle - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - get_async_backend().notify_closing(obj) - - -# -# Private API -# - - -def convert_ipv6_sockaddr( - sockaddr: tuple[str, int, int, int] | tuple[str, int], -) -> tuple[str, int]: - """ - Convert a 4-tuple IPv6 socket address to a 2-tuple (address, port) format. - - If the scope ID is nonzero, it is added to the address, separated with ``%``. - Otherwise the flow id and scope id are simply cut off from the tuple. - Any other kinds of socket addresses are returned as-is. - - :param sockaddr: the result of :meth:`~socket.socket.getsockname` - :return: the converted socket address - - """ - # This is more complicated than it should be because of MyPy - if isinstance(sockaddr, tuple) and len(sockaddr) == 4: - host, port, flowinfo, scope_id = sockaddr - if scope_id: - # PyPy (as of v7.3.11) leaves the interface name in the result, so - # we discard it and only get the scope ID from the end - # (https://foss.heptapod.net/pypy/pypy/-/issues/3938) - host = host.split("%")[0] - - # Add scope_id to the address - return f"{host}%{scope_id}", port - else: - return host, port - else: - return sockaddr - - -async def setup_unix_local_socket( - path: None | str | bytes | PathLike[Any], - mode: int | None, - socktype: int, -) -> socket.socket: - """ - Create a UNIX local socket object, deleting the socket at the given path if it - exists. - - Not available on Windows. - - :param path: path of the socket - :param mode: permissions to set on the socket - :param socktype: socket.SOCK_STREAM or socket.SOCK_DGRAM - - """ - path_str: str | None - if path is not None: - path_str = os.fsdecode(path) - - # Linux abstract namespace sockets aren't backed by a concrete file so skip stat call - if not path_str.startswith("\0"): - # Copied from pathlib... - try: - stat_result = os.stat(path) - except OSError as e: - if e.errno not in ( - errno.ENOENT, - errno.ENOTDIR, - errno.EBADF, - errno.ELOOP, - ): - raise - else: - if stat.S_ISSOCK(stat_result.st_mode): - os.unlink(path) - else: - path_str = None - - raw_socket = socket.socket(socket.AF_UNIX, socktype) - raw_socket.setblocking(False) - - if path_str is not None: - try: - await to_thread.run_sync(raw_socket.bind, path_str, abandon_on_cancel=True) - if mode is not None: - await to_thread.run_sync(chmod, path_str, mode, abandon_on_cancel=True) - except BaseException: - raw_socket.close() - raise - - return raw_socket - - -@dataclass -class TCPConnectable(ByteStreamConnectable): - """ - Connects to a TCP server at the given host and port. - - :param host: host name or IP address of the server - :param port: TCP port number of the server - """ - - host: str | IPv4Address | IPv6Address - port: int - - def __post_init__(self) -> None: - if self.port < 1 or self.port > 65535: - raise ValueError("TCP port number out of range") - - @override - async def connect(self) -> SocketStream: - try: - return await connect_tcp(self.host, self.port) - except OSError as exc: - raise ConnectionFailed( - f"error connecting to {self.host}:{self.port}: {exc}" - ) from exc - - -@dataclass -class UNIXConnectable(ByteStreamConnectable): - """ - Connects to a UNIX domain socket at the given path. - - :param path: the file system path of the socket - """ - - path: str | bytes | PathLike[str] | PathLike[bytes] - - @override - async def connect(self) -> UNIXSocketStream: - try: - return await connect_unix(self.path) - except OSError as exc: - raise ConnectionFailed(f"error connecting to {self.path!r}: {exc}") from exc - - -def as_connectable( - remote: ByteStreamConnectable - | tuple[str | IPv4Address | IPv6Address, int] - | str - | bytes - | PathLike[str], - /, - *, - tls: bool = False, - ssl_context: ssl.SSLContext | None = None, - tls_hostname: str | None = None, - tls_standard_compatible: bool = True, -) -> ByteStreamConnectable: - """ - Return a byte stream connectable from the given object. - - If a bytestream connectable is given, it is returned unchanged. - If a tuple of (host, port) is given, a TCP connectable is returned. - If a string or bytes path is given, a UNIX connectable is returned. - - If ``tls=True``, the connectable will be wrapped in a - :class:`~.streams.tls.TLSConnectable`. - - :param remote: a connectable, a tuple of (host, port) or a path to a UNIX socket - :param tls: if ``True``, wrap the plaintext connectable in a - :class:`~.streams.tls.TLSConnectable`, using the provided TLS settings) - :param ssl_context: if ``tls=True``, the SSLContext object to use (if not provided, - a secure default will be created) - :param tls_hostname: if ``tls=True``, host name of the server to use for checking - the server certificate (defaults to the host portion of the address for TCP - connectables) - :param tls_standard_compatible: if ``False`` and ``tls=True``, makes the TLS stream - skip the closing handshake when closing the connection, so it won't raise an - exception if the server does the same - - """ - connectable: TCPConnectable | UNIXConnectable | TLSConnectable - if isinstance(remote, ByteStreamConnectable): - return remote - elif isinstance(remote, tuple) and len(remote) == 2: - connectable = TCPConnectable(*remote) - elif isinstance(remote, (str, bytes, PathLike)): - connectable = UNIXConnectable(remote) - else: - raise TypeError(f"cannot convert {remote!r} to a connectable") - - if tls: - if not tls_hostname and isinstance(connectable, TCPConnectable): - tls_hostname = str(connectable.host) - - connectable = TLSConnectable( - connectable, - ssl_context=ssl_context, - hostname=tls_hostname, - standard_compatible=tls_standard_compatible, - ) - - return connectable diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_streams.py b/.venv/lib/python3.12/site-packages/anyio/_core/_streams.py deleted file mode 100644 index 2b9c7df2..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_streams.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import annotations - -import math -from typing import TypeVar -from warnings import warn - -from ..streams.memory import ( - MemoryObjectReceiveStream, - MemoryObjectSendStream, - _MemoryObjectStreamState, -) - -T_Item = TypeVar("T_Item") - - -class create_memory_object_stream( - tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]], -): - """ - Create a memory object stream. - - The stream's item type can be annotated like - :func:`create_memory_object_stream[T_Item]`. - - :param max_buffer_size: number of items held in the buffer until ``send()`` starts - blocking - :param item_type: old way of marking the streams with the right generic type for - static typing (does nothing on AnyIO 4) - - .. deprecated:: 4.0 - Use ``create_memory_object_stream[YourItemType](...)`` instead. - :return: a tuple of (send stream, receive stream) - - """ - - def __new__( # type: ignore[misc] - cls, max_buffer_size: float = 0, item_type: object = None - ) -> tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]]: - if max_buffer_size != math.inf and not isinstance(max_buffer_size, int): - raise ValueError("max_buffer_size must be either an integer or math.inf") - if max_buffer_size < 0: - raise ValueError("max_buffer_size cannot be negative") - if item_type is not None: - warn( - "The item_type argument has been deprecated in AnyIO 4.0. " - "Use create_memory_object_stream[YourItemType](...) instead.", - DeprecationWarning, - stacklevel=2, - ) - - state = _MemoryObjectStreamState[T_Item](max_buffer_size) - return (MemoryObjectSendStream(state), MemoryObjectReceiveStream(state)) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_subprocesses.py b/.venv/lib/python3.12/site-packages/anyio/_core/_subprocesses.py deleted file mode 100644 index 9796f8bb..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_subprocesses.py +++ /dev/null @@ -1,196 +0,0 @@ -from __future__ import annotations - -from collections.abc import AsyncIterable, Iterable, Mapping, Sequence -from io import BytesIO -from os import PathLike -from subprocess import PIPE, CalledProcessError, CompletedProcess -from typing import IO, Any, TypeAlias, cast - -from ..abc import Process -from ._eventloop import get_async_backend -from ._tasks import create_task_group - -StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes] - - -async def run_process( - command: StrOrBytesPath | Sequence[StrOrBytesPath], - *, - input: bytes | None = None, - stdin: int | IO[Any] | None = None, - stdout: int | IO[Any] | None = PIPE, - stderr: int | IO[Any] | None = PIPE, - check: bool = True, - cwd: StrOrBytesPath | None = None, - env: Mapping[str, str] | None = None, - startupinfo: Any = None, - creationflags: int = 0, - start_new_session: bool = False, - pass_fds: Sequence[int] = (), - user: str | int | None = None, - group: str | int | None = None, - extra_groups: Iterable[str | int] | None = None, - umask: int = -1, -) -> CompletedProcess[bytes]: - """ - Run an external command in a subprocess and wait until it completes. - - .. seealso:: :func:`subprocess.run` - - :param command: either a string to pass to the shell, or an iterable of strings - containing the executable name or path and its arguments - :param input: bytes passed to the standard input of the subprocess - :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, - a file-like object, or `None`; ``input`` overrides this - :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, - a file-like object, or `None` - :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, - :data:`subprocess.STDOUT`, a file-like object, or `None` - :param check: if ``True``, raise :exc:`~subprocess.CalledProcessError` if the - process terminates with a return code other than 0 - :param cwd: If not ``None``, change the working directory to this before running the - command - :param env: if not ``None``, this mapping replaces the inherited environment - variables from the parent process - :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used - to specify process startup parameters (Windows only) - :param creationflags: flags that can be used to control the creation of the - subprocess (see :class:`subprocess.Popen` for the specifics) - :param start_new_session: if ``true`` the setsid() system call will be made in the - child process prior to the execution of the subprocess. (POSIX only) - :param pass_fds: sequence of file descriptors to keep open between the parent and - child processes. (POSIX only) - :param user: effective user to run the process as (Python >= 3.9, POSIX only) - :param group: effective group to run the process as (Python >= 3.9, POSIX only) - :param extra_groups: supplementary groups to set in the subprocess (Python >= 3.9, - POSIX only) - :param umask: if not negative, this umask is applied in the child process before - running the given command (Python >= 3.9, POSIX only) - :return: an object representing the completed process - :raises ~subprocess.CalledProcessError: if ``check`` is ``True`` and the process - exits with a nonzero return code - - """ - - async def drain_stream(stream: AsyncIterable[bytes], index: int) -> None: - buffer = BytesIO() - async for chunk in stream: - buffer.write(chunk) - - stream_contents[index] = buffer.getvalue() - - if stdin is not None and input is not None: - raise ValueError("only one of stdin and input is allowed") - - async with await open_process( - command, - stdin=PIPE if input else stdin, - stdout=stdout, - stderr=stderr, - cwd=cwd, - env=env, - startupinfo=startupinfo, - creationflags=creationflags, - start_new_session=start_new_session, - pass_fds=pass_fds, - user=user, - group=group, - extra_groups=extra_groups, - umask=umask, - ) as process: - stream_contents: list[bytes | None] = [None, None] - async with create_task_group() as tg: - if process.stdout: - tg.start_soon(drain_stream, process.stdout, 0) - - if process.stderr: - tg.start_soon(drain_stream, process.stderr, 1) - - if process.stdin and input: - await process.stdin.send(input) - await process.stdin.aclose() - - await process.wait() - - output, errors = stream_contents - if check and process.returncode != 0: - raise CalledProcessError(cast(int, process.returncode), command, output, errors) - - return CompletedProcess(command, cast(int, process.returncode), output, errors) - - -async def open_process( - command: StrOrBytesPath | Sequence[StrOrBytesPath], - *, - stdin: int | IO[Any] | None = PIPE, - stdout: int | IO[Any] | None = PIPE, - stderr: int | IO[Any] | None = PIPE, - cwd: StrOrBytesPath | None = None, - env: Mapping[str, str] | None = None, - startupinfo: Any = None, - creationflags: int = 0, - start_new_session: bool = False, - pass_fds: Sequence[int] = (), - user: str | int | None = None, - group: str | int | None = None, - extra_groups: Iterable[str | int] | None = None, - umask: int = -1, -) -> Process: - """ - Start an external command in a subprocess. - - .. seealso:: :class:`subprocess.Popen` - - :param command: either a string to pass to the shell, or an iterable of strings - containing the executable name or path and its arguments - :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, a - file-like object, or ``None`` - :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, - a file-like object, or ``None`` - :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, - :data:`subprocess.STDOUT`, a file-like object, or ``None`` - :param cwd: If not ``None``, the working directory is changed before executing - :param env: If env is not ``None``, it must be a mapping that defines the - environment variables for the new process - :param creationflags: flags that can be used to control the creation of the - subprocess (see :class:`subprocess.Popen` for the specifics) - :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used - to specify process startup parameters (Windows only) - :param start_new_session: if ``true`` the setsid() system call will be made in the - child process prior to the execution of the subprocess. (POSIX only) - :param pass_fds: sequence of file descriptors to keep open between the parent and - child processes. (POSIX only) - :param user: effective user to run the process as (POSIX only) - :param group: effective group to run the process as (POSIX only) - :param extra_groups: supplementary groups to set in the subprocess (POSIX only) - :param umask: if not negative, this umask is applied in the child process before - running the given command (POSIX only) - :return: an asynchronous process object - - """ - kwargs: dict[str, Any] = {} - if user is not None: - kwargs["user"] = user - - if group is not None: - kwargs["group"] = group - - if extra_groups is not None: - kwargs["extra_groups"] = group - - if umask >= 0: - kwargs["umask"] = umask - - return await get_async_backend().open_process( - command, - stdin=stdin, - stdout=stdout, - stderr=stderr, - cwd=cwd, - env=env, - startupinfo=startupinfo, - creationflags=creationflags, - start_new_session=start_new_session, - pass_fds=pass_fds, - **kwargs, - ) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_synchronization.py b/.venv/lib/python3.12/site-packages/anyio/_core/_synchronization.py deleted file mode 100644 index 9098bee8..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_synchronization.py +++ /dev/null @@ -1,772 +0,0 @@ -from __future__ import annotations - -import math -from collections import deque -from collections.abc import Callable -from dataclasses import dataclass -from types import TracebackType -from typing import TypeVar - -from ..lowlevel import checkpoint_if_cancelled -from ._eventloop import get_async_backend -from ._exceptions import BusyResourceError, NoEventLoopError -from ._tasks import CancelScope -from ._testing import TaskInfo, get_current_task - -T = TypeVar("T") - - -@dataclass(frozen=True) -class EventStatistics: - """ - :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Event.wait` - """ - - tasks_waiting: int - - -@dataclass(frozen=True) -class CapacityLimiterStatistics: - """ - :ivar int borrowed_tokens: number of tokens currently borrowed by tasks - :ivar float total_tokens: total number of available tokens - :ivar tuple borrowers: tasks or other objects currently holding tokens borrowed from - this limiter - :ivar int tasks_waiting: number of tasks waiting on - :meth:`~.CapacityLimiter.acquire` or - :meth:`~.CapacityLimiter.acquire_on_behalf_of` - """ - - borrowed_tokens: int - total_tokens: float - borrowers: tuple[object, ...] - tasks_waiting: int - - -@dataclass(frozen=True) -class LockStatistics: - """ - :ivar bool locked: flag indicating if this lock is locked or not - :ivar ~anyio.TaskInfo owner: task currently holding the lock (or ``None`` if the - lock is not held by any task) - :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Lock.acquire` - """ - - locked: bool - owner: TaskInfo | None - tasks_waiting: int - - -@dataclass(frozen=True) -class ConditionStatistics: - """ - :ivar int tasks_waiting: number of tasks blocked on :meth:`~.Condition.wait` - :ivar ~anyio.LockStatistics lock_statistics: statistics of the underlying - :class:`~.Lock` - """ - - tasks_waiting: int - lock_statistics: LockStatistics - - -@dataclass(frozen=True) -class SemaphoreStatistics: - """ - :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Semaphore.acquire` - - """ - - tasks_waiting: int - - -class Event: - __slots__ = ("__weakref__",) - - def __new__(cls) -> Event: - try: - return get_async_backend().create_event() - except NoEventLoopError: - return EventAdapter() - - def set(self) -> None: - """Set the flag, notifying all listeners.""" - raise NotImplementedError - - def is_set(self) -> bool: - """Return ``True`` if the flag is set, ``False`` if not.""" - raise NotImplementedError - - async def wait(self) -> None: - """ - Wait until the flag has been set. - - If the flag has already been set when this method is called, it returns - immediately. - - """ - raise NotImplementedError - - def statistics(self) -> EventStatistics: - """Return statistics about the current state of this event.""" - raise NotImplementedError - - -class EventAdapter(Event): - __slots__ = "_internal_event", "_is_set" - - def __new__(cls) -> EventAdapter: - return object.__new__(cls) - - def __init__(self) -> None: - self._internal_event: Event | None = None - self._is_set = False - - @property - def _event(self) -> Event: - if self._internal_event is None: - self._internal_event = get_async_backend().create_event() - if self._is_set: - self._internal_event.set() - - return self._internal_event - - def set(self) -> None: - if self._internal_event is None: - self._is_set = True - else: - self._event.set() - - def is_set(self) -> bool: - if self._internal_event is None: - return self._is_set - - return self._internal_event.is_set() - - async def wait(self) -> None: - await self._event.wait() - - def statistics(self) -> EventStatistics: - if self._internal_event is None: - return EventStatistics(tasks_waiting=0) - - return self._internal_event.statistics() - - -class Lock: - __slots__ = ("__weakref__",) - - def __new__(cls, *, fast_acquire: bool = False) -> Lock: - try: - return get_async_backend().create_lock(fast_acquire=fast_acquire) - except NoEventLoopError: - return LockAdapter(fast_acquire=fast_acquire) - - async def __aenter__(self) -> None: - await self.acquire() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.release() - - async def acquire(self) -> None: - """Acquire the lock.""" - raise NotImplementedError - - def acquire_nowait(self) -> None: - """ - Acquire the lock, without blocking. - - :raises ~anyio.WouldBlock: if the operation would block - - """ - raise NotImplementedError - - def release(self) -> None: - """Release the lock.""" - raise NotImplementedError - - def locked(self) -> bool: - """Return True if the lock is currently held.""" - raise NotImplementedError - - def statistics(self) -> LockStatistics: - """ - Return statistics about the current state of this lock. - - .. versionadded:: 3.0 - """ - raise NotImplementedError - - -class LockAdapter(Lock): - __slots__ = "_internal_lock", "_fast_acquire" - - def __new__(cls, *, fast_acquire: bool = False) -> LockAdapter: - return object.__new__(cls) - - def __init__(self, *, fast_acquire: bool = False): - self._internal_lock: Lock | None = None - self._fast_acquire = fast_acquire - - @property - def _lock(self) -> Lock: - if self._internal_lock is None: - self._internal_lock = get_async_backend().create_lock( - fast_acquire=self._fast_acquire - ) - - return self._internal_lock - - async def __aenter__(self) -> None: - await self._lock.acquire() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - if self._internal_lock is not None: - self._internal_lock.release() - - async def acquire(self) -> None: - """Acquire the lock.""" - await self._lock.acquire() - - def acquire_nowait(self) -> None: - """ - Acquire the lock, without blocking. - - :raises ~anyio.WouldBlock: if the operation would block - - """ - self._lock.acquire_nowait() - - def release(self) -> None: - """Release the lock.""" - self._lock.release() - - def locked(self) -> bool: - """Return True if the lock is currently held.""" - return self._lock.locked() - - def statistics(self) -> LockStatistics: - """ - Return statistics about the current state of this lock. - - .. versionadded:: 3.0 - - """ - if self._internal_lock is None: - return LockStatistics(False, None, 0) - - return self._internal_lock.statistics() - - -class Condition: - __slots__ = "__weakref__", "_owner_task", "_lock", "_waiters" - - def __init__(self, lock: Lock | None = None): - self._owner_task: TaskInfo | None = None - self._lock = lock or Lock() - self._waiters: deque[Event] = deque() - - async def __aenter__(self) -> None: - await self.acquire() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.release() - - def _check_acquired(self) -> None: - if self._owner_task != get_current_task(): - raise RuntimeError("The current task is not holding the underlying lock") - - async def acquire(self) -> None: - """Acquire the underlying lock.""" - await self._lock.acquire() - self._owner_task = get_current_task() - - def acquire_nowait(self) -> None: - """ - Acquire the underlying lock, without blocking. - - :raises ~anyio.WouldBlock: if the operation would block - - """ - self._lock.acquire_nowait() - self._owner_task = get_current_task() - - def release(self) -> None: - """Release the underlying lock.""" - self._lock.release() - - def locked(self) -> bool: - """Return True if the lock is set.""" - return self._lock.locked() - - def notify(self, n: int = 1) -> None: - """Notify exactly n listeners.""" - self._check_acquired() - for _ in range(n): - try: - event = self._waiters.popleft() - except IndexError: - break - - event.set() - - def notify_all(self) -> None: - """Notify all the listeners.""" - self._check_acquired() - for event in self._waiters: - event.set() - - self._waiters.clear() - - async def wait(self) -> None: - """Wait for a notification.""" - await checkpoint_if_cancelled() - self._check_acquired() - event = Event() - self._waiters.append(event) - self.release() - try: - await event.wait() - except BaseException: - if not event.is_set(): - self._waiters.remove(event) - elif self._waiters: - # This task was notified by could not act on it, so pass - # it on to the next task - self._waiters.popleft().set() - - raise - finally: - with CancelScope(shield=True): - await self.acquire() - - async def wait_for(self, predicate: Callable[[], T]) -> T: - """ - Wait until a predicate becomes true. - - :param predicate: a callable that returns a truthy value when the condition is - met - :return: the result of the predicate - - .. versionadded:: 4.11.0 - - """ - while not (result := predicate()): - await self.wait() - - return result - - def statistics(self) -> ConditionStatistics: - """ - Return statistics about the current state of this condition. - - .. versionadded:: 3.0 - """ - return ConditionStatistics(len(self._waiters), self._lock.statistics()) - - -class Semaphore: - __slots__ = "__weakref__", "_fast_acquire" - - def __new__( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> Semaphore: - try: - return get_async_backend().create_semaphore( - initial_value, max_value=max_value, fast_acquire=fast_acquire - ) - except NoEventLoopError: - return SemaphoreAdapter(initial_value, max_value=max_value) - - def __init__( - self, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ): - if not isinstance(initial_value, int): - raise TypeError("initial_value must be an integer") - if initial_value < 0: - raise ValueError("initial_value must be >= 0") - if max_value is not None: - if not isinstance(max_value, int): - raise TypeError("max_value must be an integer or None") - if max_value < initial_value: - raise ValueError( - "max_value must be equal to or higher than initial_value" - ) - - self._fast_acquire = fast_acquire - - async def __aenter__(self) -> Semaphore: - await self.acquire() - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.release() - - async def acquire(self) -> None: - """Decrement the semaphore value, blocking if necessary.""" - raise NotImplementedError - - def acquire_nowait(self) -> None: - """ - Acquire the underlying lock, without blocking. - - :raises ~anyio.WouldBlock: if the operation would block - - """ - raise NotImplementedError - - def release(self) -> None: - """Increment the semaphore value.""" - raise NotImplementedError - - @property - def value(self) -> int: - """The current value of the semaphore.""" - raise NotImplementedError - - @property - def max_value(self) -> int | None: - """The maximum value of the semaphore.""" - raise NotImplementedError - - def statistics(self) -> SemaphoreStatistics: - """ - Return statistics about the current state of this semaphore. - - .. versionadded:: 3.0 - """ - raise NotImplementedError - - -class SemaphoreAdapter(Semaphore): - __slots__ = "_internal_semaphore", "_initial_value", "_max_value" - - def __new__( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> SemaphoreAdapter: - return object.__new__(cls) - - def __init__( - self, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> None: - super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire) - self._internal_semaphore: Semaphore | None = None - self._initial_value = initial_value - self._max_value = max_value - - @property - def _semaphore(self) -> Semaphore: - if self._internal_semaphore is None: - self._internal_semaphore = get_async_backend().create_semaphore( - self._initial_value, max_value=self._max_value - ) - - return self._internal_semaphore - - async def acquire(self) -> None: - await self._semaphore.acquire() - - def acquire_nowait(self) -> None: - self._semaphore.acquire_nowait() - - def release(self) -> None: - self._semaphore.release() - - @property - def value(self) -> int: - if self._internal_semaphore is None: - return self._initial_value - - return self._semaphore.value - - @property - def max_value(self) -> int | None: - return self._max_value - - def statistics(self) -> SemaphoreStatistics: - if self._internal_semaphore is None: - return SemaphoreStatistics(tasks_waiting=0) - - return self._semaphore.statistics() - - -class CapacityLimiter: - __slots__ = ("__weakref__",) - - def __new__(cls, total_tokens: float) -> CapacityLimiter: - try: - return get_async_backend().create_capacity_limiter(total_tokens) - except NoEventLoopError: - return CapacityLimiterAdapter(total_tokens) - - async def __aenter__(self) -> None: - raise NotImplementedError - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - raise NotImplementedError - - @property - def total_tokens(self) -> float: - """ - The total number of tokens available for borrowing. - - This is a read-write property. If the total number of tokens is increased, the - proportionate number of tasks waiting on this limiter will be granted their - tokens. - - .. versionchanged:: 3.0 - The property is now writable. - .. versionchanged:: 4.12 - The value can now be set to 0. - - """ - raise NotImplementedError - - @total_tokens.setter - def total_tokens(self, value: float) -> None: - raise NotImplementedError - - @property - def borrowed_tokens(self) -> int: - """The number of tokens that have currently been borrowed.""" - raise NotImplementedError - - @property - def available_tokens(self) -> float: - """The number of tokens currently available to be borrowed""" - raise NotImplementedError - - def acquire_nowait(self) -> None: - """ - Acquire a token for the current task without waiting for one to become - available. - - :raises ~anyio.WouldBlock: if there are no tokens available for borrowing - - """ - raise NotImplementedError - - def acquire_on_behalf_of_nowait(self, borrower: object) -> None: - """ - Acquire a token without waiting for one to become available. - - :param borrower: the entity borrowing a token - :raises ~anyio.WouldBlock: if there are no tokens available for borrowing - - """ - raise NotImplementedError - - async def acquire(self) -> None: - """ - Acquire a token for the current task, waiting if necessary for one to become - available. - - """ - raise NotImplementedError - - async def acquire_on_behalf_of(self, borrower: object) -> None: - """ - Acquire a token, waiting if necessary for one to become available. - - :param borrower: the entity borrowing a token - - """ - raise NotImplementedError - - def release(self) -> None: - """ - Release the token held by the current task. - - :raises RuntimeError: if the current task has not borrowed a token from this - limiter. - - """ - raise NotImplementedError - - def release_on_behalf_of(self, borrower: object) -> None: - """ - Release the token held by the given borrower. - - :raises RuntimeError: if the borrower has not borrowed a token from this - limiter. - - """ - raise NotImplementedError - - def statistics(self) -> CapacityLimiterStatistics: - """ - Return statistics about the current state of this limiter. - - .. versionadded:: 3.0 - - """ - raise NotImplementedError - - -class CapacityLimiterAdapter(CapacityLimiter): - __slots__ = "_internal_limiter", "_total_tokens" - - def __new__(cls, total_tokens: float) -> CapacityLimiterAdapter: - return object.__new__(cls) - - def __init__(self, total_tokens: float) -> None: - self._internal_limiter: CapacityLimiter | None = None - self.total_tokens = total_tokens - - @property - def _limiter(self) -> CapacityLimiter: - if self._internal_limiter is None: - self._internal_limiter = get_async_backend().create_capacity_limiter( - self._total_tokens - ) - - return self._internal_limiter - - async def __aenter__(self) -> None: - await self._limiter.__aenter__() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - return await self._limiter.__aexit__(exc_type, exc_val, exc_tb) - - @property - def total_tokens(self) -> float: - if self._internal_limiter is None: - return self._total_tokens - - return self._internal_limiter.total_tokens - - @total_tokens.setter - def total_tokens(self, value: float) -> None: - if not isinstance(value, int) and value is not math.inf: - raise TypeError("total_tokens must be an int or math.inf") - elif value < 0: - raise ValueError("total_tokens must be >= 0") - - if self._internal_limiter is None: - self._total_tokens = value - return - - self._limiter.total_tokens = value - - @property - def borrowed_tokens(self) -> int: - if self._internal_limiter is None: - return 0 - - return self._internal_limiter.borrowed_tokens - - @property - def available_tokens(self) -> float: - if self._internal_limiter is None: - return self._total_tokens - - return self._internal_limiter.available_tokens - - def acquire_nowait(self) -> None: - self._limiter.acquire_nowait() - - def acquire_on_behalf_of_nowait(self, borrower: object) -> None: - self._limiter.acquire_on_behalf_of_nowait(borrower) - - async def acquire(self) -> None: - await self._limiter.acquire() - - async def acquire_on_behalf_of(self, borrower: object) -> None: - await self._limiter.acquire_on_behalf_of(borrower) - - def release(self) -> None: - self._limiter.release() - - def release_on_behalf_of(self, borrower: object) -> None: - self._limiter.release_on_behalf_of(borrower) - - def statistics(self) -> CapacityLimiterStatistics: - if self._internal_limiter is None: - return CapacityLimiterStatistics( - borrowed_tokens=0, - total_tokens=self.total_tokens, - borrowers=(), - tasks_waiting=0, - ) - - return self._internal_limiter.statistics() - - -class ResourceGuard: - """ - A context manager for ensuring that a resource is only used by a single task at a - time. - - Entering this context manager while the previous has not exited it yet will trigger - :exc:`BusyResourceError`. - - :param action: the action to guard against (visible in the :exc:`BusyResourceError` - when triggered, e.g. "Another task is already {action} this resource") - - .. versionadded:: 4.1 - """ - - __slots__ = "__weakref__", "action", "_guarded" - - def __init__(self, action: str = "using"): - self.action: str = action - self._guarded = False - - def __enter__(self) -> None: - if self._guarded: - raise BusyResourceError(self.action) - - self._guarded = True - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self._guarded = False diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_tasks.py b/.venv/lib/python3.12/site-packages/anyio/_core/_tasks.py deleted file mode 100644 index 108393e8..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_tasks.py +++ /dev/null @@ -1,415 +0,0 @@ -from __future__ import annotations - -import math -import sys -from collections.abc import ( - Coroutine, - Generator, -) -from contextlib import ( - contextmanager, -) -from contextvars import ContextVar -from enum import Enum, auto -from inspect import iscoroutine -from types import TracebackType -from typing import Any, Generic, final - -from ..abc import TaskGroup, TaskStatus -from ._eventloop import get_async_backend, get_cancelled_exc_class -from ._exceptions import TaskCancelled, TaskFailed, TaskNotFinished - -if sys.version_info >= (3, 13): - from typing import TypeVar -else: - from typing_extensions import TypeVar - -if sys.version_info >= (3, 11): - from typing import Never, TypeVarTuple -else: - from typing_extensions import Never, TypeVarTuple - -T = TypeVar("T") -T_co = TypeVar("T_co", covariant=True) -T_startval = TypeVar("T_startval", covariant=True, default=Never) -PosArgsT = TypeVarTuple("PosArgsT") - -_current_task_handle: ContextVar[TaskHandle] = ContextVar("_current_task_handle") - - -class _IgnoredTaskStatus(TaskStatus[object]): - def started(self, value: object = None) -> None: - pass - - -TASK_STATUS_IGNORED = _IgnoredTaskStatus() - - -class CancelScope: - """ - Wraps a unit of work that can be made separately cancellable. - - :param deadline: The time (clock value) when this scope is cancelled automatically - :param shield: ``True`` to shield the cancel scope from external cancellation - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - """ - - __slots__ = ("__weakref__",) - - def __new__( - cls, *, deadline: float = math.inf, shield: bool = False - ) -> CancelScope: - return get_async_backend().create_cancel_scope(shield=shield, deadline=deadline) - - def cancel(self, reason: str | None = None) -> None: - """ - Cancel this scope immediately. - - :param reason: a message describing the reason for the cancellation - - """ - raise NotImplementedError - - @property - def deadline(self) -> float: - """ - The time (clock value) when this scope is cancelled automatically. - - Will be ``float('inf')`` if no timeout has been set. - - """ - raise NotImplementedError - - @deadline.setter - def deadline(self, value: float) -> None: - raise NotImplementedError - - @property - def cancel_called(self) -> bool: - """``True`` if :meth:`cancel` has been called.""" - raise NotImplementedError - - @property - def cancelled_caught(self) -> bool: - """ - ``True`` if this scope suppressed a cancellation exception it itself raised. - - This is typically used to check if any work was interrupted, or to see if the - scope was cancelled due to its deadline being reached. The value will, however, - only be ``True`` if the cancellation was triggered by the scope itself (and not - an outer scope). - - """ - raise NotImplementedError - - @property - def shield(self) -> bool: - """ - ``True`` if this scope is shielded from external cancellation. - - While a scope is shielded, it will not receive cancellations from outside. - - """ - raise NotImplementedError - - @shield.setter - def shield(self, value: bool) -> None: - raise NotImplementedError - - def __enter__(self) -> CancelScope: - raise NotImplementedError - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool: - raise NotImplementedError - - -@contextmanager -def fail_after( - delay: float | None, shield: bool = False -) -> Generator[CancelScope, None, None]: - """ - Create a context manager which raises a :class:`TimeoutError` if does not finish in - time. - - :param delay: maximum allowed time (in seconds) before raising the exception, or - ``None`` to disable the timeout - :param shield: ``True`` to shield the cancel scope from external cancellation - :return: a context manager that yields a cancel scope - :rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.CancelScope`\\] - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - current_time = get_async_backend().current_time - deadline = (current_time() + delay) if delay is not None else math.inf - with get_async_backend().create_cancel_scope( - deadline=deadline, shield=shield - ) as cancel_scope: - yield cancel_scope - - if cancel_scope.cancelled_caught and current_time() >= cancel_scope.deadline: - raise TimeoutError - - -def move_on_after(delay: float | None, shield: bool = False) -> CancelScope: - """ - Create a cancel scope with a deadline that expires after the given delay. - - :param delay: maximum allowed time (in seconds) before exiting the context block, or - ``None`` to disable the timeout - :param shield: ``True`` to shield the cancel scope from external cancellation - :return: a cancel scope - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - deadline = ( - (get_async_backend().current_time() + delay) if delay is not None else math.inf - ) - return get_async_backend().create_cancel_scope(deadline=deadline, shield=shield) - - -def current_effective_deadline() -> float: - """ - Return the nearest deadline among all the cancel scopes effective for the current - task. - - :return: a clock value from the event loop's internal clock (or ``float('inf')`` if - there is no deadline in effect, or ``float('-inf')`` if the current scope has - been cancelled) - :rtype: float - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - return get_async_backend().current_effective_deadline() - - -def create_task_group() -> TaskGroup: - """ - Create a task group. - - :return: a task group - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - return get_async_backend().create_task_group() - - -@final -class TaskHandle(Generic[T_co, T_startval]): - """ - Returned from the task-spawning methods of :class:`TaskGroup`. Can be awaited on to - get the return value of the task (or the raised exception). If the task was - terminated by a :exc:`BaseException`, :exc:`TaskFailed` will be raised (or its - subclass :exc:`TaskCancelled` if the task was cancelled). - - .. versionadded:: 4.14.0 - """ - - class Status(Enum): - """ - The status of a task handle. - - .. attribute:: PENDING - - The task has not finished yet. - .. attribute:: FINISHED - - The task has finished with a return value. - .. attribute:: CANCELLING - - The task has been cancelled but has not finished yet. - .. attribute:: CANCELLED - - The task was cancelled and has finished since. - .. attribute:: FAILED - - The task raised an exception. - """ - - PENDING = auto() - FINISHED = auto() - CANCELLING = auto() - CANCELLED = auto() - FAILED = auto() - - __slots__ = ( - "__weakref__", - "_coro", - "_name", - "_cancel_scope", - "_finished_event", - "_return_value", - "_start_value", - "_exception", - ) - - _return_value: T_co - _start_value: T_startval - - def __init__(self, coro: Coroutine[Any, Any, T_co], name: object) -> None: - from ._synchronization import Event - - self._coro = coro - self._cancel_scope = CancelScope() - self._finished_event = Event() - self._exception: BaseException | None = None - - if name is not None: - self._name = str(name) - elif iscoroutine(coro): - self._name = coro.__qualname__ - else: - self._name = str(coro) # coroutine-like object (e.g. asend() objects) - - async def _run_coro(self) -> None: - __tracebackhide__ = True - - with self._cancel_scope: - try: - retval = await self._coro - except BaseException as exc: - self._exception = exc - raise - else: - self._return_value = retval - finally: - self._finished_event.set() - del self # Break the reference cycle - - def cancel(self) -> None: - """ - Set the task to a cancelled state. - - This will interrupt any interruptible asynchronous operation, and will cause - any further awaits on this task to get immediately cancelled, unless done in - a shielded cancel scope. - - If the task has already finished, this method has no effect. - """ - if not self._finished_event.is_set(): - self._cancel_scope.cancel() - - @property - def coro(self) -> Coroutine[Any, Any, T_co]: - """ - The coroutine object that was passed to one of the task-spawning methods in - :class:`TaskGroup`. - """ - return self._coro - - @property - def status(self) -> TaskHandle.Status: - """ - The current status of the task. - - Every task starts in the :attr:`~TaskHandle.Status.PENDING` state. - If a task is cancelled while in this state, it will transition to the - :attr:`~TaskHandle.Status.CANCELLING` state. When the task finishes, it will - transition to one of the three final states ( - :attr:`~TaskHandle.Status.FINISHED`, :attr:`~TaskHandle.Status.FAILED`, or - :attr:`~TaskHandle.Status.CANCELLING`) depending on the exception the task - raised, if any. No other status transitions will happen. - """ - if not self._finished_event.is_set(): - if self._cancel_scope.cancel_called: - return TaskHandle.Status.CANCELLING - else: - return TaskHandle.Status.PENDING - elif self._exception is not None: - if isinstance(self._exception, get_cancelled_exc_class()): - return TaskHandle.Status.CANCELLED - else: - return TaskHandle.Status.FAILED - else: - return TaskHandle.Status.FINISHED - - @property - def name(self) -> str: - """The name of the task.""" - return self._name - - @property - def exception(self) -> BaseException | None: - """ - The exception raised by the task, or ``None`` if it finished without raising. - - :raises TaskNotFinished: if the task has not finished yet - :raises TaskCancelled: if the task was cancelled - - """ - match self.status: - case TaskHandle.Status.PENDING: - raise TaskNotFinished("the task has not finished yet") - case TaskHandle.Status.FINISHED: - return None - case TaskHandle.Status.CANCELLING: - raise TaskCancelled("the task was cancelled") - case TaskHandle.Status.CANCELLED: - raise TaskCancelled("the task was cancelled") from self._exception - case TaskHandle.Status.FAILED: - return self._exception - - @property - def return_value(self) -> T_co: - """ - The return value of the task. - - :raises TaskNotFinished: if the task has not finished yet - :raises TaskCancelled: if the task was cancelled - :raises TaskFailed: if the task raised an exception - - """ - match self.status: - case TaskHandle.Status.PENDING: - raise TaskNotFinished("the task has not finished yet") - case TaskHandle.Status.FINISHED: - return self._return_value - case TaskHandle.Status.CANCELLING: - raise TaskCancelled("the task was cancelled") - case TaskHandle.Status.CANCELLED: - raise TaskCancelled("the task was cancelled") from self._exception - case TaskHandle.Status.FAILED: - raise TaskFailed("the task raised an exception") from self._exception - - @property - def start_value(self) -> T_startval: - """ - The value passed to :meth:`task_status.started() <.abc.TaskStatus.started>`, - - :raises RuntimeError: if the task was not started with :meth:`TaskGroup.start() - <.abc.TaskGroup.start>` - """ - try: - return self._start_value - except AttributeError: - raise RuntimeError( - "the task was not started with TaskGroup.start()" - ) from None - - async def wait(self) -> None: - """ - Wait for the task to finish. - - This method will return as soon as the task has finished, no matter how it - happened. - """ - await self._finished_event.wait() - - def __await__(self) -> Generator[Any, Any, T_co]: - yield from self._finished_event.wait().__await__() - return self.return_value - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__} {self.status.name.lower()} " - f"name={self._name!r} coro={self._coro!r}>" - ) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_tempfile.py b/.venv/lib/python3.12/site-packages/anyio/_core/_tempfile.py deleted file mode 100644 index 75a09f79..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_tempfile.py +++ /dev/null @@ -1,613 +0,0 @@ -from __future__ import annotations - -import os -import sys -import tempfile -from collections.abc import Iterable -from io import BytesIO, TextIOWrapper -from types import TracebackType -from typing import ( - TYPE_CHECKING, - Any, - AnyStr, - Generic, - overload, -) - -from .. import to_thread -from .._core._fileio import AsyncFile -from ..lowlevel import checkpoint_if_cancelled - -if TYPE_CHECKING: - from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer - - -class TemporaryFile(Generic[AnyStr]): - """ - An asynchronous temporary file that is automatically created and cleaned up. - - This class provides an asynchronous context manager interface to a temporary file. - The file is created using Python's standard `tempfile.TemporaryFile` function in a - background thread, and is wrapped as an asynchronous file using `AsyncFile`. - - :param mode: The mode in which the file is opened. Defaults to "w+b". - :param buffering: The buffering policy (-1 means the default buffering). - :param encoding: The encoding used to decode or encode the file. Only applicable in - text mode. - :param newline: Controls how universal newlines mode works (only applicable in text - mode). - :param suffix: The suffix for the temporary file name. - :param prefix: The prefix for the temporary file name. - :param dir: The directory in which the temporary file is created. - :param errors: The error handling scheme used for encoding/decoding errors. - """ - - _async_file: AsyncFile[AnyStr] - - @overload - def __init__( - self: TemporaryFile[bytes], - mode: OpenBinaryMode = ..., - buffering: int = ..., - encoding: str | None = ..., - newline: str | None = ..., - suffix: str | None = ..., - prefix: str | None = ..., - dir: str | None = ..., - *, - errors: str | None = ..., - ): ... - @overload - def __init__( - self: TemporaryFile[str], - mode: OpenTextMode, - buffering: int = ..., - encoding: str | None = ..., - newline: str | None = ..., - suffix: str | None = ..., - prefix: str | None = ..., - dir: str | None = ..., - *, - errors: str | None = ..., - ): ... - - def __init__( - self, - mode: OpenTextMode | OpenBinaryMode = "w+b", - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - *, - errors: str | None = None, - ) -> None: - self.mode = mode - self.buffering = buffering - self.encoding = encoding - self.newline = newline - self.suffix: str | None = suffix - self.prefix: str | None = prefix - self.dir: str | None = dir - self.errors = errors - - async def __aenter__(self) -> AsyncFile[AnyStr]: - fp = await to_thread.run_sync( - lambda: tempfile.TemporaryFile( - self.mode, - self.buffering, - self.encoding, - self.newline, - self.suffix, - self.prefix, - self.dir, - errors=self.errors, - ) - ) - self._async_file = AsyncFile(fp) - return self._async_file - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - await self._async_file.aclose() - - -class NamedTemporaryFile(Generic[AnyStr]): - """ - An asynchronous named temporary file that is automatically created and cleaned up. - - This class provides an asynchronous context manager for a temporary file with a - visible name in the file system. It uses Python's standard - :func:`~tempfile.NamedTemporaryFile` function and wraps the file object with - :class:`AsyncFile` for asynchronous operations. - - :param mode: The mode in which the file is opened. Defaults to "w+b". - :param buffering: The buffering policy (-1 means the default buffering). - :param encoding: The encoding used to decode or encode the file. Only applicable in - text mode. - :param newline: Controls how universal newlines mode works (only applicable in text - mode). - :param suffix: The suffix for the temporary file name. - :param prefix: The prefix for the temporary file name. - :param dir: The directory in which the temporary file is created. - :param delete: Whether to delete the file when it is closed. - :param errors: The error handling scheme used for encoding/decoding errors. - :param delete_on_close: (Python 3.12+) Whether to delete the file on close. - """ - - _async_file: AsyncFile[AnyStr] - - @overload - def __init__( - self: NamedTemporaryFile[bytes], - mode: OpenBinaryMode = ..., - buffering: int = ..., - encoding: str | None = ..., - newline: str | None = ..., - suffix: str | None = ..., - prefix: str | None = ..., - dir: str | None = ..., - delete: bool = ..., - *, - errors: str | None = ..., - delete_on_close: bool = ..., - ): ... - @overload - def __init__( - self: NamedTemporaryFile[str], - mode: OpenTextMode, - buffering: int = ..., - encoding: str | None = ..., - newline: str | None = ..., - suffix: str | None = ..., - prefix: str | None = ..., - dir: str | None = ..., - delete: bool = ..., - *, - errors: str | None = ..., - delete_on_close: bool = ..., - ): ... - - def __init__( - self, - mode: OpenBinaryMode | OpenTextMode = "w+b", - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - delete: bool = True, - *, - errors: str | None = None, - delete_on_close: bool = True, - ) -> None: - self._params: dict[str, Any] = { - "mode": mode, - "buffering": buffering, - "encoding": encoding, - "newline": newline, - "suffix": suffix, - "prefix": prefix, - "dir": dir, - "delete": delete, - "errors": errors, - } - if sys.version_info >= (3, 12): - self._params["delete_on_close"] = delete_on_close - - async def __aenter__(self) -> AsyncFile[AnyStr]: - fp = await to_thread.run_sync( - lambda: tempfile.NamedTemporaryFile(**self._params) - ) - self._async_file = AsyncFile(fp) - return self._async_file - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - await self._async_file.aclose() - - -class SpooledTemporaryFile(AsyncFile[AnyStr]): - """ - An asynchronous spooled temporary file that starts in memory and is spooled to disk. - - This class provides an asynchronous interface to a spooled temporary file, much like - Python's standard :class:`~tempfile.SpooledTemporaryFile`. It supports asynchronous - write operations and provides a method to force a rollover to disk. - - :param max_size: Maximum size in bytes before the file is rolled over to disk. - :param mode: The mode in which the file is opened. Defaults to "w+b". - :param buffering: The buffering policy (-1 means the default buffering). - :param encoding: The encoding used to decode or encode the file (text mode only). - :param newline: Controls how universal newlines mode works (text mode only). - :param suffix: The suffix for the temporary file name. - :param prefix: The prefix for the temporary file name. - :param dir: The directory in which the temporary file is created. - :param errors: The error handling scheme used for encoding/decoding errors. - """ - - _rolled: bool = False - - @overload - def __init__( - self: SpooledTemporaryFile[bytes], - max_size: int = ..., - mode: OpenBinaryMode = ..., - buffering: int = ..., - encoding: str | None = ..., - newline: str | None = ..., - suffix: str | None = ..., - prefix: str | None = ..., - dir: str | None = ..., - *, - errors: str | None = ..., - ): ... - @overload - def __init__( - self: SpooledTemporaryFile[str], - max_size: int = ..., - mode: OpenTextMode = ..., - buffering: int = ..., - encoding: str | None = ..., - newline: str | None = ..., - suffix: str | None = ..., - prefix: str | None = ..., - dir: str | None = ..., - *, - errors: str | None = ..., - ): ... - - def __init__( - self, - max_size: int = 0, - mode: OpenBinaryMode | OpenTextMode = "w+b", - buffering: int = -1, - encoding: str | None = None, - newline: str | None = None, - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - *, - errors: str | None = None, - ) -> None: - self._tempfile_params: dict[str, Any] = { - "mode": mode, - "buffering": buffering, - "encoding": encoding, - "newline": newline, - "suffix": suffix, - "prefix": prefix, - "dir": dir, - "errors": errors, - } - self._max_size = max_size - if "b" in mode: - super().__init__(BytesIO()) # type: ignore[arg-type] - else: - super().__init__( - TextIOWrapper( # type: ignore[arg-type] - BytesIO(), - encoding=encoding, - errors=errors, - newline=newline, - write_through=True, - ) - ) - - async def aclose(self) -> None: - if not self._rolled: - self._fp.close() - return - - await super().aclose() - - async def _check(self) -> None: - if self._rolled or self._fp.tell() <= self._max_size: - return - - await self.rollover() - - async def rollover(self) -> None: - if self._rolled: - return - - self._rolled = True - buffer = self._fp - buffer.seek(0) - self._fp = await to_thread.run_sync( - lambda: tempfile.TemporaryFile(**self._tempfile_params) - ) - await self.write(buffer.read()) - buffer.close() - - @property - def closed(self) -> bool: - return self._fp.closed - - async def read(self, size: int = -1) -> AnyStr: - if not self._rolled: - await checkpoint_if_cancelled() - return self._fp.read(size) - - return await super().read(size) # type: ignore[return-value] - - async def read1(self: SpooledTemporaryFile[bytes], size: int = -1) -> bytes: - if not self._rolled: - await checkpoint_if_cancelled() - return self._fp.read1(size) - - return await super().read1(size) - - async def readline(self) -> AnyStr: - if not self._rolled: - await checkpoint_if_cancelled() - return self._fp.readline() - - return await super().readline() # type: ignore[return-value] - - async def readlines(self) -> list[AnyStr]: - if not self._rolled: - await checkpoint_if_cancelled() - return self._fp.readlines() - - return await super().readlines() # type: ignore[return-value] - - async def readinto(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int: - if not self._rolled: - await checkpoint_if_cancelled() - self._fp.readinto(b) - - return await super().readinto(b) - - async def readinto1(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int: - if not self._rolled: - await checkpoint_if_cancelled() - self._fp.readinto(b) - - return await super().readinto1(b) - - async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: - if not self._rolled: - await checkpoint_if_cancelled() - return self._fp.seek(offset, whence) - - return await super().seek(offset, whence) - - async def tell(self) -> int: - if not self._rolled: - await checkpoint_if_cancelled() - return self._fp.tell() - - return await super().tell() - - async def truncate(self, size: int | None = None) -> int: - if not self._rolled: - await checkpoint_if_cancelled() - return self._fp.truncate(size) - - return await super().truncate(size) - - @overload - async def write(self: SpooledTemporaryFile[bytes], b: ReadableBuffer) -> int: ... - @overload - async def write(self: SpooledTemporaryFile[str], b: str) -> int: ... - - async def write(self, b: ReadableBuffer | str) -> int: - """ - Asynchronously write data to the spooled temporary file. - - If the file has not yet been rolled over, the data is written synchronously, - and a rollover is triggered if the size exceeds the maximum size. - - :param s: The data to write. - :return: The number of bytes written. - :raises RuntimeError: If the underlying file is not initialized. - - """ - if not self._rolled: - await checkpoint_if_cancelled() - result = self._fp.write(b) - await self._check() - return result - - return await super().write(b) # type: ignore[misc] - - @overload - async def writelines( - self: SpooledTemporaryFile[bytes], lines: Iterable[ReadableBuffer] - ) -> None: ... - @overload - async def writelines( - self: SpooledTemporaryFile[str], lines: Iterable[str] - ) -> None: ... - - async def writelines(self, lines: Iterable[str] | Iterable[ReadableBuffer]) -> None: - """ - Asynchronously write a list of lines to the spooled temporary file. - - If the file has not yet been rolled over, the lines are written synchronously, - and a rollover is triggered if the size exceeds the maximum size. - - :param lines: An iterable of lines to write. - :raises RuntimeError: If the underlying file is not initialized. - - """ - if not self._rolled: - await checkpoint_if_cancelled() - result = self._fp.writelines(lines) - await self._check() - return result - - return await super().writelines(lines) # type: ignore[misc] - - -class TemporaryDirectory(Generic[AnyStr]): - """ - An asynchronous temporary directory that is created and cleaned up automatically. - - This class provides an asynchronous context manager for creating a temporary - directory. It wraps Python's standard :class:`~tempfile.TemporaryDirectory` to - perform directory creation and cleanup operations in a background thread. - - :param suffix: Suffix to be added to the temporary directory name. - :param prefix: Prefix to be added to the temporary directory name. - :param dir: The parent directory where the temporary directory is created. - :param ignore_cleanup_errors: Whether to ignore errors during cleanup - :param delete: Whether to delete the directory upon closing (Python 3.12+). - """ - - def __init__( - self, - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: AnyStr | None = None, - *, - ignore_cleanup_errors: bool = False, - delete: bool = True, - ) -> None: - self.suffix: AnyStr | None = suffix - self.prefix: AnyStr | None = prefix - self.dir: AnyStr | None = dir - self.ignore_cleanup_errors = ignore_cleanup_errors - self.delete = delete - - self._tempdir: tempfile.TemporaryDirectory | None = None - - async def __aenter__(self) -> str: - params: dict[str, Any] = { - "suffix": self.suffix, - "prefix": self.prefix, - "dir": self.dir, - "ignore_cleanup_errors": self.ignore_cleanup_errors, - } - if sys.version_info >= (3, 12): - params["delete"] = self.delete - - self._tempdir = await to_thread.run_sync( - lambda: tempfile.TemporaryDirectory(**params) - ) - return await to_thread.run_sync(self._tempdir.__enter__) - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - if self._tempdir is not None: - await to_thread.run_sync( - self._tempdir.__exit__, exc_type, exc_value, traceback - ) - - async def cleanup(self) -> None: - if self._tempdir is not None: - await to_thread.run_sync(self._tempdir.cleanup) - - -@overload -async def mkstemp( - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, - text: bool = False, -) -> tuple[int, str]: ... - - -@overload -async def mkstemp( - suffix: bytes | None = None, - prefix: bytes | None = None, - dir: bytes | None = None, - text: bool = False, -) -> tuple[int, bytes]: ... - - -async def mkstemp( - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: AnyStr | None = None, - text: bool = False, -) -> tuple[int, str | bytes]: - """ - Asynchronously create a temporary file and return an OS-level handle and the file - name. - - This function wraps `tempfile.mkstemp` and executes it in a background thread. - - :param suffix: Suffix to be added to the file name. - :param prefix: Prefix to be added to the file name. - :param dir: Directory in which the temporary file is created. - :param text: Whether the file is opened in text mode. - :return: A tuple containing the file descriptor and the file name. - - """ - return await to_thread.run_sync(tempfile.mkstemp, suffix, prefix, dir, text) - - -@overload -async def mkdtemp( - suffix: str | None = None, - prefix: str | None = None, - dir: str | None = None, -) -> str: ... - - -@overload -async def mkdtemp( - suffix: bytes | None = None, - prefix: bytes | None = None, - dir: bytes | None = None, -) -> bytes: ... - - -async def mkdtemp( - suffix: AnyStr | None = None, - prefix: AnyStr | None = None, - dir: AnyStr | None = None, -) -> str | bytes: - """ - Asynchronously create a temporary directory and return its path. - - This function wraps `tempfile.mkdtemp` and executes it in a background thread. - - :param suffix: Suffix to be added to the directory name. - :param prefix: Prefix to be added to the directory name. - :param dir: Parent directory where the temporary directory is created. - :return: The path of the created temporary directory. - - """ - return await to_thread.run_sync(tempfile.mkdtemp, suffix, prefix, dir) - - -async def gettempdir() -> str: - """ - Asynchronously return the name of the directory used for temporary files. - - This function wraps `tempfile.gettempdir` and executes it in a background thread. - - :return: The path of the temporary directory as a string. - - """ - return await to_thread.run_sync(tempfile.gettempdir) - - -async def gettempdirb() -> bytes: - """ - Asynchronously return the name of the directory used for temporary files in bytes. - - This function wraps `tempfile.gettempdirb` and executes it in a background thread. - - :return: The path of the temporary directory as bytes. - - """ - return await to_thread.run_sync(tempfile.gettempdirb) diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_testing.py b/.venv/lib/python3.12/site-packages/anyio/_core/_testing.py deleted file mode 100644 index 369e65c0..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_testing.py +++ /dev/null @@ -1,82 +0,0 @@ -from __future__ import annotations - -from collections.abc import Awaitable, Generator -from typing import Any, cast - -from ._eventloop import get_async_backend - - -class TaskInfo: - """ - Represents an asynchronous task. - - :ivar int id: the unique identifier of the task - :ivar parent_id: the identifier of the parent task, if any - :vartype parent_id: Optional[int] - :ivar str name: the description of the task (if any) - :ivar ~collections.abc.Coroutine coro: the coroutine object of the task - """ - - __slots__ = "_name", "id", "parent_id", "name", "coro" - - def __init__( - self, - id: int, - parent_id: int | None, - name: str | None, - coro: Generator[Any, Any, Any] | Awaitable[Any], - ): - func = get_current_task - self._name = f"{func.__module__}.{func.__qualname__}" - self.id: int = id - self.parent_id: int | None = parent_id - self.name: str | None = name - self.coro: Generator[Any, Any, Any] | Awaitable[Any] = coro - - def __eq__(self, other: object) -> bool: - if isinstance(other, TaskInfo): - return self.id == other.id - - return NotImplemented - - def __hash__(self) -> int: - return hash(self.id) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})" - - def has_pending_cancellation(self) -> bool: - """ - Return ``True`` if the task has a cancellation pending, ``False`` otherwise. - - """ - return False - - -def get_current_task() -> TaskInfo: - """ - Return the current task. - - :return: a representation of the current task - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - return get_async_backend().get_current_task() - - -def get_running_tasks() -> list[TaskInfo]: - """ - Return a list of running tasks in the current event loop. - - :return: a list of task info objects - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - return cast("list[TaskInfo]", get_async_backend().get_running_tasks()) - - -async def wait_all_tasks_blocked() -> None: - """Wait until all other tasks are waiting for something.""" - await get_async_backend().wait_all_tasks_blocked() diff --git a/.venv/lib/python3.12/site-packages/anyio/_core/_typedattr.py b/.venv/lib/python3.12/site-packages/anyio/_core/_typedattr.py deleted file mode 100644 index f358a448..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/_core/_typedattr.py +++ /dev/null @@ -1,81 +0,0 @@ -from __future__ import annotations - -from collections.abc import Callable, Mapping -from typing import Any, TypeVar, final, overload - -from ._exceptions import TypedAttributeLookupError - -T_Attr = TypeVar("T_Attr") -T_Default = TypeVar("T_Default") -undefined = object() - - -def typed_attribute() -> Any: - """Return a unique object, used to mark typed attributes.""" - return object() - - -class TypedAttributeSet: - """ - Superclass for typed attribute collections. - - Checks that every public attribute of every subclass has a type annotation. - """ - - def __init_subclass__(cls) -> None: - annotations: dict[str, Any] = getattr(cls, "__annotations__", {}) - for attrname in dir(cls): - if not attrname.startswith("_") and attrname not in annotations: - raise TypeError( - f"Attribute {attrname!r} is missing its type annotation" - ) - - super().__init_subclass__() - - -class TypedAttributeProvider: - """Base class for classes that wish to provide typed extra attributes.""" - - @property - def extra_attributes(self) -> Mapping[T_Attr, Callable[[], T_Attr]]: - """ - A mapping of the extra attributes to callables that return the corresponding - values. - - If the provider wraps another provider, the attributes from that wrapper should - also be included in the returned mapping (but the wrapper may override the - callables from the wrapped instance). - - """ - return {} - - @overload - def extra(self, attribute: T_Attr) -> T_Attr: ... - - @overload - def extra(self, attribute: T_Attr, default: T_Default) -> T_Attr | T_Default: ... - - @final - def extra(self, attribute: Any, default: object = undefined) -> object: - """ - extra(attribute, default=undefined) - - Return the value of the given typed extra attribute. - - :param attribute: the attribute (member of a :class:`~TypedAttributeSet`) to - look for - :param default: the value that should be returned if no value is found for the - attribute - :raises ~anyio.TypedAttributeLookupError: if the search failed and no default - value was given - - """ - try: - getter = self.extra_attributes[attribute] - except KeyError: - if default is undefined: - raise TypedAttributeLookupError("Attribute not found") from None - else: - return default - - return getter() diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/__init__.py b/.venv/lib/python3.12/site-packages/anyio/abc/__init__.py deleted file mode 100644 index d560ce3f..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/abc/__init__.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - -from ._eventloop import AsyncBackend as AsyncBackend -from ._resources import AsyncResource as AsyncResource -from ._sockets import ConnectedUDPSocket as ConnectedUDPSocket -from ._sockets import ConnectedUNIXDatagramSocket as ConnectedUNIXDatagramSocket -from ._sockets import IPAddressType as IPAddressType -from ._sockets import IPSockAddrType as IPSockAddrType -from ._sockets import SocketAttribute as SocketAttribute -from ._sockets import SocketListener as SocketListener -from ._sockets import SocketStream as SocketStream -from ._sockets import UDPPacketType as UDPPacketType -from ._sockets import UDPSocket as UDPSocket -from ._sockets import UNIXDatagramPacketType as UNIXDatagramPacketType -from ._sockets import UNIXDatagramSocket as UNIXDatagramSocket -from ._sockets import UNIXSocketStream as UNIXSocketStream -from ._streams import AnyByteReceiveStream as AnyByteReceiveStream -from ._streams import AnyByteSendStream as AnyByteSendStream -from ._streams import AnyByteStream as AnyByteStream -from ._streams import AnyByteStreamConnectable as AnyByteStreamConnectable -from ._streams import AnyUnreliableByteReceiveStream as AnyUnreliableByteReceiveStream -from ._streams import AnyUnreliableByteSendStream as AnyUnreliableByteSendStream -from ._streams import AnyUnreliableByteStream as AnyUnreliableByteStream -from ._streams import ByteReceiveStream as ByteReceiveStream -from ._streams import ByteSendStream as ByteSendStream -from ._streams import ByteStream as ByteStream -from ._streams import ByteStreamConnectable as ByteStreamConnectable -from ._streams import Listener as Listener -from ._streams import ObjectReceiveStream as ObjectReceiveStream -from ._streams import ObjectSendStream as ObjectSendStream -from ._streams import ObjectStream as ObjectStream -from ._streams import ObjectStreamConnectable as ObjectStreamConnectable -from ._streams import UnreliableObjectReceiveStream as UnreliableObjectReceiveStream -from ._streams import UnreliableObjectSendStream as UnreliableObjectSendStream -from ._streams import UnreliableObjectStream as UnreliableObjectStream -from ._subprocesses import Process as Process -from ._tasks import TaskGroup as TaskGroup -from ._tasks import TaskStatus as TaskStatus -from ._testing import TestRunner as TestRunner - -# Re-exported here, for backwards compatibility -# isort: off -from .._core._synchronization import ( - CapacityLimiter as CapacityLimiter, - Condition as Condition, - Event as Event, - Lock as Lock, - Semaphore as Semaphore, -) -from .._core._tasks import CancelScope as CancelScope -from ..from_thread import BlockingPortal as BlockingPortal - -# Re-export imports so they look like they live directly in this package -for __value in list(locals().values()): - if getattr(__value, "__module__", "").startswith("anyio.abc."): - __value.__module__ = __name__ - -del __value diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/_eventloop.py b/.venv/lib/python3.12/site-packages/anyio/abc/_eventloop.py deleted file mode 100644 index cad3fa76..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/abc/_eventloop.py +++ /dev/null @@ -1,410 +0,0 @@ -from __future__ import annotations - -import math -import sys -from abc import ABCMeta, abstractmethod -from collections.abc import AsyncIterator, Awaitable, Callable, Coroutine, Sequence -from contextlib import AbstractContextManager -from os import PathLike -from signal import Signals -from socket import AddressFamily, SocketKind, socket -from typing import ( - IO, - TYPE_CHECKING, - Any, - TypeAlias, - TypeVar, - overload, -) - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -if TYPE_CHECKING: - from _typeshed import FileDescriptorLike - - from .._core._synchronization import CapacityLimiter, Event, Lock, Semaphore - from .._core._tasks import CancelScope - from .._core._testing import TaskInfo - from ._sockets import ( - ConnectedUDPSocket, - ConnectedUNIXDatagramSocket, - IPSockAddrType, - SocketListener, - SocketStream, - UDPSocket, - UNIXDatagramSocket, - UNIXSocketStream, - ) - from ._subprocesses import Process - from ._tasks import TaskGroup - from ._testing import TestRunner - -T_Retval = TypeVar("T_Retval") -T_co = TypeVar("T_co", covariant=True) -PosArgsT = TypeVarTuple("PosArgsT") -StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes] - - -class AsyncBackend(metaclass=ABCMeta): - @classmethod - @abstractmethod - def run( - cls, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - options: dict[str, Any], - ) -> T_Retval: - """ - Run the given coroutine function in an asynchronous event loop. - - The current thread must not be already running an event loop. - - :param func: a coroutine function - :param args: positional arguments to ``func`` - :param kwargs: positional arguments to ``func`` - :param options: keyword arguments to call the backend ``run()`` implementation - with - :return: the return value of the coroutine function - """ - - @classmethod - @abstractmethod - def current_token(cls) -> object: - """ - Return an object that allows other threads to run code inside the event loop. - - :return: a token object, specific to the event loop running in the current - thread - """ - - @classmethod - @abstractmethod - def current_time(cls) -> float: - """ - Return the current value of the event loop's internal clock. - - :return: the clock value (seconds) - """ - - @classmethod - @abstractmethod - def cancelled_exception_class(cls) -> type[BaseException]: - """Return the exception class that is raised in a task if it's cancelled.""" - - @classmethod - @abstractmethod - async def checkpoint(cls) -> None: - """ - Check if the task has been cancelled, and allow rescheduling of other tasks. - - This is effectively the same as running :meth:`checkpoint_if_cancelled` and then - :meth:`cancel_shielded_checkpoint`. - """ - - @classmethod - async def checkpoint_if_cancelled(cls) -> None: - """ - Check if the current task group has been cancelled. - - This will check if the task has been cancelled, but will not allow other tasks - to be scheduled if not. - - """ - if cls.current_effective_deadline() == -math.inf: - await cls.checkpoint() - - @classmethod - async def cancel_shielded_checkpoint(cls) -> None: - """ - Allow the rescheduling of other tasks. - - This will give other tasks the opportunity to run, but without checking if the - current task group has been cancelled, unlike with :meth:`checkpoint`. - - """ - with cls.create_cancel_scope(shield=True): - await cls.sleep(0) - - @classmethod - @abstractmethod - async def sleep(cls, delay: float) -> None: - """ - Pause the current task for the specified duration. - - :param delay: the duration, in seconds - """ - - @classmethod - @abstractmethod - def create_cancel_scope( - cls, *, deadline: float = math.inf, shield: bool = False - ) -> CancelScope: - pass - - @classmethod - @abstractmethod - def current_effective_deadline(cls) -> float: - """ - Return the nearest deadline among all the cancel scopes effective for the - current task. - - :return: - - a clock value from the event loop's internal clock - - ``inf`` if there is no deadline in effect - - ``-inf`` if the current scope has been cancelled - :rtype: float - """ - - @classmethod - @abstractmethod - def create_task_group(cls) -> TaskGroup: - pass - - @classmethod - @abstractmethod - def create_event(cls) -> Event: - pass - - @classmethod - @abstractmethod - def create_lock(cls, *, fast_acquire: bool) -> Lock: - pass - - @classmethod - @abstractmethod - def create_semaphore( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> Semaphore: - pass - - @classmethod - @abstractmethod - def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: - pass - - @classmethod - @abstractmethod - async def run_sync_in_worker_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - abandon_on_cancel: bool = False, - limiter: CapacityLimiter | None = None, - ) -> T_Retval: - pass - - @classmethod - @abstractmethod - def check_cancelled(cls) -> None: - pass - - @classmethod - @abstractmethod - def run_async_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_co: - pass - - @classmethod - @abstractmethod - def run_sync_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_Retval: - pass - - @classmethod - @abstractmethod - async def open_process( - cls, - command: StrOrBytesPath | Sequence[StrOrBytesPath], - *, - stdin: int | IO[Any] | None, - stdout: int | IO[Any] | None, - stderr: int | IO[Any] | None, - **kwargs: Any, - ) -> Process: - pass - - @classmethod - @abstractmethod - def setup_process_pool_exit_at_shutdown(cls, workers: set[Process]) -> None: - pass - - @classmethod - @abstractmethod - async def connect_tcp( - cls, host: str, port: int, local_address: IPSockAddrType | None = None - ) -> SocketStream: - pass - - @classmethod - @abstractmethod - async def connect_unix(cls, path: str | bytes) -> UNIXSocketStream: - pass - - @classmethod - @abstractmethod - def create_tcp_listener(cls, sock: socket) -> SocketListener: - pass - - @classmethod - @abstractmethod - def create_unix_listener(cls, sock: socket) -> SocketListener: - pass - - @classmethod - @abstractmethod - async def create_udp_socket( - cls, - family: AddressFamily, - local_address: IPSockAddrType | None, - remote_address: IPSockAddrType | None, - reuse_port: bool, - ) -> UDPSocket | ConnectedUDPSocket: - pass - - @classmethod - @overload - async def create_unix_datagram_socket( - cls, raw_socket: socket, remote_path: None - ) -> UNIXDatagramSocket: ... - - @classmethod - @overload - async def create_unix_datagram_socket( - cls, raw_socket: socket, remote_path: str | bytes - ) -> ConnectedUNIXDatagramSocket: ... - - @classmethod - @abstractmethod - async def create_unix_datagram_socket( - cls, raw_socket: socket, remote_path: str | bytes | None - ) -> UNIXDatagramSocket | ConnectedUNIXDatagramSocket: - pass - - @classmethod - @abstractmethod - async def getaddrinfo( - cls, - host: bytes | str | None, - port: str | int | None, - *, - family: int | AddressFamily = 0, - type: int | SocketKind = 0, - proto: int = 0, - flags: int = 0, - ) -> Sequence[ - tuple[ - AddressFamily, - SocketKind, - int, - str, - tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes], - ] - ]: - pass - - @classmethod - @abstractmethod - async def getnameinfo( - cls, sockaddr: IPSockAddrType, flags: int = 0 - ) -> tuple[str, str]: - pass - - @classmethod - @abstractmethod - async def wait_readable(cls, obj: FileDescriptorLike) -> None: - pass - - @classmethod - @abstractmethod - async def wait_writable(cls, obj: FileDescriptorLike) -> None: - pass - - @classmethod - @abstractmethod - def notify_closing(cls, obj: FileDescriptorLike) -> None: - pass - - @classmethod - @abstractmethod - async def wrap_listener_socket(cls, sock: socket) -> SocketListener: - pass - - @classmethod - @abstractmethod - async def wrap_stream_socket(cls, sock: socket) -> SocketStream: - pass - - @classmethod - @abstractmethod - async def wrap_unix_stream_socket(cls, sock: socket) -> UNIXSocketStream: - pass - - @classmethod - @abstractmethod - async def wrap_udp_socket(cls, sock: socket) -> UDPSocket: - pass - - @classmethod - @abstractmethod - async def wrap_connected_udp_socket(cls, sock: socket) -> ConnectedUDPSocket: - pass - - @classmethod - @abstractmethod - async def wrap_unix_datagram_socket(cls, sock: socket) -> UNIXDatagramSocket: - pass - - @classmethod - @abstractmethod - async def wrap_connected_unix_datagram_socket( - cls, sock: socket - ) -> ConnectedUNIXDatagramSocket: - pass - - @classmethod - @abstractmethod - def current_default_thread_limiter(cls) -> CapacityLimiter: - pass - - @classmethod - @abstractmethod - def open_signal_receiver( - cls, *signals: Signals - ) -> AbstractContextManager[AsyncIterator[Signals]]: - pass - - @classmethod - @abstractmethod - def get_current_task(cls) -> TaskInfo: - pass - - @classmethod - @abstractmethod - def get_running_tasks(cls) -> Sequence[TaskInfo]: - pass - - @classmethod - @abstractmethod - async def wait_all_tasks_blocked(cls) -> None: - pass - - @classmethod - @abstractmethod - def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: - pass diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/_resources.py b/.venv/lib/python3.12/site-packages/anyio/abc/_resources.py deleted file mode 100644 index 10df115a..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/abc/_resources.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - -from abc import ABCMeta, abstractmethod -from types import TracebackType -from typing import TypeVar - -T = TypeVar("T") - - -class AsyncResource(metaclass=ABCMeta): - """ - Abstract base class for all closeable asynchronous resources. - - Works as an asynchronous context manager which returns the instance itself on enter, - and calls :meth:`aclose` on exit. - """ - - __slots__ = () - - async def __aenter__(self: T) -> T: - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - await self.aclose() - - @abstractmethod - async def aclose(self) -> None: - """Close the resource.""" diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/_sockets.py b/.venv/lib/python3.12/site-packages/anyio/abc/_sockets.py deleted file mode 100644 index feb26bd4..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/abc/_sockets.py +++ /dev/null @@ -1,399 +0,0 @@ -from __future__ import annotations - -import errno -import socket -from abc import abstractmethod -from collections.abc import Callable, Collection, Mapping -from contextlib import AsyncExitStack -from io import IOBase -from ipaddress import IPv4Address, IPv6Address -from socket import AddressFamily -from typing import Any, TypeAlias, TypeVar - -from .._core._eventloop import get_async_backend -from .._core._typedattr import ( - TypedAttributeProvider, - TypedAttributeSet, - typed_attribute, -) -from ._streams import ByteStream, Listener, UnreliableObjectStream -from ._tasks import TaskGroup - -IPAddressType: TypeAlias = str | IPv4Address | IPv6Address -IPSockAddrType: TypeAlias = tuple[str, int] -SockAddrType: TypeAlias = IPSockAddrType | str -UDPPacketType: TypeAlias = tuple[bytes, IPSockAddrType] -UNIXDatagramPacketType: TypeAlias = tuple[bytes, str] -T_Retval = TypeVar("T_Retval") - - -def _validate_socket( - sock_or_fd: socket.socket | int, - sock_type: socket.SocketKind, - addr_family: socket.AddressFamily = socket.AF_UNSPEC, - *, - require_connected: bool = False, - require_bound: bool = False, -) -> socket.socket: - if isinstance(sock_or_fd, int): - try: - sock = socket.socket(fileno=sock_or_fd) - except OSError as exc: - if exc.errno == errno.ENOTSOCK: - raise ValueError( - "the file descriptor does not refer to a socket" - ) from exc - elif require_connected: - raise ValueError("the socket must be connected") from exc - elif require_bound: - raise ValueError("the socket must be bound to a local address") from exc - else: - raise - elif isinstance(sock_or_fd, socket.socket): - sock = sock_or_fd - else: - raise TypeError( - f"expected an int or socket, got {type(sock_or_fd).__qualname__} instead" - ) - - try: - if require_connected: - try: - sock.getpeername() - except OSError as exc: - raise ValueError("the socket must be connected") from exc - - if require_bound: - try: - if sock.family in (socket.AF_INET, socket.AF_INET6): - bound_addr = sock.getsockname()[1] - else: - bound_addr = sock.getsockname() - except OSError: - bound_addr = None - - if not bound_addr: - raise ValueError("the socket must be bound to a local address") - - if addr_family != socket.AF_UNSPEC and sock.family != addr_family: - raise ValueError( - f"address family mismatch: expected {addr_family.name}, got " - f"{sock.family.name}" - ) - - if sock.type != sock_type: - raise ValueError( - f"socket type mismatch: expected {sock_type.name}, got {sock.type.name}" - ) - except BaseException: - # Avoid ResourceWarning from the locally constructed socket object - if isinstance(sock_or_fd, int): - sock.detach() - - raise - - sock.setblocking(False) - return sock - - -class SocketAttribute(TypedAttributeSet): - """ - .. attribute:: family - :type: socket.AddressFamily - - the address family of the underlying socket - - .. attribute:: local_address - :type: tuple[str, int] | str - - the local address the underlying socket is connected to - - .. attribute:: local_port - :type: int - - for IP based sockets, the local port the underlying socket is bound to - - .. attribute:: raw_socket - :type: socket.socket - - the underlying stdlib socket object - - .. attribute:: remote_address - :type: tuple[str, int] | str - - the remote address the underlying socket is connected to - - .. attribute:: remote_port - :type: int - - for IP based sockets, the remote port the underlying socket is connected to - """ - - family: AddressFamily = typed_attribute() - local_address: SockAddrType = typed_attribute() - local_port: int = typed_attribute() - raw_socket: socket.socket = typed_attribute() - remote_address: SockAddrType = typed_attribute() - remote_port: int = typed_attribute() - - -class _SocketProvider(TypedAttributeProvider): - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - from .._core._sockets import convert_ipv6_sockaddr as convert - - attributes: dict[Any, Callable[[], Any]] = { - SocketAttribute.family: lambda: self._raw_socket.family, - SocketAttribute.local_address: lambda: convert( - self._raw_socket.getsockname() - ), - SocketAttribute.raw_socket: lambda: self._raw_socket, - } - try: - peername: tuple[str, int] | None = convert(self._raw_socket.getpeername()) - except OSError: - peername = None - - # Provide the remote address for connected sockets - if peername is not None: - attributes[SocketAttribute.remote_address] = lambda: peername - - # Provide local and remote ports for IP based sockets - if self._raw_socket.family in (AddressFamily.AF_INET, AddressFamily.AF_INET6): - attributes[SocketAttribute.local_port] = lambda: ( - self._raw_socket.getsockname()[1] - ) - if peername is not None: - remote_port = peername[1] - attributes[SocketAttribute.remote_port] = lambda: remote_port - - return attributes - - @property - @abstractmethod - def _raw_socket(self) -> socket.socket: - pass - - -class SocketStream(ByteStream, _SocketProvider): - """ - Transports bytes over a socket. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - @classmethod - async def from_socket(cls, sock_or_fd: socket.socket | int) -> SocketStream: - """ - Wrap an existing socket object or file descriptor as a socket stream. - - The newly created socket wrapper takes ownership of the socket being passed in. - The existing socket must already be connected. - - :param sock_or_fd: a socket object or file descriptor - :return: a socket stream - - """ - sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_connected=True) - return await get_async_backend().wrap_stream_socket(sock) - - -class UNIXSocketStream(SocketStream): - @classmethod - async def from_socket(cls, sock_or_fd: socket.socket | int) -> UNIXSocketStream: - """ - Wrap an existing socket object or file descriptor as a UNIX socket stream. - - The newly created socket wrapper takes ownership of the socket being passed in. - The existing socket must already be connected. - - :param sock_or_fd: a socket object or file descriptor - :return: a UNIX socket stream - - """ - sock = _validate_socket( - sock_or_fd, socket.SOCK_STREAM, socket.AF_UNIX, require_connected=True - ) - return await get_async_backend().wrap_unix_stream_socket(sock) - - @abstractmethod - async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: - """ - Send file descriptors along with a message to the peer. - - :param message: a non-empty bytestring - :param fds: a collection of files (either numeric file descriptors or open file - or socket objects) - """ - - @abstractmethod - async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: - """ - Receive file descriptors along with a message from the peer. - - :param msglen: length of the message to expect from the peer - :param maxfds: maximum number of file descriptors to expect from the peer - :return: a tuple of (message, file descriptors) - """ - - -class SocketListener(Listener[SocketStream], _SocketProvider): - """ - Listens to incoming socket connections. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - @classmethod - async def from_socket( - cls, - sock_or_fd: socket.socket | int, - ) -> SocketListener: - """ - Wrap an existing socket object or file descriptor as a socket listener. - - The newly created listener takes ownership of the socket being passed in. - - :param sock_or_fd: a socket object or file descriptor - :return: a socket listener - - """ - sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_bound=True) - return await get_async_backend().wrap_listener_socket(sock) - - @abstractmethod - async def accept(self) -> SocketStream: - """Accept an incoming connection.""" - - async def serve( - self, - handler: Callable[[SocketStream], Any], - task_group: TaskGroup | None = None, - ) -> None: - from .. import create_task_group - - async with AsyncExitStack() as stack: - if task_group is None: - task_group = await stack.enter_async_context(create_task_group()) - - while True: - stream = await self.accept() - task_group.start_soon(handler, stream) - - -class UDPSocket(UnreliableObjectStream[UDPPacketType], _SocketProvider): - """ - Represents an unconnected UDP socket. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - @classmethod - async def from_socket(cls, sock_or_fd: socket.socket | int) -> UDPSocket: - """ - Wrap an existing socket object or file descriptor as a UDP socket. - - The newly created socket wrapper takes ownership of the socket being passed in. - The existing socket must be bound to a local address. - - :param sock_or_fd: a socket object or file descriptor - :return: a UDP socket - - """ - sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, require_bound=True) - return await get_async_backend().wrap_udp_socket(sock) - - async def sendto(self, data: bytes, host: str, port: int) -> None: - """ - Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, (host, port))). - - """ - return await self.send((data, (host, port))) - - -class ConnectedUDPSocket(UnreliableObjectStream[bytes], _SocketProvider): - """ - Represents an connected UDP socket. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - @classmethod - async def from_socket(cls, sock_or_fd: socket.socket | int) -> ConnectedUDPSocket: - """ - Wrap an existing socket object or file descriptor as a connected UDP socket. - - The newly created socket wrapper takes ownership of the socket being passed in. - The existing socket must already be connected. - - :param sock_or_fd: a socket object or file descriptor - :return: a connected UDP socket - - """ - sock = _validate_socket( - sock_or_fd, - socket.SOCK_DGRAM, - require_connected=True, - ) - return await get_async_backend().wrap_connected_udp_socket(sock) - - -class UNIXDatagramSocket( - UnreliableObjectStream[UNIXDatagramPacketType], _SocketProvider -): - """ - Represents an unconnected Unix datagram socket. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - @classmethod - async def from_socket( - cls, - sock_or_fd: socket.socket | int, - ) -> UNIXDatagramSocket: - """ - Wrap an existing socket object or file descriptor as a UNIX datagram - socket. - - The newly created socket wrapper takes ownership of the socket being passed in. - - :param sock_or_fd: a socket object or file descriptor - :return: a UNIX datagram socket - - """ - sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX) - return await get_async_backend().wrap_unix_datagram_socket(sock) - - async def sendto(self, data: bytes, path: str) -> None: - """Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, path)).""" - return await self.send((data, path)) - - -class ConnectedUNIXDatagramSocket(UnreliableObjectStream[bytes], _SocketProvider): - """ - Represents a connected Unix datagram socket. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - @classmethod - async def from_socket( - cls, - sock_or_fd: socket.socket | int, - ) -> ConnectedUNIXDatagramSocket: - """ - Wrap an existing socket object or file descriptor as a connected UNIX datagram - socket. - - The newly created socket wrapper takes ownership of the socket being passed in. - The existing socket must already be connected. - - :param sock_or_fd: a socket object or file descriptor - :return: a connected UNIX datagram socket - - """ - sock = _validate_socket( - sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX, require_connected=True - ) - return await get_async_backend().wrap_connected_unix_datagram_socket(sock) diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/_streams.py b/.venv/lib/python3.12/site-packages/anyio/abc/_streams.py deleted file mode 100644 index 186e3f50..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/abc/_streams.py +++ /dev/null @@ -1,233 +0,0 @@ -from __future__ import annotations - -from abc import ABCMeta, abstractmethod -from collections.abc import Callable -from typing import Any, Generic, TypeAlias, TypeVar - -from .._core._exceptions import EndOfStream -from .._core._typedattr import TypedAttributeProvider -from ._resources import AsyncResource -from ._tasks import TaskGroup - -T_Item = TypeVar("T_Item") -T_co = TypeVar("T_co", covariant=True) -T_contra = TypeVar("T_contra", contravariant=True) - - -class UnreliableObjectReceiveStream( - Generic[T_co], AsyncResource, TypedAttributeProvider -): - """ - An interface for receiving objects. - - This interface makes no guarantees that the received messages arrive in the order in - which they were sent, or that no messages are missed. - - Asynchronously iterating over objects of this type will yield objects matching the - given type parameter. - """ - - def __aiter__(self) -> UnreliableObjectReceiveStream[T_co]: - return self - - async def __anext__(self) -> T_co: - try: - return await self.receive() - except EndOfStream: - raise StopAsyncIteration from None - - @abstractmethod - async def receive(self) -> T_co: - """ - Receive the next item. - - :raises ~anyio.ClosedResourceError: if the receive stream has been explicitly - closed - :raises ~anyio.EndOfStream: if this stream has been closed from the other end - :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable - due to external causes - """ - - -class UnreliableObjectSendStream( - Generic[T_contra], AsyncResource, TypedAttributeProvider -): - """ - An interface for sending objects. - - This interface makes no guarantees that the messages sent will reach the - recipient(s) in the same order in which they were sent, or at all. - """ - - @abstractmethod - async def send(self, item: T_contra) -> None: - """ - Send an item to the peer(s). - - :param item: the item to send - :raises ~anyio.ClosedResourceError: if the send stream has been explicitly - closed - :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable - due to external causes - """ - - -class UnreliableObjectStream( - UnreliableObjectReceiveStream[T_Item], UnreliableObjectSendStream[T_Item] -): - """ - A bidirectional message stream which does not guarantee the order or reliability of - message delivery. - """ - - -class ObjectReceiveStream(UnreliableObjectReceiveStream[T_co]): - """ - A receive message stream which guarantees that messages are received in the same - order in which they were sent, and that no messages are missed. - """ - - -class ObjectSendStream(UnreliableObjectSendStream[T_contra]): - """ - A send message stream which guarantees that messages are delivered in the same order - in which they were sent, without missing any messages in the middle. - """ - - -class ObjectStream( - ObjectReceiveStream[T_Item], - ObjectSendStream[T_Item], - UnreliableObjectStream[T_Item], -): - """ - A bidirectional message stream which guarantees the order and reliability of message - delivery. - """ - - @abstractmethod - async def send_eof(self) -> None: - """ - Send an end-of-file indication to the peer. - - You should not try to send any further data to this stream after calling this - method. This method is idempotent (does nothing on successive calls). - """ - - -class ByteReceiveStream(AsyncResource, TypedAttributeProvider): - """ - An interface for receiving bytes from a single peer. - - Iterating this byte stream will yield a byte string of arbitrary length, but no more - than 65536 bytes. - """ - - def __aiter__(self) -> ByteReceiveStream: - return self - - async def __anext__(self) -> bytes: - try: - return await self.receive() - except EndOfStream: - raise StopAsyncIteration from None - - @abstractmethod - async def receive(self, max_bytes: int = 65536) -> bytes: - """ - Receive at most ``max_bytes`` bytes from the peer. - - .. note:: Implementers of this interface should not return an empty - :class:`bytes` object, and users should ignore them. - - :param max_bytes: maximum number of bytes to receive - :return: the received bytes - :raises ~anyio.EndOfStream: if this stream has been closed from the other end - """ - - -class ByteSendStream(AsyncResource, TypedAttributeProvider): - """An interface for sending bytes to a single peer.""" - - @abstractmethod - async def send(self, item: bytes) -> None: - """ - Send the given bytes to the peer. - - :param item: the bytes to send - """ - - -class ByteStream(ByteReceiveStream, ByteSendStream): - """A bidirectional byte stream.""" - - @abstractmethod - async def send_eof(self) -> None: - """ - Send an end-of-file indication to the peer. - - You should not try to send any further data to this stream after calling this - method. This method is idempotent (does nothing on successive calls). - """ - - -#: Type alias for all unreliable bytes-oriented receive streams. -AnyUnreliableByteReceiveStream: TypeAlias = ( - UnreliableObjectReceiveStream[bytes] | ByteReceiveStream -) -#: Type alias for all unreliable bytes-oriented send streams. -AnyUnreliableByteSendStream: TypeAlias = ( - UnreliableObjectSendStream[bytes] | ByteSendStream -) -#: Type alias for all unreliable bytes-oriented streams. -AnyUnreliableByteStream: TypeAlias = UnreliableObjectStream[bytes] | ByteStream -#: Type alias for all bytes-oriented receive streams. -AnyByteReceiveStream: TypeAlias = ObjectReceiveStream[bytes] | ByteReceiveStream -#: Type alias for all bytes-oriented send streams. -AnyByteSendStream: TypeAlias = ObjectSendStream[bytes] | ByteSendStream -#: Type alias for all bytes-oriented streams. -AnyByteStream: TypeAlias = ObjectStream[bytes] | ByteStream - - -class Listener(Generic[T_co], AsyncResource, TypedAttributeProvider): - """An interface for objects that let you accept incoming connections.""" - - @abstractmethod - async def serve( - self, handler: Callable[[T_co], Any], task_group: TaskGroup | None = None - ) -> None: - """ - Accept incoming connections as they come in and start tasks to handle them. - - :param handler: a callable that will be used to handle each accepted connection - :param task_group: the task group that will be used to start tasks for handling - each accepted connection (if omitted, an ad-hoc task group will be created) - """ - - -class ObjectStreamConnectable(Generic[T_co], metaclass=ABCMeta): - @abstractmethod - async def connect(self) -> ObjectStream[T_co]: - """ - Connect to the remote endpoint. - - :return: an object stream connected to the remote end - :raises ConnectionFailed: if the connection fails - """ - - -class ByteStreamConnectable(metaclass=ABCMeta): - @abstractmethod - async def connect(self) -> ByteStream: - """ - Connect to the remote endpoint. - - :return: a bytestream connected to the remote end - :raises ConnectionFailed: if the connection fails - """ - - -#: Type alias for all connectables returning bytestreams or bytes-oriented object streams -AnyByteStreamConnectable: TypeAlias = ( - ObjectStreamConnectable[bytes] | ByteStreamConnectable -) diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/_subprocesses.py b/.venv/lib/python3.12/site-packages/anyio/abc/_subprocesses.py deleted file mode 100644 index ce0564ce..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/abc/_subprocesses.py +++ /dev/null @@ -1,79 +0,0 @@ -from __future__ import annotations - -from abc import abstractmethod -from signal import Signals - -from ._resources import AsyncResource -from ._streams import ByteReceiveStream, ByteSendStream - - -class Process(AsyncResource): - """An asynchronous version of :class:`subprocess.Popen`.""" - - @abstractmethod - async def wait(self) -> int: - """ - Wait until the process exits. - - :return: the exit code of the process - """ - - @abstractmethod - def terminate(self) -> None: - """ - Terminates the process, gracefully if possible. - - On Windows, this calls ``TerminateProcess()``. - On POSIX systems, this sends ``SIGTERM`` to the process. - - .. seealso:: :meth:`subprocess.Popen.terminate` - """ - - @abstractmethod - def kill(self) -> None: - """ - Kills the process. - - On Windows, this calls ``TerminateProcess()``. - On POSIX systems, this sends ``SIGKILL`` to the process. - - .. seealso:: :meth:`subprocess.Popen.kill` - """ - - @abstractmethod - def send_signal(self, signal: Signals) -> None: - """ - Send a signal to the subprocess. - - .. seealso:: :meth:`subprocess.Popen.send_signal` - - :param signal: the signal number (e.g. :data:`signal.SIGHUP`) - """ - - @property - @abstractmethod - def pid(self) -> int: - """The process ID of the process.""" - - @property - @abstractmethod - def returncode(self) -> int | None: - """ - The return code of the process. If the process has not yet terminated, this will - be ``None``. - """ - - @property - @abstractmethod - def stdin(self) -> ByteSendStream | None: - """The stream for the standard input of the process.""" - - @property - @abstractmethod - def stdout(self) -> ByteReceiveStream | None: - """The stream for the standard output of the process.""" - - @property - @abstractmethod - def stderr(self) -> ByteReceiveStream | None: - """The stream for the standard error output of the process.""" diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/_tasks.py b/.venv/lib/python3.12/site-packages/anyio/abc/_tasks.py deleted file mode 100644 index 44ee3a70..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/abc/_tasks.py +++ /dev/null @@ -1,209 +0,0 @@ -from __future__ import annotations - -import sys -from abc import ABCMeta, abstractmethod -from collections.abc import Callable, Coroutine -from contextvars import Context -from types import TracebackType -from typing import TYPE_CHECKING, Any, Literal, Protocol, final, overload - -if sys.version_info >= (3, 13): - from typing import TypeVar -else: - from typing_extensions import TypeVar - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -if TYPE_CHECKING: - from .._core._tasks import CancelScope, TaskHandle - -T_co = TypeVar("T_co", covariant=True) -T_contra = TypeVar("T_contra", contravariant=True, default=None) -PosArgsT = TypeVarTuple("PosArgsT") - - -def get_callable_name(func: Callable, override: object = None) -> str: - if override is not None: - return str(override) - - module = getattr(func, "__module__", None) - qualname = getattr(func, "__qualname__", None) - return ".".join([x for x in (module, qualname) if x]) - - -def call_for_coroutine( - func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], - args: tuple[Unpack[PosArgsT]], - **kwargs: Any, -) -> Coroutine[Any, Any, T_co]: - """ - Call the given function with the given positional and keyword arguments. - - :return: the resulting coroutine - :raises TypeError: if the return value was not a coroutine object - - """ - coro = func(*args, **kwargs) - if not isinstance(coro, Coroutine): - prefix = f"{func.__module__}." if hasattr(func, "__module__") else "" - raise TypeError( - f"Expected {prefix}{func.__qualname__}() to return a coroutine, but " - f"the return value ({coro!r}) is not a coroutine object" - ) - - return coro - - -class TaskStatus(Protocol[T_contra]): - @overload - def started(self: TaskStatus[None]) -> None: ... - - @overload - def started(self, value: T_contra) -> None: ... - - def started(self, value: T_contra | None = None) -> None: - """ - Signal that the task has started. - - :param value: object passed back to the starter of the task - """ - - -class TaskGroup(metaclass=ABCMeta): - """ - Groups several asynchronous tasks together. - - :ivar cancel_scope: the cancel scope inherited by all child tasks - :vartype cancel_scope: CancelScope - - .. note:: On asyncio, support for eager task factories is considered to be - **experimental**. In particular, they don't follow the usual semantics of new - tasks being scheduled on the next iteration of the event loop, and may thus - cause unexpected behavior in code that wasn't written with such semantics in - mind. - """ - - cancel_scope: CancelScope - - def cancel(self, reason: str | None = None) -> None: - """ - Cancel this task group's cancel scope immediately. - - This is a shortcut for calling ``.cancel_scope.cancel()`` on the task group. - - :param reason: a message describing the reason for the cancellation - - .. versionadded:: 4.14.0 - - """ - self.cancel_scope.cancel(reason) - - @abstractmethod - def create_task( - self, - coro: Coroutine[Any, Any, T_co], - *, - name: object = None, - context: Context | None = None, - ) -> TaskHandle[T_co]: - """ - Create a new task from a coroutine object and schedule it to run. - - :param coro: a coroutine object - :param name: optional name to give the task - :param context: optional context to run the task in - :return: a task handle - - .. versionadded:: 4.14.0 - """ - - @final - def start_soon( - self, - func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], - *args: Unpack[PosArgsT], - name: object = None, - ) -> TaskHandle[T_co]: - """ - Start a new task in this task group. - - :param func: a coroutine function - :param args: positional arguments to call the function with - :param name: name of the task, for the purposes of introspection and debugging - :return: a task handle - - .. versionadded:: 3.0 - .. versionchanged:: 4.14.0 - This method now returns a task handle. - - """ - final_name = get_callable_name(func, name) - return self.create_task(call_for_coroutine(func, args), name=final_name) - - @overload - async def start( - self, - func: Callable[..., Coroutine[Any, Any, T_co]], - *args: object, - name: object = None, - return_handle: Literal[False] = ..., - ) -> Any: ... - - @overload - async def start( - self, - func: Callable[..., Coroutine[Any, Any, T_co]], - *args: object, - name: object = None, - return_handle: Literal[True], - ) -> TaskHandle[T_co, Any]: ... - - @abstractmethod - async def start( - self, - func: Callable[..., Coroutine[Any, Any, T_co]], - *args: object, - name: object = None, - return_handle: Literal[False] | Literal[True] = False, - ) -> Any: - """ - Start a new task and wait until it signals for readiness. - - The target callable must accept a keyword argument ``task_status`` (of type - :class:`TaskStatus`). Awaiting on this method will return whatever was passed to - ``task_status.started()`` (``None`` by default). - - .. note:: The :class:`TaskStatus` class is generic, and the type argument should - indicate the type of the value that will be passed to - ``task_status.started()``. - - :param func: a coroutine function that accepts the ``task_status`` keyword - argument - :param args: positional arguments to call the function with - :param name: an optional name for the task, for introspection and debugging - :param return_handle: if ``True``, return a :class:`TaskHandle` which also - contains the start value in ``start_value`` - :return: the value passed to ``task_status.started()`` - :raises RuntimeError: if the task finishes without calling - ``task_status.started()`` - - .. seealso:: :ref:`start_initialize` - - .. versionadded:: 3.0 - """ - - @abstractmethod - async def __aenter__(self) -> TaskGroup: - """Enter the task group context and allow starting new tasks.""" - - @abstractmethod - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool: - """Exit the task group context waiting for all tasks to finish.""" diff --git a/.venv/lib/python3.12/site-packages/anyio/abc/_testing.py b/.venv/lib/python3.12/site-packages/anyio/abc/_testing.py deleted file mode 100644 index 2a93fb7c..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/abc/_testing.py +++ /dev/null @@ -1,73 +0,0 @@ -from __future__ import annotations - -import types -from abc import ABCMeta, abstractmethod -from collections.abc import AsyncGenerator, Callable, Coroutine, Iterable -from typing import Any, TypeVar - -_T = TypeVar("_T") - - -class TestRunner(metaclass=ABCMeta): - """ - Encapsulates a running event loop. Every call made through this object will use the - same event loop. - """ - - def __enter__(self) -> TestRunner: - return self - - @abstractmethod - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: types.TracebackType | None, - ) -> bool | None: ... - - @abstractmethod - def run_asyncgen_fixture( - self, - fixture_func: Callable[..., AsyncGenerator[_T, Any]], - kwargs: dict[str, Any], - ) -> Iterable[_T]: - """ - Run an async generator fixture. - - :param fixture_func: the fixture function - :param kwargs: keyword arguments to call the fixture function with - :return: an iterator yielding the value yielded from the async generator - """ - - @abstractmethod - def run_fixture( - self, - fixture_func: Callable[..., Coroutine[Any, Any, _T]], - kwargs: dict[str, Any], - ) -> _T: - """ - Run an async fixture. - - :param fixture_func: the fixture function - :param kwargs: keyword arguments to call the fixture function with - :return: the return value of the fixture function - """ - - @abstractmethod - def run_test( - self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] - ) -> None: - """ - Run an async test function. - - :param test_func: the test function - :param kwargs: keyword arguments to call the test function with - """ - - @abstractmethod - def is_running(self) -> bool: - """ - Check if the test runner is running. - - :return: ``True`` if the coroutine is currently being run, ``False`` otherwise. - """ diff --git a/.venv/lib/python3.12/site-packages/anyio/from_thread.py b/.venv/lib/python3.12/site-packages/anyio/from_thread.py deleted file mode 100644 index 8c7914c2..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/from_thread.py +++ /dev/null @@ -1,582 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "BlockingPortal", - "BlockingPortalProvider", - "check_cancelled", - "run", - "run_sync", - "start_blocking_portal", -) - -import sys -from collections.abc import Awaitable, Callable, Coroutine, Generator -from concurrent.futures import Future -from contextlib import ( - AbstractAsyncContextManager, - AbstractContextManager, - contextmanager, -) -from dataclasses import dataclass, field -from functools import partial -from inspect import isawaitable -from threading import Lock, Thread, current_thread, get_ident -from types import TracebackType -from typing import ( - Any, - Generic, - TypeVar, - cast, - overload, -) - -from ._core._eventloop import ( - get_cancelled_exc_class, - threadlocals, -) -from ._core._eventloop import run as run_eventloop -from ._core._exceptions import NoEventLoopError -from ._core._synchronization import Event -from ._core._tasks import CancelScope, create_task_group -from .abc._tasks import TaskStatus -from .lowlevel import EventLoopToken, current_token - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -T_Retval = TypeVar("T_Retval") -T_co = TypeVar("T_co", covariant=True) -PosArgsT = TypeVarTuple("PosArgsT") - - -def _token_or_error(token: EventLoopToken | None) -> EventLoopToken: - if token is not None: - return token - - try: - return threadlocals.current_token - except AttributeError: - raise NoEventLoopError( - "Not running inside an AnyIO worker thread, and no event loop token was " - "provided" - ) from None - - -def run( - func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]], - *args: Unpack[PosArgsT], - token: EventLoopToken | None = None, -) -> T_co: - """ - Call a coroutine function from a worker thread. - - :param func: a coroutine function - :param args: positional arguments for the callable - :param token: an event loop token to use to get back to the event loop thread - (required if calling this function from outside an AnyIO worker thread) - :return: the return value of the coroutine function - :raises MissingTokenError: if no token was provided and called from outside an - AnyIO worker thread - :raises RunFinishedError: if the event loop tied to ``token`` is no longer running - - .. versionchanged:: 4.11.0 - Added the ``token`` parameter. - - """ - explicit_token = token is not None - token = _token_or_error(token) - return token.backend_class.run_async_from_thread( - func, args, token=token.native_token if explicit_token else None - ) - - -def run_sync( - func: Callable[[Unpack[PosArgsT]], T_Retval], - *args: Unpack[PosArgsT], - token: EventLoopToken | None = None, -) -> T_Retval: - """ - Call a function in the event loop thread from a worker thread. - - :param func: a callable - :param args: positional arguments for the callable - :param token: an event loop token to use to get back to the event loop thread - (required if calling this function from outside an AnyIO worker thread) - :return: the return value of the callable - :raises MissingTokenError: if no token was provided and called from outside an - AnyIO worker thread - :raises RunFinishedError: if the event loop tied to ``token`` is no longer running - - .. versionchanged:: 4.11.0 - Added the ``token`` parameter. - - """ - explicit_token = token is not None - token = _token_or_error(token) - return token.backend_class.run_sync_from_thread( - func, args, token=token.native_token if explicit_token else None - ) - - -class _BlockingAsyncContextManager(Generic[T_co], AbstractContextManager): - _enter_future: Future[T_co] - _exit_future: Future[bool | None] - _exit_event: Event - _exit_exc_info: tuple[ - type[BaseException] | None, BaseException | None, TracebackType | None - ] = (None, None, None) - - def __init__( - self, async_cm: AbstractAsyncContextManager[T_co], portal: BlockingPortal - ): - self._async_cm = async_cm - self._portal = portal - - async def run_async_cm(self) -> bool | None: - try: - self._exit_event = Event() - value = await self._async_cm.__aenter__() - except BaseException as exc: - self._enter_future.set_exception(exc) - raise - else: - self._enter_future.set_result(value) - - try: - # Wait for the sync context manager to exit. - # This next statement can raise `get_cancelled_exc_class()` if - # something went wrong in a task group in this async context - # manager. - await self._exit_event.wait() - finally: - # In case of cancellation, it could be that we end up here before - # `_BlockingAsyncContextManager.__exit__` is called, and an - # `_exit_exc_info` has been set. - result = await self._async_cm.__aexit__(*self._exit_exc_info) - - return result - - def __enter__(self) -> T_co: - self._enter_future = Future() - self._exit_future = self._portal.start_task_soon(self.run_async_cm) - return self._enter_future.result() - - def __exit__( - self, - __exc_type: type[BaseException] | None, - __exc_value: BaseException | None, - __traceback: TracebackType | None, - ) -> bool | None: - self._exit_exc_info = __exc_type, __exc_value, __traceback - self._portal.call(self._exit_event.set) - return self._exit_future.result() - - -class _BlockingPortalTaskStatus(TaskStatus): - def __init__(self, future: Future): - self._future = future - - def started(self, value: object = None) -> None: - self._future.set_result(value) - - -class BlockingPortal: - """ - An object that lets external threads run code in an asynchronous event loop. - - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - """ - - def __init__(self) -> None: - self._token = current_token() - self._event_loop_thread_id: int | None = get_ident() - self._stop_event = Event() - self._task_group = create_task_group() - - async def __aenter__(self) -> BlockingPortal: - await self._task_group.__aenter__() - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool: - await self.stop() - return await self._task_group.__aexit__(exc_type, exc_val, exc_tb) - - def _check_running(self) -> None: - if self._event_loop_thread_id is None: - raise RuntimeError("This portal is not running") - if self._event_loop_thread_id == get_ident(): - raise RuntimeError( - "This method cannot be called from the event loop thread" - ) - - async def sleep_until_stopped(self) -> None: - """Sleep until :meth:`stop` is called.""" - await self._stop_event.wait() - - async def stop(self, cancel_remaining: bool = False) -> None: - """ - Signal the portal to shut down. - - This marks the portal as no longer accepting new calls and exits from - :meth:`sleep_until_stopped`. - - :param cancel_remaining: ``True`` to cancel all the remaining tasks, ``False`` - to let them finish before returning - - """ - self._event_loop_thread_id = None - self._stop_event.set() - if cancel_remaining: - self._task_group.cancel_scope.cancel("the blocking portal is shutting down") - - async def _call_func( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - future: Future[T_Retval], - ) -> None: - event_loop_thread_id = self._event_loop_thread_id - - def callback(f: Future[T_Retval]) -> None: - if f.cancelled(): - if event_loop_thread_id == get_ident(): - scope.cancel("the future was cancelled") - elif event_loop_thread_id is not None: - run_sync( - scope.cancel, "the future was cancelled", token=self._token - ) - - try: - retval_or_awaitable = func(*args, **kwargs) - if isawaitable(retval_or_awaitable): - with CancelScope() as scope: - future.add_done_callback(callback) - retval = await retval_or_awaitable - else: - retval = retval_or_awaitable - except get_cancelled_exc_class(): - future.cancel() - future.set_running_or_notify_cancel() - except BaseException as exc: - if not future.cancelled(): - future.set_exception(exc) - - # Let base exceptions fall through - if not isinstance(exc, Exception): - raise - else: - if not future.cancelled(): - future.set_result(retval) - finally: - scope = None # type: ignore[assignment] - - def _spawn_task_from_thread( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - name: object, - future: Future[T_Retval], - ) -> None: - """ - Spawn a new task using the given callable. - - :param func: a callable - :param args: positional arguments to be passed to the callable - :param kwargs: keyword arguments to be passed to the callable - :param name: name of the task (will be coerced to a string if not ``None``) - :param future: a future that will resolve to the return value of the callable, - or the exception raised during its execution - - """ - run_sync( - partial(self._task_group.start_soon, name=name), - self._call_func, - func, - args, - kwargs, - future, - token=self._token, - ) - - @overload - def call( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - *args: Unpack[PosArgsT], - ) -> T_Retval: ... - - @overload - def call( - self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] - ) -> T_Retval: ... - - def call( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], - *args: Unpack[PosArgsT], - ) -> T_Retval: - """ - Call the given function in the event loop thread. - - If the callable returns a coroutine object, it is awaited on. - - :param func: any callable - :raises RuntimeError: if the portal is not running or if this method is called - from within the event loop thread - - """ - return cast(T_Retval, self.start_task_soon(func, *args).result()) - - @overload - def start_task_soon( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - *args: Unpack[PosArgsT], - name: object = None, - ) -> Future[T_Retval]: ... - - @overload - def start_task_soon( - self, - func: Callable[[Unpack[PosArgsT]], T_Retval], - *args: Unpack[PosArgsT], - name: object = None, - ) -> Future[T_Retval]: ... - - def start_task_soon( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], - *args: Unpack[PosArgsT], - name: object = None, - ) -> Future[T_Retval]: - """ - Start a task in the portal's task group. - - The task will be run inside a cancel scope which can be cancelled by cancelling - the returned future. - - :param func: the target function - :param args: positional arguments passed to ``func`` - :param name: name of the task (will be coerced to a string if not ``None``) - :return: a future that resolves with the return value of the callable if the - task completes successfully, or with the exception raised in the task - :raises RuntimeError: if the portal is not running or if this method is called - from within the event loop thread - :rtype: concurrent.futures.Future[T_Retval] - - .. versionadded:: 3.0 - - """ - self._check_running() - f: Future[T_Retval] = Future() - self._spawn_task_from_thread(func, args, {}, name, f) - return f - - def start_task( - self, - func: Callable[..., Awaitable[T_Retval]], - *args: object, - name: object = None, - ) -> tuple[Future[T_Retval], Any]: - """ - Start a task in the portal's task group and wait until it signals for readiness. - - This method works the same way as :meth:`.abc.TaskGroup.start`. - - :param func: the target function - :param args: positional arguments passed to ``func`` - :param name: name of the task (will be coerced to a string if not ``None``) - :return: a tuple of (future, task_status_value) where the ``task_status_value`` - is the value passed to ``task_status.started()`` from within the target - function - :rtype: tuple[concurrent.futures.Future[T_Retval], Any] - - .. versionadded:: 3.0 - - """ - - def task_done(future: Future[T_Retval]) -> None: - if not task_status_future.done(): - if future.cancelled(): - task_status_future.cancel() - elif future.exception(): - task_status_future.set_exception(future.exception()) - else: - exc = RuntimeError( - "Task exited without calling task_status.started()" - ) - task_status_future.set_exception(exc) - - self._check_running() - task_status_future: Future = Future() - task_status = _BlockingPortalTaskStatus(task_status_future) - f: Future = Future() - f.add_done_callback(task_done) - self._spawn_task_from_thread(func, args, {"task_status": task_status}, name, f) - return f, task_status_future.result() - - def wrap_async_context_manager( - self, cm: AbstractAsyncContextManager[T_co] - ) -> AbstractContextManager[T_co]: - """ - Wrap an async context manager as a synchronous context manager via this portal. - - Spawns a task that will call both ``__aenter__()`` and ``__aexit__()``, stopping - in the middle until the synchronous context manager exits. - - :param cm: an asynchronous context manager - :return: a synchronous context manager - - .. versionadded:: 2.1 - - """ - return _BlockingAsyncContextManager(cm, self) - - -@dataclass -class BlockingPortalProvider: - """ - A manager for a blocking portal. Used as a context manager. The first thread to - enter this context manager causes a blocking portal to be started with the specific - parameters, and the last thread to exit causes the portal to be shut down. Thus, - there will be exactly one blocking portal running in this context as long as at - least one thread has entered this context manager. - - The parameters are the same as for :func:`~anyio.run`. - - :param backend: name of the backend - :param backend_options: backend options - - .. versionadded:: 4.4 - """ - - backend: str = "asyncio" - backend_options: dict[str, Any] | None = None - _lock: Lock = field(init=False, default_factory=Lock) - _leases: int = field(init=False, default=0) - _portal: BlockingPortal = field(init=False) - _portal_cm: AbstractContextManager[BlockingPortal] | None = field( - init=False, default=None - ) - - def __enter__(self) -> BlockingPortal: - with self._lock: - if self._portal_cm is None: - self._portal_cm = start_blocking_portal( - self.backend, self.backend_options - ) - self._portal = self._portal_cm.__enter__() - - self._leases += 1 - return self._portal - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - portal_cm: AbstractContextManager[BlockingPortal] | None = None - with self._lock: - assert self._portal_cm - assert self._leases > 0 - self._leases -= 1 - if not self._leases: - portal_cm = self._portal_cm - self._portal_cm = None - del self._portal - - if portal_cm: - portal_cm.__exit__(None, None, None) - - -@contextmanager -def start_blocking_portal( - backend: str = "asyncio", - backend_options: dict[str, Any] | None = None, - *, - name: str | None = None, -) -> Generator[BlockingPortal, Any, None]: - """ - Start a new event loop in a new thread and run a blocking portal in its main task. - - The parameters are the same as for :func:`~anyio.run`. - - :param backend: name of the backend - :param backend_options: backend options - :param name: name of the thread - :return: a context manager that yields a blocking portal - - .. versionchanged:: 3.0 - Usage as a context manager is now required. - - """ - - async def run_portal() -> None: - async with BlockingPortal() as portal_: - if name is None: - current_thread().name = f"{backend}-portal-{id(portal_):x}" - - future.set_result(portal_) - await portal_.sleep_until_stopped() - - def run_blocking_portal() -> None: - if future.set_running_or_notify_cancel(): - try: - run_eventloop( - run_portal, backend=backend, backend_options=backend_options - ) - except BaseException as exc: - if not future.done(): - future.set_exception(exc) - - future: Future[BlockingPortal] = Future() - thread = Thread(target=run_blocking_portal, daemon=True, name=name) - thread.start() - try: - cancel_remaining_tasks = False - portal = future.result() - try: - yield portal - except BaseException: - cancel_remaining_tasks = True - raise - finally: - try: - portal.call(portal.stop, cancel_remaining_tasks) - except RuntimeError: - pass - finally: - thread.join() - - -def check_cancelled() -> None: - """ - Check if the cancel scope of the host task's running the current worker thread has - been cancelled. - - If the host task's current cancel scope has indeed been cancelled, the - backend-specific cancellation exception will be raised. - - :raises RuntimeError: if the current thread was not spawned by - :func:`.to_thread.run_sync` - - """ - try: - token: EventLoopToken = threadlocals.current_token - except AttributeError: - raise NoEventLoopError( - "This function can only be called inside an AnyIO worker thread" - ) from None - - token.backend_class.check_cancelled() diff --git a/.venv/lib/python3.12/site-packages/anyio/functools.py b/.venv/lib/python3.12/site-packages/anyio/functools.py deleted file mode 100644 index b0bdfb45..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/functools.py +++ /dev/null @@ -1,400 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "AsyncCacheInfo", - "AsyncCacheParameters", - "AsyncLRUCacheWrapper", - "cache", - "lru_cache", - "reduce", -) - -import functools -from collections import OrderedDict -from collections.abc import ( - AsyncIterable, - Awaitable, - Callable, - Coroutine, - Hashable, - Iterable, -) -from functools import update_wrapper -from inspect import iscoroutinefunction -from typing import ( - Any, - Generic, - NamedTuple, - ParamSpec, - TypedDict, - TypeVar, - cast, - final, - overload, -) -from weakref import WeakKeyDictionary - -from ._core._eventloop import current_time -from ._core._synchronization import Lock -from .lowlevel import RunVar, checkpoint - -T = TypeVar("T") -S = TypeVar("S") -P = ParamSpec("P") -lru_cache_items: RunVar[ - WeakKeyDictionary[ - AsyncLRUCacheWrapper[Any, Any], - OrderedDict[ - Hashable, - tuple[_InitialMissingType, Lock, float | None] - | tuple[Any, None, float | None], - ], - ] -] = RunVar("lru_cache_items") - - -class _InitialMissingType: - pass - - -initial_missing: _InitialMissingType = _InitialMissingType() - - -class AsyncCacheInfo(NamedTuple): - hits: int - misses: int - maxsize: int | None - currsize: int - ttl: int | None - - -class AsyncCacheParameters(TypedDict): - maxsize: int | None - typed: bool - always_checkpoint: bool - ttl: int | None - - -class _LRUMethodWrapper(Generic[T]): - def __init__(self, wrapper: AsyncLRUCacheWrapper[..., T], instance: object): - self.__wrapper = wrapper - self.__instance = instance - - def cache_info(self) -> AsyncCacheInfo: - return self.__wrapper.cache_info() - - def cache_parameters(self) -> AsyncCacheParameters: - return self.__wrapper.cache_parameters() - - def cache_clear(self) -> None: - self.__wrapper.cache_clear() - - async def __call__(self, *args: Any, **kwargs: Any) -> T: - if self.__instance is None: - return await self.__wrapper(*args, **kwargs) - - return await self.__wrapper(self.__instance, *args, **kwargs) - - -@final -class AsyncLRUCacheWrapper(Generic[P, T]): - def __init__( - self, - func: Callable[P, Awaitable[T]], - maxsize: int | None, - typed: bool, - always_checkpoint: bool, - ttl: int | None, - ): - self.__wrapped__ = func - self._hits: int = 0 - self._misses: int = 0 - self._maxsize = max(maxsize, 0) if maxsize is not None else None - self._currsize: int = 0 - self._typed = typed - self._always_checkpoint = always_checkpoint - self._ttl = ttl - update_wrapper(self, func) - - def cache_info(self) -> AsyncCacheInfo: - return AsyncCacheInfo( - self._hits, self._misses, self._maxsize, self._currsize, self._ttl - ) - - def cache_parameters(self) -> AsyncCacheParameters: - return { - "maxsize": self._maxsize, - "typed": self._typed, - "always_checkpoint": self._always_checkpoint, - "ttl": self._ttl, - } - - def cache_clear(self) -> None: - if cache := lru_cache_items.get(None): - cache.pop(self, None) - self._hits = self._misses = self._currsize = 0 - - async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: - # Easy case first: if maxsize == 0, no caching is done - if self._maxsize == 0: - value = await self.__wrapped__(*args, **kwargs) - self._misses += 1 - return value - - # The key is constructed as a flat tuple to avoid memory overhead - key: tuple[Any, ...] = args - if kwargs: - # initial_missing is used as a separator - key += (initial_missing,) + sum(kwargs.items(), ()) - - if self._typed: - key += tuple(type(arg) for arg in args) - if kwargs: - key += (initial_missing,) + tuple(type(val) for val in kwargs.values()) - - try: - cache = lru_cache_items.get() - except LookupError: - cache = WeakKeyDictionary() - lru_cache_items.set(cache) - - try: - cache_entry = cache[self] - except KeyError: - cache_entry = cache[self] = OrderedDict() - - cached_value: T | _InitialMissingType - try: - cached_value, lock, expires_at = cache_entry[key] - except KeyError: - # We're the first task to call this function - cached_value, lock, expires_at = ( - initial_missing, - Lock(fast_acquire=not self._always_checkpoint), - None, - ) - cache_entry[key] = cached_value, lock, expires_at - - if lock is None: - if expires_at is not None and current_time() >= expires_at: - self._currsize -= 1 - cached_value, lock, expires_at = ( - initial_missing, - Lock(fast_acquire=not self._always_checkpoint), - None, - ) - cache_entry[key] = cached_value, lock, expires_at - else: - # The value was already cached - self._hits += 1 - cache_entry.move_to_end(key) - if self._always_checkpoint: - await checkpoint() - - return cast(T, cached_value) - - async with lock: - # Check if another task filled the cache while we acquired the lock - if (cached_value := cache_entry[key][0]) is initial_missing: - self._misses += 1 - if self._maxsize is not None and self._currsize >= self._maxsize: - cache_entry.popitem(last=False) - else: - self._currsize += 1 - - value = await self.__wrapped__(*args, **kwargs) - expires_at = ( - current_time() + self._ttl if self._ttl is not None else None - ) - cache_entry[key] = value, None, expires_at - else: - # Another task filled the cache while we were waiting for the lock - self._hits += 1 - cache_entry.move_to_end(key) - value = cast(T, cached_value) - - return value - - def __get__( - self, instance: object, owner: type | None = None - ) -> _LRUMethodWrapper[T]: - wrapper = _LRUMethodWrapper(self, instance) - update_wrapper(wrapper, self.__wrapped__) - return wrapper - - -class _LRUCacheWrapper: - def __init__( - self, maxsize: int | None, typed: bool, always_checkpoint: bool, ttl: int | None - ): - self._maxsize = maxsize - self._typed = typed - self._always_checkpoint = always_checkpoint - self._ttl = ttl - - @overload - def __call__( # type: ignore[overload-overlap] - self, func: Callable[P, Coroutine[Any, Any, T]], / - ) -> AsyncLRUCacheWrapper[P, T]: ... - - @overload - def __call__( - self, func: Callable[..., T], / - ) -> functools._lru_cache_wrapper[T]: ... - - def __call__( - self, f: Callable[P, Coroutine[Any, Any, T]] | Callable[..., T], / - ) -> AsyncLRUCacheWrapper[P, T] | functools._lru_cache_wrapper[T]: - if iscoroutinefunction(f): - return AsyncLRUCacheWrapper( - f, self._maxsize, self._typed, self._always_checkpoint, self._ttl - ) - - return functools.lru_cache(maxsize=self._maxsize, typed=self._typed)(f) # type: ignore[arg-type] - - -@overload -def cache( # type: ignore[overload-overlap] - func: Callable[P, Coroutine[Any, Any, T]], / -) -> AsyncLRUCacheWrapper[P, T]: ... - - -@overload -def cache(func: Callable[..., T], /) -> functools._lru_cache_wrapper[T]: ... - - -def cache(func: Callable[..., Any] | Callable[P, Coroutine[Any, Any, Any]], /) -> Any: - """ - A convenient shortcut for :func:`lru_cache` with ``maxsize=None``. - - This is the asynchronous equivalent to :func:`functools.cache`. - - """ - return lru_cache(maxsize=None)(func) - - -@overload -def lru_cache( - *, - maxsize: int | None = ..., - typed: bool = ..., - always_checkpoint: bool = ..., - ttl: int | None = ..., -) -> _LRUCacheWrapper: ... - - -@overload -def lru_cache( # type: ignore[overload-overlap] - func: Callable[P, Coroutine[Any, Any, T]], / -) -> AsyncLRUCacheWrapper[P, T]: ... - - -@overload -def lru_cache(func: Callable[..., T], /) -> functools._lru_cache_wrapper[T]: ... - - -def lru_cache( - func: Callable[..., Coroutine[Any, Any, Any]] | Callable[..., Any] | None = None, - /, - *, - maxsize: int | None = 128, - typed: bool = False, - always_checkpoint: bool = False, - ttl: int | None = None, -) -> Any: - """ - An asynchronous version of :func:`functools.lru_cache`. - - If a synchronous function is passed, the standard library - :func:`functools.lru_cache` is applied instead. - - :param always_checkpoint: if ``True``, every call to the cached function will be - guaranteed to yield control to the event loop at least once - :param ttl: time in seconds after which to invalidate cache entries - - .. note:: Caches and locks are managed on a per-event loop basis. - - """ - if func is None: - return _LRUCacheWrapper(maxsize, typed, always_checkpoint, ttl) - - if not callable(func): - raise TypeError("the first argument must be callable") - - return _LRUCacheWrapper(maxsize, typed, always_checkpoint, ttl)(func) - - -@overload -async def reduce( - function: Callable[[T, S], Awaitable[T]], - iterable: Iterable[S] | AsyncIterable[S], - /, - initial: T, -) -> T: ... - - -@overload -async def reduce( - function: Callable[[T, T], Awaitable[T]], - iterable: Iterable[T] | AsyncIterable[T], - /, -) -> T: ... - - -async def reduce( # type: ignore[misc] - function: Callable[[T, T], Awaitable[T]] | Callable[[T, S], Awaitable[T]], - iterable: Iterable[T] | Iterable[S] | AsyncIterable[T] | AsyncIterable[S], - /, - initial: T | _InitialMissingType = initial_missing, -) -> T: - """ - Asynchronous version of :func:`functools.reduce`. - - :param function: a coroutine function that takes two arguments: the accumulated - value and the next element from the iterable - :param iterable: an iterable or async iterable - :param initial: the initial value (if missing, the first element of the iterable is - used as the initial value) - - """ - element: Any - function_called = False - if isinstance(iterable, AsyncIterable): - async_it = iterable.__aiter__() - if initial is initial_missing: - try: - value = cast(T, await async_it.__anext__()) - except StopAsyncIteration: - raise TypeError( - "reduce() of empty sequence with no initial value" - ) from None - else: - value = cast(T, initial) - - async for element in async_it: - value = await function(value, element) - function_called = True - elif isinstance(iterable, Iterable): - it = iter(iterable) - if initial is initial_missing: - try: - value = cast(T, next(it)) - except StopIteration: - raise TypeError( - "reduce() of empty sequence with no initial value" - ) from None - else: - value = cast(T, initial) - - for element in it: - value = await function(value, element) - function_called = True - else: - raise TypeError("reduce() argument 2 must be an iterable or async iterable") - - # Make sure there is at least one checkpoint, even if an empty iterable and an - # initial value were given - if not function_called: - await checkpoint() - - return value diff --git a/.venv/lib/python3.12/site-packages/anyio/itertools.py b/.venv/lib/python3.12/site-packages/anyio/itertools.py deleted file mode 100644 index 7e5248e4..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/itertools.py +++ /dev/null @@ -1,626 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "accumulate", - "batched", - "Chain", - "combinations", - "combinations_with_replacement", - "compress", - "count", - "cycle", - "dropwhile", - "filterfalse", - "groupby", - "islice", - "pairwise", - "permutations", - "product", - "repeat", - "starmap", - "tee", - "takewhile", - "zip_longest", -) - -import itertools -import operator -import sys -from collections.abc import ( - AsyncGenerator, - AsyncIterable, - AsyncIterator, - Awaitable, - Callable, - Iterable, - Iterator, -) -from dataclasses import dataclass, field -from typing import Any, Generic, TypeVar, cast, overload - -from ._core._synchronization import Lock -from ._core._tasks import CancelScope -from .lowlevel import cancel_shielded_checkpoint, checkpoint, checkpoint_if_cancelled - -T = TypeVar("T") -R = TypeVar("R") -_tee_end = object() - - -@dataclass(eq=False) -class _IterableAsyncIterator(AsyncIterator[T]): - iterator: Iterator[T] - - async def __anext__(self) -> T: - await checkpoint_if_cancelled() - try: - result = next(self.iterator) - except StopIteration: - await cancel_shielded_checkpoint() - raise StopAsyncIteration from None - - await cancel_shielded_checkpoint() - return result - - -def _iterate(iterable: Iterable[T] | AsyncIterable[T]) -> AsyncIterator[T]: - if isinstance(iterable, AsyncIterator): - return iterable - - if isinstance(iterable, AsyncIterable): - return iterable.__aiter__() - - return _IterableAsyncIterator(iter(iterable)) - - -@dataclass(eq=False) -class _TeeLink(Generic[T]): - value: object | None = None - next: _TeeLink[T] | None = None - filled: bool = False - - -@dataclass(eq=False) -class _TeeState(Generic[T]): - iterator: AsyncIterator[T] - lock: Lock = field(default_factory=Lock) - - async def fill(self, link: _TeeLink[T]) -> bool: - if link.filled: - return False - - async with self.lock: - if link.filled: - return True - - link.value = await anext(self.iterator, _tee_end) - if link.value is not _tee_end: - link.next = _TeeLink() - - link.filled = True - return True - - -class _TeeAsyncIterator(AsyncIterator[T]): - _state: _TeeState[T] - _link: _TeeLink[T] - _element_yielded: bool - - def __init__( - self, iterable: Iterable[T] | AsyncIterable[T] | _TeeAsyncIterator[T] - ) -> None: - if isinstance(iterable, _TeeAsyncIterator): - self._state = iterable._state - self._link = iterable._link - else: - self._state = _TeeState(_iterate(iterable)) - self._link = _TeeLink() - - self._element_yielded = False - - async def __anext__(self) -> T: - had_yieldpoint = await self._state.fill(self._link) - if self._link.value is _tee_end: - if not self._element_yielded: - await checkpoint() - - raise StopAsyncIteration - - if not had_yieldpoint: - await checkpoint_if_cancelled() - - self._element_yielded = True - value = cast(T, self._link.value) - next_link = self._link.next - assert next_link is not None - self._link = next_link - if not had_yieldpoint: - await cancel_shielded_checkpoint() - - return value - - -async def _operator_add(x: T, y: T) -> T: - return operator.add(x, y) - - -async def accumulate( - iterable: Iterable[T] | AsyncIterable[T], - function: Callable[[T, T], Awaitable[T]] = _operator_add, - *, - initial: T | None = None, -) -> AsyncGenerator[T, None]: - iterator = _iterate(iterable) - if initial is None: - try: - total = await anext(iterator) - except StopAsyncIteration: - await checkpoint() - return - else: - await checkpoint_if_cancelled() - total = initial - await cancel_shielded_checkpoint() - - yield total - - async for element in iterator: - total = await function(total, element) - yield total - - -async def batched( - iterable: Iterable[T] | AsyncIterable[T], n: int, *, strict: bool = False -) -> AsyncGenerator[tuple[T, ...], None]: - if n < 1: - raise ValueError("n must be at least one") - - iterator = _iterate(iterable) - - while True: - batch: list[T] = [] - for _ in range(n): - try: - batch.append(await anext(iterator)) - except StopAsyncIteration: - if not batch: - await checkpoint() - return - if strict: - raise ValueError("batched(): incomplete batch") from None - - yield tuple(batch) - return - - yield tuple(batch) - - -class Chain: - def __call__( - self, *iterables: Iterable[T] | AsyncIterable[T] - ) -> AsyncGenerator[T, None]: - return self.from_iterable(iterables) - - async def from_iterable( - self, - iterables: ( - Iterable[Iterable[T] | AsyncIterable[T]] - | AsyncIterable[Iterable[T] | AsyncIterable[T]] - ), - ) -> AsyncGenerator[T, None]: - element_yielded = False - outer_iter = _iterate(iterables) - - try: - async for iterable in outer_iter: - async for element in _iterate(iterable): - element_yielded = True - yield element - finally: - aclose = getattr(outer_iter, "aclose", None) - if aclose is not None: - with CancelScope(shield=True): - await aclose() - - if not element_yielded: - await checkpoint() - - -chain: Chain = Chain() - - -async def combinations( - iterable: Iterable[T] | AsyncIterable[T], r: int -) -> AsyncGenerator[tuple[T, ...], None]: - pool: list[T] = [element async for element in _iterate(iterable)] - async for combination in _iterate(itertools.combinations(pool, r)): - yield combination - - -async def combinations_with_replacement( - iterable: Iterable[T] | AsyncIterable[T], r: int -) -> AsyncGenerator[tuple[T, ...], None]: - pool: list[T] = [element async for element in _iterate(iterable)] - async for combination in _iterate(itertools.combinations_with_replacement(pool, r)): - yield combination - - -async def compress( - data: Iterable[T] | AsyncIterable[T], - selectors: Iterable[object] | AsyncIterable[object], -) -> AsyncGenerator[T, None]: - data_iterator = _iterate(data) - selector_iterator = _iterate(selectors) - element_yielded = False - - while True: - try: - datum = await anext(data_iterator) - selector = await anext(selector_iterator) - except StopAsyncIteration: - if not element_yielded: - await checkpoint() - - return - - if selector: - element_yielded = True - yield datum - - -async def count(start: int = 0, step: int = 1) -> AsyncGenerator[int, None]: - n = start - while True: - await checkpoint_if_cancelled() - value = n - n += step - await cancel_shielded_checkpoint() - yield value - - -async def cycle( - iterable: Iterable[T] | AsyncIterable[T], -) -> AsyncGenerator[T, None]: - saved: list[T] = [] - async for element in _iterate(iterable): - saved.append(element) - yield element - - if not saved: - await checkpoint() - return - - while True: - for element in saved: - await checkpoint() - yield element - - -async def dropwhile( - predicate: Callable[[T], Awaitable[object]], - iterable: Iterable[T] | AsyncIterable[T], -) -> AsyncGenerator[T, None]: - element_yielded = False - dropping = True - - async for element in _iterate(iterable): - if dropping and await predicate(element): - continue - - dropping = False - element_yielded = True - yield element - - if not element_yielded: - await checkpoint() - - -async def filterfalse( - predicate: Callable[[T], Awaitable[object]], - iterable: Iterable[T] | AsyncIterable[T], -) -> AsyncGenerator[T, None]: - element_yielded = False - - async for element in _iterate(iterable): - if not await predicate(element): - element_yielded = True - yield element - - if not element_yielded: - await checkpoint() - - -@overload -def groupby( - iterable: Iterable[T] | AsyncIterable[T], -) -> AsyncGenerator[tuple[T, list[T]], None]: ... - - -@overload -def groupby( - iterable: Iterable[T] | AsyncIterable[T], - key: Callable[[T], Awaitable[R]], -) -> AsyncGenerator[tuple[R, list[T]], None]: ... - - -async def groupby( - iterable: Iterable[T] | AsyncIterable[T], - key: Callable[[T], Awaitable[object]] | None = None, -) -> AsyncGenerator[tuple[object, list[T]], None]: - iterator = _iterate(iterable) - try: - element = await anext(iterator) - except StopAsyncIteration: - await checkpoint() - return - - group_key = element if key is None else await key(element) - values = [element] - - async for element in iterator: - next_key = element if key is None else await key(element) - if next_key != group_key: - completed_group = group_key, values - group_key = next_key - values = [element] - yield completed_group - else: - values.append(element) - - yield group_key, values - - -@overload -def islice( - iterable: Iterable[T] | AsyncIterable[T], - stop: int | None, - /, -) -> AsyncGenerator[T, None]: ... - - -@overload -def islice( - iterable: Iterable[T] | AsyncIterable[T], - start: int | None, - stop: int | None, - step: int | None = 1, - /, -) -> AsyncGenerator[T, None]: ... - - -async def islice( - iterable: Iterable[T] | AsyncIterable[T], - *args: int | None, -) -> AsyncGenerator[T, None]: - if not args: - raise TypeError("islice expected at least 2 arguments, got 1") - if len(args) > 3: - raise TypeError(f"islice expected at most 4 arguments, got {len(args) + 1}") - - slice_args = slice(*args) - - start_message = ( - "Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize." - ) - stop_message = ( - "Stop argument for islice() must be None or an integer: 0 <= x <= sys.maxsize." - ) - step_message = "Step for islice() must be a positive integer or None." - - def normalize_index(value: object, message: str) -> int: - try: - index = operator.index(cast(Any, value)) - except TypeError: - raise ValueError(message) from None - - if index < 0 or index > sys.maxsize: - raise ValueError(message) - - return index - - start = ( - 0 - if slice_args.start is None - else normalize_index(slice_args.start, start_message) - ) - stop = ( - None - if slice_args.stop is None - else normalize_index(slice_args.stop, stop_message) - ) - step = ( - 1 if slice_args.step is None else normalize_index(slice_args.step, step_message) - ) - - if step <= 0: - raise ValueError(step_message) - - if stop == 0 or start == stop: - await checkpoint() - return - - iterator = _iterate(iterable) - index = 0 - element_yielded = False - - while stop is None or index < stop: - try: - element = await anext(iterator) - except StopAsyncIteration: - if not element_yielded: - await checkpoint() - - return - - if index >= start and (index - start) % step == 0: - index += 1 - element_yielded = True - yield element - else: - index += 1 - - if not element_yielded: - await checkpoint() - - -async def pairwise( - iterable: Iterable[T] | AsyncIterable[T], -) -> AsyncGenerator[tuple[T, T], None]: - iterator = _iterate(iterable) - try: - previous = await anext(iterator) - except StopAsyncIteration: - await checkpoint() - return - - element_yielded = False - async for element in iterator: - element_yielded = True - pair = (previous, element) - previous = element - yield pair - - if not element_yielded: - await checkpoint() - - -async def permutations( - iterable: Iterable[T] | AsyncIterable[T], r: int | None = None -) -> AsyncGenerator[tuple[T, ...], None]: - pool: list[T] = [element async for element in _iterate(iterable)] - n = len(pool) - if r is None: - r = n - elif not isinstance(r, int): - raise TypeError("Expected int as r") - elif r < 0: - raise ValueError("r must be non-negative") - - async for permutation in _iterate(itertools.permutations(pool, r)): - yield permutation - - -async def product( - *iterables: Iterable[T] | AsyncIterable[T], repeat: int = 1 -) -> AsyncGenerator[tuple[T, ...], None]: - repeat = operator.index(repeat) - if repeat < 0: - raise ValueError("repeat argument cannot be negative") - - pools: list[tuple[T, ...]] = [] - for iterable in iterables: - pool: list[T] = [element async for element in _iterate(iterable)] - pools.append(tuple(pool)) - - async for value in _iterate(itertools.product(*pools, repeat=repeat)): - yield value - - -async def repeat(element: T, times: int | None = None) -> AsyncGenerator[T, None]: - if times is None: - while True: - await checkpoint() - yield element - - remaining = operator.index(cast(Any, times)) - if remaining <= 0: - await checkpoint() - return - - while remaining > 0: - await checkpoint_if_cancelled() - remaining -= 1 - await cancel_shielded_checkpoint() - yield element - - -async def starmap( - function: Callable[..., Awaitable[R]], - iterable: ( - Iterable[Iterable[object] | AsyncIterable[object]] - | AsyncIterable[Iterable[object] | AsyncIterable[object]] - ), -) -> AsyncGenerator[R, None]: - result_yielded = False - - async for args_iterable in _iterate(iterable): - args = [element async for element in _iterate(args_iterable)] - result_yielded = True - yield await function(*args) - - if not result_yielded: - await checkpoint() - - -def tee( - iterable: Iterable[T] | AsyncIterable[T], n: int = 2 -) -> tuple[AsyncIterator[T], ...]: - n = operator.index(cast(Any, n)) - if n < 0: - raise ValueError("n must be >= 0") - if n == 0: - return () - - iterator = _TeeAsyncIterator(iterable) - iterators: list[AsyncIterator[T]] = [iterator] - iterators.extend(_TeeAsyncIterator(iterator) for _ in range(n - 1)) - return tuple(iterators) - - -async def takewhile( - predicate: Callable[[T], Awaitable[object]], - iterable: Iterable[T] | AsyncIterable[T], -) -> AsyncGenerator[T, None]: - element_yielded = False - - async for element in _iterate(iterable): - if not await predicate(element): - if not element_yielded: - await checkpoint() - - return - - element_yielded = True - yield element - - if not element_yielded: - await checkpoint() - - -async def zip_longest( - *iterables: Iterable[object] | AsyncIterable[object], - fillvalue: object = None, -) -> AsyncGenerator[tuple[object, ...], None]: - iterators = [_iterate(iterable) for iterable in iterables] - num_active = len(iterators) - if not num_active: - await checkpoint() - return - - active = [True] * num_active - tuple_yielded = False - - while True: - values: list[object] = [] - for index, iterator in enumerate(iterators): - if not active[index]: - values.append(fillvalue) - continue - - try: - value = await anext(iterator) - except StopAsyncIteration: - active[index] = False - num_active -= 1 - if not num_active: - if not tuple_yielded: - await checkpoint() - - return - - value = fillvalue - - values.append(value) - - tuple_yielded = True - yield tuple(values) diff --git a/.venv/lib/python3.12/site-packages/anyio/lowlevel.py b/.venv/lib/python3.12/site-packages/anyio/lowlevel.py deleted file mode 100644 index d0457918..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/lowlevel.py +++ /dev/null @@ -1,226 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "EventLoopToken", - "RunvarToken", - "RunVar", - "checkpoint", - "checkpoint_if_cancelled", - "cancel_shielded_checkpoint", - "current_token", -) - -import enum -from dataclasses import dataclass -from types import TracebackType -from typing import Any, Generic, Literal, TypeVar, final, overload -from weakref import WeakKeyDictionary - -from ._core._eventloop import get_async_backend -from .abc import AsyncBackend - -T = TypeVar("T") -D = TypeVar("D") - - -async def checkpoint() -> None: - """ - Check for cancellation and allow the scheduler to switch to another task. - - Equivalent to (but more efficient than):: - - await checkpoint_if_cancelled() - await cancel_shielded_checkpoint() - - .. versionadded:: 3.0 - - """ - await get_async_backend().checkpoint() - - -async def checkpoint_if_cancelled() -> None: - """ - Enter a checkpoint if the enclosing cancel scope has been cancelled. - - This does not allow the scheduler to switch to a different task. - - .. versionadded:: 3.0 - - """ - await get_async_backend().checkpoint_if_cancelled() - - -async def cancel_shielded_checkpoint() -> None: - """ - Allow the scheduler to switch to another task but without checking for cancellation. - - Equivalent to (but potentially more efficient than):: - - with CancelScope(shield=True): - await checkpoint() - - .. versionadded:: 3.0 - - """ - await get_async_backend().cancel_shielded_checkpoint() - - -@final -@dataclass(frozen=True, repr=False) -class EventLoopToken: - """ - An opaque object that holds a reference to an event loop. - - .. versionadded:: 4.11.0 - """ - - backend_class: type[AsyncBackend] - native_token: object - - -def current_token() -> EventLoopToken: - """ - Return a token object that can be used to call code in the current event loop from - another thread. - - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - .. versionadded:: 4.11.0 - - """ - backend_class = get_async_backend() - raw_token = backend_class.current_token() - return EventLoopToken(backend_class, raw_token) - - -_run_vars: WeakKeyDictionary[object, dict[RunVar[Any], Any]] = WeakKeyDictionary() - - -class _NoValueSet(enum.Enum): - NO_VALUE_SET = enum.auto() - - -class RunvarToken(Generic[T]): - """ - A token that can be used to restore a :class:`RunVar` to its previous value. - - Returned by :meth:`RunVar.set`. Can be used as a context manager to automatically - reset the variable on exit, or passed directly to :meth:`RunVar.reset`. - """ - - __slots__ = "_var", "_value", "_redeemed" - - def __init__(self, var: RunVar[T], value: T | Literal[_NoValueSet.NO_VALUE_SET]): - self._var = var - self._value: T | Literal[_NoValueSet.NO_VALUE_SET] = value - self._redeemed = False - - def __enter__(self) -> RunvarToken[T]: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self._var.reset(self) - - -class RunVar(Generic[T]): - """ - Like a :class:`~contextvars.ContextVar`, except scoped to the running event loop. - - Can be used as a context manager, Just like :class:`~contextvars.ContextVar`, that - will reset the variable to its previous value when the context block is exited. - """ - - __slots__ = "_name", "_default" - - NO_VALUE_SET: Literal[_NoValueSet.NO_VALUE_SET] = _NoValueSet.NO_VALUE_SET - - def __init__( - self, name: str, default: T | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET - ): - self._name = name - self._default = default - - @property - def _current_vars(self) -> dict[RunVar[T], T]: - native_token = current_token().native_token - try: - return _run_vars[native_token] - except KeyError: - run_vars = _run_vars[native_token] = {} - return run_vars - - @overload - def get(self, default: D) -> T | D: ... - - @overload - def get(self) -> T: ... - - def get( - self, default: D | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET - ) -> T | D: - """ - Return the current value of this run variable. - - :param default: a fallback value to return if no value has been set - :return: the current value, the provided default, or the variable's own default - :raises LookupError: if no value is set and no default is available - - """ - try: - return self._current_vars[self] - except KeyError: - if default is not RunVar.NO_VALUE_SET: - return default - elif self._default is not RunVar.NO_VALUE_SET: - return self._default - - raise LookupError( - f'Run variable "{self._name}" has no value and no default set' - ) - - def set(self, value: T) -> RunvarToken[T]: - """ - Set the value of this run variable for the current event loop. - - :param value: the new value - :return: a token that can be used to restore the previous value - - """ - current_vars = self._current_vars - token = RunvarToken(self, current_vars.get(self, RunVar.NO_VALUE_SET)) - current_vars[self] = value - return token - - def reset(self, token: RunvarToken[T]) -> None: - """ - Restore this run variable to the value it held before the matching :meth:`set`. - - :param token: the token returned by :meth:`set` - :raises ValueError: if the token belongs to a different :class:`RunVar` or the token - has already been used - - """ - if token._var is not self: - raise ValueError("This token does not belong to this RunVar") - - if token._redeemed: - raise ValueError("This token has already been used") - - if token._value is _NoValueSet.NO_VALUE_SET: - try: - del self._current_vars[self] - except KeyError: - pass - else: - self._current_vars[self] = token._value - - token._redeemed = True - - def __repr__(self) -> str: - return f"" diff --git a/.venv/lib/python3.12/site-packages/anyio/py.typed b/.venv/lib/python3.12/site-packages/anyio/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/anyio/pytest_plugin.py b/.venv/lib/python3.12/site-packages/anyio/pytest_plugin.py deleted file mode 100644 index 5c667597..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/pytest_plugin.py +++ /dev/null @@ -1,375 +0,0 @@ -from __future__ import annotations - -import dataclasses -import socket -import sys -from collections.abc import Callable, Generator, Iterator -from contextlib import ExitStack, contextmanager -from inspect import isasyncgenfunction, iscoroutinefunction, ismethod -from typing import Any, cast - -import pytest -from _pytest.fixtures import FuncFixtureInfo, SubRequest -from _pytest.outcomes import Exit -from _pytest.python import CallSpec2 -from _pytest.scope import Scope - -from . import get_available_backends -from ._core._eventloop import ( - current_async_library, - get_async_backend, - reset_current_async_library, - set_current_async_library, -) -from ._core._exceptions import iterate_exceptions -from .abc import TestRunner - -if sys.version_info < (3, 11): - from exceptiongroup import ExceptionGroup - -_current_runner: TestRunner | None = None -_runner_stack: ExitStack | None = None -_runner_leases = 0 - - -def extract_backend_and_options(backend: object) -> tuple[str, dict[str, Any]]: - if isinstance(backend, str): - return backend, {} - elif isinstance(backend, tuple) and len(backend) == 2: - if isinstance(backend[0], str) and isinstance(backend[1], dict): - return cast(tuple[str, dict[str, Any]], backend) - - raise TypeError("anyio_backend must be either a string or tuple of (string, dict)") - - -@contextmanager -def get_runner( - backend_name: str, backend_options: dict[str, Any] -) -> Iterator[TestRunner]: - global _current_runner, _runner_leases, _runner_stack - if _current_runner is None: - asynclib = get_async_backend(backend_name) - _runner_stack = ExitStack() - if current_async_library() is None: - # Since we're in control of the event loop, we can cache the name of the - # async library - token = set_current_async_library(backend_name) - _runner_stack.callback(reset_current_async_library, token) - - backend_options = backend_options or {} - _current_runner = _runner_stack.enter_context( - asynclib.create_test_runner(backend_options) - ) - - _runner_leases += 1 - try: - yield _current_runner - finally: - _runner_leases -= 1 - if not _runner_leases: - assert _runner_stack is not None - _runner_stack.close() - _runner_stack = _current_runner = None - - -def pytest_addoption(parser: pytest.Parser) -> None: - parser.addini( - "anyio_mode", - default="strict", - help='AnyIO plugin mode (either "strict" or "auto")', - ) - - -def pytest_configure(config: pytest.Config) -> None: - config.addinivalue_line( - "markers", - "anyio: mark the (coroutine function) test to be run asynchronously via anyio.", - ) - if ( - config.getini("anyio_mode") == "auto" - and config.pluginmanager.has_plugin("asyncio") - and config.getini("asyncio_mode") == "auto" - ): - config.issue_config_time_warning( - pytest.PytestConfigWarning( - "AnyIO auto mode has been enabled together with pytest-asyncio auto " - "mode. This may cause unexpected behavior." - ), - 1, - ) - - -@pytest.hookimpl(hookwrapper=True) -def pytest_fixture_setup(fixturedef: Any, request: Any) -> Generator[Any]: - def wrapper(anyio_backend: Any, request: SubRequest, **kwargs: Any) -> Any: - # Rebind any fixture methods to the request instance - if ( - request.instance - and ismethod(func) - and type(func.__self__) is type(request.instance) - ): - local_func = func.__func__.__get__(request.instance) - else: - local_func = func - - backend_name, backend_options = extract_backend_and_options(anyio_backend) - if has_backend_arg: - kwargs["anyio_backend"] = anyio_backend - - if has_request_arg: - kwargs["request"] = request - - with get_runner(backend_name, backend_options) as runner: - # re-entrant call into the test runner detected. this happens when an async fixture - # is dynamically requested via request.getfixturevalue() from inside a running async - # test or fixture. on asyncio this raises RuntimeError: This event loop is already - # running, on trio the runner deadlocks - the host loop blocks waiting for the - # coroutine to return, but the coroutine is waiting for the host loop. raising here - # prevents the hang and gives a consistent error across backends. - if runner.is_running(): - raise RuntimeError( - "Cannot schedule a coroutine in the test runner while another is already running; " - "likely caused by request.getfixturevalue() on an async fixture." - ) - - if isasyncgenfunction(local_func): - yield from runner.run_asyncgen_fixture(local_func, kwargs) - else: - yield runner.run_fixture(local_func, kwargs) - - # Only apply this to coroutine functions and async generator functions in requests - # that involve the anyio_backend fixture - func = fixturedef.func - if isasyncgenfunction(func) or iscoroutinefunction(func): - if "anyio_backend" in request.fixturenames: - fixturedef.func = wrapper - original_argname = fixturedef.argnames - - if not (has_backend_arg := "anyio_backend" in fixturedef.argnames): - fixturedef.argnames += ("anyio_backend",) - - if not (has_request_arg := "request" in fixturedef.argnames): - fixturedef.argnames += ("request",) - - try: - return (yield) - finally: - fixturedef.func = func - fixturedef.argnames = original_argname - - return (yield) - - -@pytest.hookimpl(tryfirst=True) -def pytest_pycollect_makeitem( - collector: pytest.Module | pytest.Class, name: str, obj: object -) -> None: - if collector.istestfunction(obj, name): - inner_func = obj.hypothesis.inner_test if hasattr(obj, "hypothesis") else obj - if iscoroutinefunction(inner_func): - anyio_auto_mode = collector.config.getini("anyio_mode") == "auto" - marker = collector.get_closest_marker("anyio") - own_markers = getattr(obj, "pytestmark", ()) - if ( - anyio_auto_mode - or marker - or any(marker.name == "anyio" for marker in own_markers) - ): - pytest.mark.usefixtures("anyio_backend")(obj) - - -def pytest_collection_finish(session: pytest.Session) -> None: - for i, item in reversed(list(enumerate(session.items))): - if ( - isinstance(item, pytest.Function) - and iscoroutinefunction(item.function) - and item.get_closest_marker("anyio") is not None - and "anyio_backend" not in item.fixturenames - ): - new_items = [] - try: - cs_fields = {f.name for f in dataclasses.fields(CallSpec2)} - except TypeError: - cs_fields = set() - - for param_index, backend in enumerate(get_available_backends()): - if "_arg2scope" in cs_fields: # pytest >= 8 - callspec = CallSpec2( - params={"anyio_backend": backend}, - indices={"anyio_backend": param_index}, - _arg2scope={"anyio_backend": Scope.Module}, - _idlist=[backend], - marks=[], - ) - else: # pytest 7.x - callspec = CallSpec2( # type: ignore[call-arg] - funcargs={}, - params={"anyio_backend": backend}, - indices={"anyio_backend": param_index}, - arg2scope={"anyio_backend": Scope.Module}, - idlist=[backend], - marks=[], - ) - - fi = item._fixtureinfo - new_names_closure = list(fi.names_closure) - if "anyio_backend" not in new_names_closure: - new_names_closure.append("anyio_backend") - - new_fixtureinfo = FuncFixtureInfo( - argnames=fi.argnames, - initialnames=fi.initialnames, - names_closure=new_names_closure, - name2fixturedefs=fi.name2fixturedefs, - ) - new_item = pytest.Function.from_parent( - item.parent, - name=f"{item.originalname}[{backend}]", - callspec=callspec, - callobj=item.obj, - fixtureinfo=new_fixtureinfo, - keywords=item.keywords, - originalname=item.originalname, - ) - new_items.append(new_item) - - session.items[i : i + 1] = new_items - - -@pytest.hookimpl(tryfirst=True) -def pytest_pyfunc_call(pyfuncitem: Any) -> bool | None: - def run_with_hypothesis(**kwargs: Any) -> None: - with get_runner(backend_name, backend_options) as runner: - runner.run_test(original_func, kwargs) - - backend = pyfuncitem.funcargs.get("anyio_backend") - if backend: - backend_name, backend_options = extract_backend_and_options(backend) - - if hasattr(pyfuncitem.obj, "hypothesis"): - # Wrap the inner test function unless it's already wrapped - original_func = pyfuncitem.obj.hypothesis.inner_test - if original_func.__qualname__ != run_with_hypothesis.__qualname__: - if iscoroutinefunction(original_func): - pyfuncitem.obj.hypothesis.inner_test = run_with_hypothesis - - return None - - if iscoroutinefunction(pyfuncitem.obj): - funcargs = pyfuncitem.funcargs - testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} - with get_runner(backend_name, backend_options) as runner: - try: - runner.run_test(pyfuncitem.obj, testargs) - except ExceptionGroup as excgrp: - for exc in iterate_exceptions(excgrp): - if isinstance(exc, (Exit, KeyboardInterrupt, SystemExit)): - raise exc from excgrp - - raise - - return True - - return None - - -@pytest.fixture(scope="module", params=get_available_backends()) -def anyio_backend(request: Any) -> Any: - return request.param - - -@pytest.fixture -def anyio_backend_name(anyio_backend: Any) -> str: - if isinstance(anyio_backend, str): - return anyio_backend - else: - return anyio_backend[0] - - -@pytest.fixture -def anyio_backend_options(anyio_backend: Any) -> dict[str, Any]: - if isinstance(anyio_backend, str): - return {} - else: - return anyio_backend[1] - - -class FreePortFactory: - """ - Manages port generation based on specified socket kind, ensuring no duplicate - ports are generated. - - This class provides functionality for generating available free ports on the - system. It is initialized with a specific socket kind and can generate ports - for given address families while avoiding reuse of previously generated ports. - - Users should not instantiate this class directly, but use the - ``free_tcp_port_factory`` and ``free_udp_port_factory`` fixtures instead. For simple - uses cases, ``free_tcp_port`` and ``free_udp_port`` can be used instead. - """ - - def __init__(self, kind: socket.SocketKind) -> None: - self._kind = kind - self._generated = set[int]() - - @property - def kind(self) -> socket.SocketKind: - """ - The type of socket connection (e.g., :data:`~socket.SOCK_STREAM` or - :data:`~socket.SOCK_DGRAM`) used to bind for checking port availability - - """ - return self._kind - - def __call__(self, family: socket.AddressFamily | None = None) -> int: - """ - Return an unbound port for the given address family. - - :param family: if omitted, both IPv4 and IPv6 addresses will be tried - :return: a port number - - """ - if family is not None: - families = [family] - else: - families = [socket.AF_INET] - if socket.has_ipv6: - families.append(socket.AF_INET6) - - while True: - port = 0 - with ExitStack() as stack: - for family in families: - sock = stack.enter_context(socket.socket(family, self._kind)) - addr = "::1" if family == socket.AF_INET6 else "127.0.0.1" - try: - sock.bind((addr, port)) - except OSError: - break - - if not port: - port = sock.getsockname()[1] - else: - if port not in self._generated: - self._generated.add(port) - return port - - -@pytest.fixture(scope="session") -def free_tcp_port_factory() -> FreePortFactory: - return FreePortFactory(socket.SOCK_STREAM) - - -@pytest.fixture(scope="session") -def free_udp_port_factory() -> FreePortFactory: - return FreePortFactory(socket.SOCK_DGRAM) - - -@pytest.fixture -def free_tcp_port(free_tcp_port_factory: Callable[[], int]) -> int: - return free_tcp_port_factory() - - -@pytest.fixture -def free_udp_port(free_udp_port_factory: Callable[[], int]) -> int: - return free_udp_port_factory() diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/__init__.py b/.venv/lib/python3.12/site-packages/anyio/streams/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/buffered.py b/.venv/lib/python3.12/site-packages/anyio/streams/buffered.py deleted file mode 100644 index acf312ba..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/streams/buffered.py +++ /dev/null @@ -1,198 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "BufferedByteReceiveStream", - "BufferedByteStream", - "BufferedConnectable", -) - -import sys -from collections.abc import Callable, Iterable, Mapping -from dataclasses import dataclass, field -from typing import Any, SupportsIndex - -from .. import ClosedResourceError, DelimiterNotFound, EndOfStream, IncompleteRead -from ..abc import ( - AnyByteReceiveStream, - AnyByteStream, - AnyByteStreamConnectable, - ByteReceiveStream, - ByteStream, - ByteStreamConnectable, -) - -if sys.version_info >= (3, 12): - from typing import override -else: - from typing_extensions import override - - -@dataclass(eq=False) -class BufferedByteReceiveStream(ByteReceiveStream): - """ - Wraps any bytes-based receive stream and uses a buffer to provide sophisticated - receiving capabilities in the form of a byte stream. - """ - - receive_stream: AnyByteReceiveStream - _buffer: bytearray = field(init=False, default_factory=bytearray) - _closed: bool = field(init=False, default=False) - - async def aclose(self) -> None: - await self.receive_stream.aclose() - self._closed = True - - @property - def buffer(self) -> bytes: - """The bytes currently in the buffer.""" - return bytes(self._buffer) - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return self.receive_stream.extra_attributes - - def feed_data(self, data: Iterable[SupportsIndex], /) -> None: - """ - Append data directly into the buffer. - - Any data in the buffer will be consumed by receive operations before receiving - anything from the wrapped stream. - - :param data: the data to append to the buffer (can be bytes or anything else - that supports ``__index__()``) - - """ - self._buffer.extend(data) - - async def receive(self, max_bytes: int = 65536) -> bytes: - if self._closed: - raise ClosedResourceError - - if self._buffer: - chunk = bytes(self._buffer[:max_bytes]) - del self._buffer[:max_bytes] - return chunk - elif isinstance(self.receive_stream, ByteReceiveStream): - return await self.receive_stream.receive(max_bytes) - else: - # With a bytes-oriented object stream, we need to handle any surplus bytes - # we get from the receive() call - chunk = await self.receive_stream.receive() - if len(chunk) > max_bytes: - # Save the surplus bytes in the buffer - self._buffer.extend(chunk[max_bytes:]) - return chunk[:max_bytes] - else: - return chunk - - async def receive_exactly(self, nbytes: int) -> bytes: - """ - Read exactly the given amount of bytes from the stream. - - :param nbytes: the number of bytes to read - :return: the bytes read - :raises ~anyio.IncompleteRead: if the stream was closed before the requested - amount of bytes could be read from the stream - - """ - while True: - remaining = nbytes - len(self._buffer) - if remaining <= 0: - retval = self._buffer[:nbytes] - del self._buffer[:nbytes] - return bytes(retval) - - try: - if isinstance(self.receive_stream, ByteReceiveStream): - chunk = await self.receive_stream.receive(remaining) - else: - chunk = await self.receive_stream.receive() - except EndOfStream as exc: - raise IncompleteRead from exc - - self._buffer.extend(chunk) - - async def receive_until(self, delimiter: bytes, max_bytes: int) -> bytes: - """ - Read from the stream until the delimiter is found or max_bytes have been read. - - :param delimiter: the marker to look for in the stream - :param max_bytes: maximum number of bytes that will be read before raising - :exc:`~anyio.DelimiterNotFound` - :return: the bytes read (not including the delimiter) - :raises ~anyio.IncompleteRead: if the stream was closed before the delimiter - was found - :raises ~anyio.DelimiterNotFound: if the delimiter is not found within the - bytes read up to the maximum allowed - - """ - delimiter_size = len(delimiter) - offset = 0 - while True: - # Check if the delimiter can be found in the current buffer - index = self._buffer.find(delimiter, offset) - if index >= 0: - found = self._buffer[:index] - del self._buffer[: index + len(delimiter) :] - return bytes(found) - - # Check if the buffer is already at or over the limit - if len(self._buffer) >= max_bytes: - raise DelimiterNotFound(max_bytes) - - # Read more data into the buffer from the socket - try: - data = await self.receive_stream.receive() - except EndOfStream as exc: - raise IncompleteRead from exc - - # Move the offset forward and add the new data to the buffer - offset = max(len(self._buffer) - delimiter_size + 1, 0) - self._buffer.extend(data) - - -class BufferedByteStream(BufferedByteReceiveStream, ByteStream): - """ - A full-duplex variant of :class:`BufferedByteReceiveStream`. All writes are passed - through to the wrapped stream as-is. - """ - - def __init__(self, stream: AnyByteStream): - """ - :param stream: the stream to be wrapped - - """ - super().__init__(stream) - self._stream = stream - - @override - async def send_eof(self) -> None: - await self._stream.send_eof() - - @override - async def send(self, item: bytes) -> None: - await self._stream.send(item) - - -class BufferedConnectable(ByteStreamConnectable): - """ - Wraps a byte stream connectable to produce :class:`BufferedByteStream` connections. - - Use this when you want the streams returned by :meth:`connect` to have the buffered - receive API (e.g. :meth:`~BufferedByteReceiveStream.receive_exactly` and - :meth:`~BufferedByteReceiveStream.receive_until`). - - :param connectable: the byte stream connectable to wrap - """ - - def __init__(self, connectable: AnyByteStreamConnectable): - """ - :param connectable: the connectable to wrap - - """ - self.connectable = connectable - - @override - async def connect(self) -> BufferedByteStream: - stream = await self.connectable.connect() - return BufferedByteStream(stream) diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/file.py b/.venv/lib/python3.12/site-packages/anyio/streams/file.py deleted file mode 100644 index 79c3d500..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/streams/file.py +++ /dev/null @@ -1,154 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "FileReadStream", - "FileStreamAttribute", - "FileWriteStream", -) - -from collections.abc import Callable, Mapping -from io import SEEK_SET, UnsupportedOperation -from os import PathLike -from pathlib import Path -from typing import IO, Any - -from .. import ( - BrokenResourceError, - ClosedResourceError, - EndOfStream, - TypedAttributeSet, - to_thread, - typed_attribute, -) -from ..abc import ByteReceiveStream, ByteSendStream - - -class FileStreamAttribute(TypedAttributeSet): - #: the open file descriptor - file: IO[bytes] = typed_attribute() - #: the path of the file on the file system, if available (file must be a real file) - path: Path = typed_attribute() - #: the file number, if available (file must be a real file or a TTY) - fileno: int = typed_attribute() - - -class _BaseFileStream: - def __init__(self, file: IO[bytes]): - self._file = file - - async def aclose(self) -> None: - await to_thread.run_sync(self._file.close) - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - attributes: dict[Any, Callable[[], Any]] = { - FileStreamAttribute.file: lambda: self._file, - } - - if hasattr(self._file, "name"): - attributes[FileStreamAttribute.path] = lambda: Path(self._file.name) - - try: - self._file.fileno() - except UnsupportedOperation: - pass - else: - attributes[FileStreamAttribute.fileno] = lambda: self._file.fileno() - - return attributes - - -class FileReadStream(_BaseFileStream, ByteReceiveStream): - """ - A byte stream that reads from a file in the file system. - - :param file: a file that has been opened for reading in binary mode - - .. versionadded:: 3.0 - """ - - @classmethod - async def from_path(cls, path: str | PathLike[str]) -> FileReadStream: - """ - Create a file read stream by opening the given file. - - :param path: path of the file to read from - - """ - file = await to_thread.run_sync(Path(path).open, "rb") - return cls(file) - - async def receive(self, max_bytes: int = 65536) -> bytes: - try: - data = await to_thread.run_sync(self._file.read, max_bytes) - except ValueError: - raise ClosedResourceError from None - except OSError as exc: - raise BrokenResourceError from exc - - if data: - return data - else: - raise EndOfStream - - async def seek(self, position: int, whence: int = SEEK_SET) -> int: - """ - Seek the file to the given position. - - .. seealso:: :meth:`io.IOBase.seek` - - .. note:: Not all file descriptors are seekable. - - :param position: position to seek the file to - :param whence: controls how ``position`` is interpreted - :return: the new absolute position - :raises OSError: if the file is not seekable - - """ - return await to_thread.run_sync(self._file.seek, position, whence) - - async def tell(self) -> int: - """ - Return the current stream position. - - .. note:: Not all file descriptors are seekable. - - :return: the current absolute position - :raises OSError: if the file is not seekable - - """ - return await to_thread.run_sync(self._file.tell) - - -class FileWriteStream(_BaseFileStream, ByteSendStream): - """ - A byte stream that writes to a file in the file system. - - :param file: a file that has been opened for writing in binary mode - - .. versionadded:: 3.0 - """ - - @classmethod - async def from_path( - cls, path: str | PathLike[str], append: bool = False - ) -> FileWriteStream: - """ - Create a file write stream by opening the given file for writing. - - :param path: path of the file to write to - :param append: if ``True``, open the file for appending; if ``False``, any - existing file at the given path will be truncated - - """ - mode = "ab" if append else "wb" - file = await to_thread.run_sync(Path(path).open, mode) - return cls(file) - - async def send(self, item: bytes) -> None: - try: - await to_thread.run_sync(self._file.write, item) - except ValueError: - raise ClosedResourceError from None - except OSError as exc: - raise BrokenResourceError from exc diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/memory.py b/.venv/lib/python3.12/site-packages/anyio/streams/memory.py deleted file mode 100644 index a3fa0c3d..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/streams/memory.py +++ /dev/null @@ -1,325 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "MemoryObjectReceiveStream", - "MemoryObjectSendStream", - "MemoryObjectStreamStatistics", -) - -import warnings -from collections import OrderedDict, deque -from dataclasses import dataclass, field -from types import TracebackType -from typing import Generic, NamedTuple, TypeVar - -from .. import ( - BrokenResourceError, - ClosedResourceError, - EndOfStream, - WouldBlock, -) -from .._core._testing import TaskInfo, get_current_task -from ..abc import Event, ObjectReceiveStream, ObjectSendStream -from ..lowlevel import checkpoint - -T_Item = TypeVar("T_Item") -T_co = TypeVar("T_co", covariant=True) -T_contra = TypeVar("T_contra", contravariant=True) - - -class MemoryObjectStreamStatistics(NamedTuple): - current_buffer_used: int #: number of items stored in the buffer - #: maximum number of items that can be stored on this stream (or :data:`math.inf`) - max_buffer_size: float - open_send_streams: int #: number of unclosed clones of the send stream - open_receive_streams: int #: number of unclosed clones of the receive stream - #: number of tasks blocked on :meth:`MemoryObjectSendStream.send` - tasks_waiting_send: int - #: number of tasks blocked on :meth:`MemoryObjectReceiveStream.receive` - tasks_waiting_receive: int - - -@dataclass(eq=False) -class _MemoryObjectItemReceiver(Generic[T_Item]): - task_info: TaskInfo = field(init=False, default_factory=get_current_task) - item: T_Item = field(init=False) - - def __repr__(self) -> str: - # When item is not defined, we get following error with default __repr__: - # AttributeError: 'MemoryObjectItemReceiver' object has no attribute 'item' - item = getattr(self, "item", None) - return f"{self.__class__.__name__}(task_info={self.task_info}, item={item!r})" - - -@dataclass(eq=False) -class _MemoryObjectStreamState(Generic[T_Item]): - max_buffer_size: float = field() - buffer: deque[T_Item] = field(init=False, default_factory=deque) - open_send_channels: int = field(init=False, default=0) - open_receive_channels: int = field(init=False, default=0) - waiting_receivers: OrderedDict[Event, _MemoryObjectItemReceiver[T_Item]] = field( - init=False, default_factory=OrderedDict - ) - waiting_senders: OrderedDict[Event, T_Item] = field( - init=False, default_factory=OrderedDict - ) - - def statistics(self) -> MemoryObjectStreamStatistics: - return MemoryObjectStreamStatistics( - len(self.buffer), - self.max_buffer_size, - self.open_send_channels, - self.open_receive_channels, - len(self.waiting_senders), - len(self.waiting_receivers), - ) - - -@dataclass(eq=False) -class MemoryObjectReceiveStream(Generic[T_co], ObjectReceiveStream[T_co]): - _state: _MemoryObjectStreamState[T_co] - _closed: bool = field(init=False, default=False) - - def __post_init__(self) -> None: - self._state.open_receive_channels += 1 - - def receive_nowait(self) -> T_co: - """ - Receive the next item if it can be done without waiting. - - :return: the received item - :raises ~anyio.ClosedResourceError: if this send stream has been closed - :raises ~anyio.EndOfStream: if the buffer is empty and this stream has been - closed from the sending end - :raises ~anyio.WouldBlock: if there are no items in the buffer and no tasks - waiting to send - - """ - if self._closed: - raise ClosedResourceError - - if self._state.waiting_senders: - # Get the item from the next sender - send_event, item = self._state.waiting_senders.popitem(last=False) - self._state.buffer.append(item) - send_event.set() - - if self._state.buffer: - return self._state.buffer.popleft() - elif not self._state.open_send_channels: - raise EndOfStream - - raise WouldBlock - - async def receive(self) -> T_co: - await checkpoint() - try: - return self.receive_nowait() - except WouldBlock: - # Add ourselves in the queue - receive_event = Event() - receiver = _MemoryObjectItemReceiver[T_co]() - self._state.waiting_receivers[receive_event] = receiver - - try: - await receive_event.wait() - finally: - self._state.waiting_receivers.pop(receive_event, None) - - try: - return receiver.item - except AttributeError: - raise EndOfStream from None - - def clone(self) -> MemoryObjectReceiveStream[T_co]: - """ - Create a clone of this receive stream. - - Each clone can be closed separately. Only when all clones have been closed will - the receiving end of the memory stream be considered closed by the sending ends. - - :return: the cloned stream - - """ - if self._closed: - raise ClosedResourceError - - return MemoryObjectReceiveStream(_state=self._state) - - def close(self) -> None: - """ - Close the stream. - - This works the exact same way as :meth:`aclose`, but is provided as a special - case for the benefit of synchronous callbacks. - - """ - if not self._closed: - self._closed = True - self._state.open_receive_channels -= 1 - if self._state.open_receive_channels == 0: - send_events = list(self._state.waiting_senders.keys()) - for event in send_events: - event.set() - - async def aclose(self) -> None: - self.close() - - def statistics(self) -> MemoryObjectStreamStatistics: - """ - Return statistics about the current state of this stream. - - .. versionadded:: 3.0 - """ - return self._state.statistics() - - def __enter__(self) -> MemoryObjectReceiveStream[T_co]: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.close() - - def __del__(self) -> None: - if not self._closed: - warnings.warn( - f"Unclosed <{self.__class__.__name__} at {id(self):x}>", - ResourceWarning, - stacklevel=1, - source=self, - ) - - -@dataclass(eq=False) -class MemoryObjectSendStream(Generic[T_contra], ObjectSendStream[T_contra]): - _state: _MemoryObjectStreamState[T_contra] - _closed: bool = field(init=False, default=False) - - def __post_init__(self) -> None: - self._state.open_send_channels += 1 - - def send_nowait(self, item: T_contra) -> None: - """ - Send an item immediately if it can be done without waiting. - - :param item: the item to send - :raises ~anyio.ClosedResourceError: if this send stream has been closed - :raises ~anyio.BrokenResourceError: if the stream has been closed from the - receiving end - :raises ~anyio.WouldBlock: if the buffer is full and there are no tasks waiting - to receive - - """ - if self._closed: - raise ClosedResourceError - if not self._state.open_receive_channels: - raise BrokenResourceError - - while self._state.waiting_receivers: - receive_event, receiver = self._state.waiting_receivers.popitem(last=False) - if not receiver.task_info.has_pending_cancellation(): - receiver.item = item - receive_event.set() - return - - if len(self._state.buffer) < self._state.max_buffer_size: - self._state.buffer.append(item) - else: - raise WouldBlock - - async def send(self, item: T_contra) -> None: - """ - Send an item to the stream. - - If the buffer is full, this method blocks until there is again room in the - buffer or the item can be sent directly to a receiver. - - :param item: the item to send - :raises ~anyio.ClosedResourceError: if this send stream has been closed - :raises ~anyio.BrokenResourceError: if the stream has been closed from the - receiving end - - """ - await checkpoint() - try: - self.send_nowait(item) - except WouldBlock: - # Wait until there's someone on the receiving end - send_event = Event() - self._state.waiting_senders[send_event] = item - try: - await send_event.wait() - except BaseException: - self._state.waiting_senders.pop(send_event, None) - raise - - if send_event in self._state.waiting_senders: - del self._state.waiting_senders[send_event] - raise BrokenResourceError from None - - def clone(self) -> MemoryObjectSendStream[T_contra]: - """ - Create a clone of this send stream. - - Each clone can be closed separately. Only when all clones have been closed will - the sending end of the memory stream be considered closed by the receiving ends. - - :return: the cloned stream - - """ - if self._closed: - raise ClosedResourceError - - return MemoryObjectSendStream(_state=self._state) - - def close(self) -> None: - """ - Close the stream. - - This works the exact same way as :meth:`aclose`, but is provided as a special - case for the benefit of synchronous callbacks. - - """ - if not self._closed: - self._closed = True - self._state.open_send_channels -= 1 - if self._state.open_send_channels == 0: - receive_events = list(self._state.waiting_receivers.keys()) - self._state.waiting_receivers.clear() - for event in receive_events: - event.set() - - async def aclose(self) -> None: - self.close() - - def statistics(self) -> MemoryObjectStreamStatistics: - """ - Return statistics about the current state of this stream. - - .. versionadded:: 3.0 - """ - return self._state.statistics() - - def __enter__(self) -> MemoryObjectSendStream[T_contra]: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.close() - - def __del__(self) -> None: - if not self._closed: - warnings.warn( - f"Unclosed <{self.__class__.__name__} at {id(self):x}>", - ResourceWarning, - stacklevel=1, - source=self, - ) diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/stapled.py b/.venv/lib/python3.12/site-packages/anyio/streams/stapled.py deleted file mode 100644 index 9248b68a..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/streams/stapled.py +++ /dev/null @@ -1,147 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "MultiListener", - "StapledByteStream", - "StapledObjectStream", -) - -from collections.abc import Callable, Mapping, Sequence -from dataclasses import dataclass -from typing import Any, Generic, TypeVar - -from ..abc import ( - ByteReceiveStream, - ByteSendStream, - ByteStream, - Listener, - ObjectReceiveStream, - ObjectSendStream, - ObjectStream, - TaskGroup, -) - -T_Item = TypeVar("T_Item") -T_Stream = TypeVar("T_Stream") - - -@dataclass(eq=False) -class StapledByteStream(ByteStream): - """ - Combines two byte streams into a single, bidirectional byte stream. - - Extra attributes will be provided from both streams, with the receive stream - providing the values in case of a conflict. - - :param ByteSendStream send_stream: the sending byte stream - :param ByteReceiveStream receive_stream: the receiving byte stream - """ - - send_stream: ByteSendStream - receive_stream: ByteReceiveStream - - async def receive(self, max_bytes: int = 65536) -> bytes: - return await self.receive_stream.receive(max_bytes) - - async def send(self, item: bytes) -> None: - await self.send_stream.send(item) - - async def send_eof(self) -> None: - await self.send_stream.aclose() - - async def aclose(self) -> None: - await self.send_stream.aclose() - await self.receive_stream.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return { - **self.send_stream.extra_attributes, - **self.receive_stream.extra_attributes, - } - - -@dataclass(eq=False) -class StapledObjectStream(Generic[T_Item], ObjectStream[T_Item]): - """ - Combines two object streams into a single, bidirectional object stream. - - Extra attributes will be provided from both streams, with the receive stream - providing the values in case of a conflict. - - :param ObjectSendStream send_stream: the sending object stream - :param ObjectReceiveStream receive_stream: the receiving object stream - """ - - send_stream: ObjectSendStream[T_Item] - receive_stream: ObjectReceiveStream[T_Item] - - async def receive(self) -> T_Item: - return await self.receive_stream.receive() - - async def send(self, item: T_Item) -> None: - await self.send_stream.send(item) - - async def send_eof(self) -> None: - await self.send_stream.aclose() - - async def aclose(self) -> None: - await self.send_stream.aclose() - await self.receive_stream.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return { - **self.send_stream.extra_attributes, - **self.receive_stream.extra_attributes, - } - - -@dataclass(eq=False) -class MultiListener(Generic[T_Stream], Listener[T_Stream]): - """ - Combines multiple listeners into one, serving connections from all of them at once. - - Any MultiListeners in the given collection of listeners will have their listeners - moved into this one. - - Extra attributes are provided from each listener, with each successive listener - overriding any conflicting attributes from the previous one. - - :param listeners: listeners to serve - :type listeners: Sequence[Listener[T_Stream]] - """ - - listeners: Sequence[Listener[T_Stream]] - - def __post_init__(self) -> None: - listeners: list[Listener[T_Stream]] = [] - for listener in self.listeners: - if isinstance(listener, MultiListener): - listeners.extend(listener.listeners) - del listener.listeners[:] # type: ignore[attr-defined] - else: - listeners.append(listener) - - self.listeners = listeners - - async def serve( - self, handler: Callable[[T_Stream], Any], task_group: TaskGroup | None = None - ) -> None: - from .. import create_task_group - - async with create_task_group() as tg: - for listener in self.listeners: - tg.start_soon(listener.serve, handler, task_group) - - async def aclose(self) -> None: - for listener in self.listeners: - await listener.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - attributes: dict = {} - for listener in self.listeners: - attributes.update(listener.extra_attributes) - - return attributes diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/text.py b/.venv/lib/python3.12/site-packages/anyio/streams/text.py deleted file mode 100644 index 296cd250..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/streams/text.py +++ /dev/null @@ -1,176 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "TextConnectable", - "TextReceiveStream", - "TextSendStream", - "TextStream", -) - -import codecs -import sys -from collections.abc import Callable, Mapping -from dataclasses import InitVar, dataclass, field -from typing import Any - -from ..abc import ( - AnyByteReceiveStream, - AnyByteSendStream, - AnyByteStream, - AnyByteStreamConnectable, - ObjectReceiveStream, - ObjectSendStream, - ObjectStream, - ObjectStreamConnectable, -) - -if sys.version_info >= (3, 12): - from typing import override -else: - from typing_extensions import override - - -@dataclass(eq=False) -class TextReceiveStream(ObjectReceiveStream[str]): - """ - Stream wrapper that decodes bytes to strings using the given encoding. - - Decoding is done using :class:`~codecs.IncrementalDecoder` which returns any - completely received unicode characters as soon as they come in. - - :param transport_stream: any bytes-based receive stream - :param encoding: character encoding to use for decoding bytes to strings (defaults - to ``utf-8``) - :param errors: handling scheme for decoding errors (defaults to ``strict``; see the - `codecs module documentation`_ for a comprehensive list of options) - - .. _codecs module documentation: - https://docs.python.org/3/library/codecs.html#codec-objects - """ - - transport_stream: AnyByteReceiveStream - encoding: InitVar[str] = "utf-8" - errors: InitVar[str] = "strict" - _decoder: codecs.IncrementalDecoder = field(init=False) - - def __post_init__(self, encoding: str, errors: str) -> None: - decoder_class = codecs.getincrementaldecoder(encoding) - self._decoder = decoder_class(errors=errors) - - async def receive(self) -> str: - while True: - chunk = await self.transport_stream.receive() - decoded = self._decoder.decode(chunk) - if decoded: - return decoded - - async def aclose(self) -> None: - await self.transport_stream.aclose() - self._decoder.reset() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return self.transport_stream.extra_attributes - - -@dataclass(eq=False) -class TextSendStream(ObjectSendStream[str]): - """ - Sends strings to the wrapped stream as bytes using the given encoding. - - :param AnyByteSendStream transport_stream: any bytes-based send stream - :param str encoding: character encoding to use for encoding strings to bytes - (defaults to ``utf-8``) - :param str errors: handling scheme for encoding errors (defaults to ``strict``; see - the `codecs module documentation`_ for a comprehensive list of options) - - .. _codecs module documentation: - https://docs.python.org/3/library/codecs.html#codec-objects - """ - - transport_stream: AnyByteSendStream - encoding: InitVar[str] = "utf-8" - errors: str = "strict" - _encoder: Callable[..., tuple[bytes, int]] = field(init=False) - - def __post_init__(self, encoding: str) -> None: - self._encoder = codecs.getencoder(encoding) - - async def send(self, item: str) -> None: - encoded = self._encoder(item, self.errors)[0] - await self.transport_stream.send(encoded) - - async def aclose(self) -> None: - await self.transport_stream.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return self.transport_stream.extra_attributes - - -@dataclass(eq=False) -class TextStream(ObjectStream[str]): - """ - A bidirectional stream that decodes bytes to strings on receive and encodes strings - to bytes on send. - - Extra attributes will be provided from both streams, with the receive stream - providing the values in case of a conflict. - - :param AnyByteStream transport_stream: any bytes-based stream - :param str encoding: character encoding to use for encoding/decoding strings to/from - bytes (defaults to ``utf-8``) - :param str errors: handling scheme for encoding errors (defaults to ``strict``; see - the `codecs module documentation`_ for a comprehensive list of options) - - .. _codecs module documentation: - https://docs.python.org/3/library/codecs.html#codec-objects - """ - - transport_stream: AnyByteStream - encoding: InitVar[str] = "utf-8" - errors: InitVar[str] = "strict" - _receive_stream: TextReceiveStream = field(init=False) - _send_stream: TextSendStream = field(init=False) - - def __post_init__(self, encoding: str, errors: str) -> None: - self._receive_stream = TextReceiveStream( - self.transport_stream, encoding=encoding, errors=errors - ) - self._send_stream = TextSendStream( - self.transport_stream, encoding=encoding, errors=errors - ) - - async def receive(self) -> str: - return await self._receive_stream.receive() - - async def send(self, item: str) -> None: - await self._send_stream.send(item) - - async def send_eof(self) -> None: - await self.transport_stream.send_eof() - - async def aclose(self) -> None: - await self._send_stream.aclose() - await self._receive_stream.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return { - **self._send_stream.extra_attributes, - **self._receive_stream.extra_attributes, - } - - -class TextConnectable(ObjectStreamConnectable[str]): - def __init__(self, connectable: AnyByteStreamConnectable): - """ - :param connectable: the bytestream endpoint to wrap - - """ - self.connectable = connectable - - @override - async def connect(self) -> TextStream: - stream = await self.connectable.connect() - return TextStream(stream) diff --git a/.venv/lib/python3.12/site-packages/anyio/streams/tls.py b/.venv/lib/python3.12/site-packages/anyio/streams/tls.py deleted file mode 100644 index e2a7ca5b..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/streams/tls.py +++ /dev/null @@ -1,421 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "TLSAttribute", - "TLSConnectable", - "TLSListener", - "TLSStream", -) - -import logging -import re -import ssl -import sys -from collections.abc import Callable, Mapping -from dataclasses import dataclass -from functools import wraps -from ssl import SSLContext -from typing import Any, TypeAlias, TypeVar - -from .. import ( - BrokenResourceError, - EndOfStream, - aclose_forcefully, - get_cancelled_exc_class, - to_thread, -) -from .._core._typedattr import TypedAttributeSet, typed_attribute -from ..abc import ( - AnyByteStream, - AnyByteStreamConnectable, - ByteStream, - ByteStreamConnectable, - Listener, - TaskGroup, -) - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -if sys.version_info >= (3, 12): - from typing import override -else: - from typing_extensions import override - -T_Retval = TypeVar("T_Retval") -PosArgsT = TypeVarTuple("PosArgsT") -_PCTRTT: TypeAlias = tuple[tuple[str, str], ...] -_PCTRTTT: TypeAlias = tuple[_PCTRTT, ...] - - -class TLSAttribute(TypedAttributeSet): - """Contains Transport Layer Security related attributes.""" - - #: the selected ALPN protocol - alpn_protocol: str | None = typed_attribute() - #: the channel binding for type ``tls-unique`` - channel_binding_tls_unique: bytes = typed_attribute() - #: the selected cipher - cipher: tuple[str, str, int] = typed_attribute() - #: the peer certificate in dictionary form (see :meth:`ssl.SSLSocket.getpeercert` - # for more information) - peer_certificate: None | (dict[str, str | _PCTRTTT | _PCTRTT]) = typed_attribute() - #: the peer certificate in binary form - peer_certificate_binary: bytes | None = typed_attribute() - #: ``True`` if this is the server side of the connection - server_side: bool = typed_attribute() - #: ciphers shared by the client during the TLS handshake (``None`` if this is the - #: client side) - shared_ciphers: list[tuple[str, str, int]] | None = typed_attribute() - #: the :class:`~ssl.SSLObject` used for encryption - ssl_object: ssl.SSLObject = typed_attribute() - #: ``True`` if this stream does (and expects) a closing TLS handshake when the - #: stream is being closed - standard_compatible: bool = typed_attribute() - #: the TLS protocol version (e.g. ``TLSv1.2``) - tls_version: str = typed_attribute() - - -@dataclass(eq=False) -class TLSStream(ByteStream): - """ - A stream wrapper that encrypts all sent data and decrypts received data. - - This class has no public initializer; use :meth:`wrap` instead. - All extra attributes from :class:`~TLSAttribute` are supported. - - :var AnyByteStream transport_stream: the wrapped stream - - """ - - transport_stream: AnyByteStream - standard_compatible: bool - _ssl_object: ssl.SSLObject - _read_bio: ssl.MemoryBIO - _write_bio: ssl.MemoryBIO - - @classmethod - async def wrap( - cls, - transport_stream: AnyByteStream, - *, - server_side: bool | None = None, - hostname: str | None = None, - ssl_context: ssl.SSLContext | None = None, - standard_compatible: bool = True, - ) -> TLSStream: - """ - Wrap an existing stream with Transport Layer Security. - - This performs a TLS handshake with the peer. - - :param transport_stream: a bytes-transporting stream to wrap - :param server_side: ``True`` if this is the server side of the connection, - ``False`` if this is the client side (if omitted, will be set to ``False`` - if ``hostname`` has been provided, ``False`` otherwise). Used only to create - a default context when an explicit context has not been provided. - :param hostname: host name of the peer (if host name checking is desired) - :param ssl_context: the SSLContext object to use (if not provided, a secure - default will be created) - :param standard_compatible: if ``False``, skip the closing handshake when - closing the connection, and don't raise an exception if the peer does the - same - :raises ~ssl.SSLError: if the TLS handshake fails - - """ - if server_side is None: - server_side = not hostname - - if not ssl_context: - purpose = ( - ssl.Purpose.CLIENT_AUTH if server_side else ssl.Purpose.SERVER_AUTH - ) - ssl_context = ssl.create_default_context(purpose) - - # Re-enable detection of unexpected EOFs if it was disabled by Python - if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"): - ssl_context.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF - - bio_in = ssl.MemoryBIO() - bio_out = ssl.MemoryBIO() - - # External SSLContext implementations may do blocking I/O in wrap_bio(), - # but the standard library implementation won't - if type(ssl_context) is ssl.SSLContext: - ssl_object = ssl_context.wrap_bio( - bio_in, bio_out, server_side=server_side, server_hostname=hostname - ) - else: - ssl_object = await to_thread.run_sync( - ssl_context.wrap_bio, - bio_in, - bio_out, - server_side, - hostname, - None, - ) - - wrapper = cls( - transport_stream=transport_stream, - standard_compatible=standard_compatible, - _ssl_object=ssl_object, - _read_bio=bio_in, - _write_bio=bio_out, - ) - await wrapper._call_sslobject_method(ssl_object.do_handshake) - return wrapper - - async def _call_sslobject_method( - self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] - ) -> T_Retval: - while True: - try: - result = func(*args) - except ssl.SSLWantReadError: - try: - # Flush any pending writes first - if self._write_bio.pending: - await self.transport_stream.send(self._write_bio.read()) - - data = await self.transport_stream.receive() - except EndOfStream: - self._read_bio.write_eof() - except OSError as exc: - self._read_bio.write_eof() - self._write_bio.write_eof() - raise BrokenResourceError from exc - else: - self._read_bio.write(data) - except ssl.SSLWantWriteError: - await self.transport_stream.send(self._write_bio.read()) - except ssl.SSLSyscallError as exc: - self._read_bio.write_eof() - self._write_bio.write_eof() - raise BrokenResourceError from exc - except ssl.SSLError as exc: - self._read_bio.write_eof() - self._write_bio.write_eof() - if isinstance(exc, ssl.SSLEOFError) or ( - exc.strerror and "UNEXPECTED_EOF_WHILE_READING" in exc.strerror - ): - if self.standard_compatible: - raise BrokenResourceError from exc - else: - raise EndOfStream from None - - raise - else: - # Flush any pending writes first - if self._write_bio.pending: - await self.transport_stream.send(self._write_bio.read()) - - return result - - async def unwrap(self) -> tuple[AnyByteStream, bytes]: - """ - Does the TLS closing handshake. - - :return: a tuple of (wrapped byte stream, bytes left in the read buffer) - - """ - await self._call_sslobject_method(self._ssl_object.unwrap) - self._read_bio.write_eof() - self._write_bio.write_eof() - return self.transport_stream, self._read_bio.read() - - async def aclose(self) -> None: - if self.standard_compatible: - try: - await self.unwrap() - except BaseException: - await aclose_forcefully(self.transport_stream) - raise - - await self.transport_stream.aclose() - - async def receive(self, max_bytes: int = 65536) -> bytes: - data = await self._call_sslobject_method(self._ssl_object.read, max_bytes) - if not data: - raise EndOfStream - - return data - - async def send(self, item: bytes) -> None: - await self._call_sslobject_method(self._ssl_object.write, item) - - async def send_eof(self) -> None: - tls_version = self.extra(TLSAttribute.tls_version) - match = re.match(r"TLSv(\d+)(?:\.(\d+))?", tls_version) - if match: - major, minor = int(match.group(1)), int(match.group(2) or 0) - if (major, minor) < (1, 3): - raise NotImplementedError( - f"send_eof() requires at least TLSv1.3; current " - f"session uses {tls_version}" - ) - - raise NotImplementedError( - "send_eof() has not yet been implemented for TLS streams" - ) - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return { - **self.transport_stream.extra_attributes, - TLSAttribute.alpn_protocol: self._ssl_object.selected_alpn_protocol, - TLSAttribute.channel_binding_tls_unique: ( - self._ssl_object.get_channel_binding - ), - TLSAttribute.cipher: self._ssl_object.cipher, - TLSAttribute.peer_certificate: lambda: self._ssl_object.getpeercert(False), - TLSAttribute.peer_certificate_binary: lambda: self._ssl_object.getpeercert( - True - ), - TLSAttribute.server_side: lambda: self._ssl_object.server_side, - TLSAttribute.shared_ciphers: lambda: ( - self._ssl_object.shared_ciphers() - if self._ssl_object.server_side - else None - ), - TLSAttribute.standard_compatible: lambda: self.standard_compatible, - TLSAttribute.ssl_object: lambda: self._ssl_object, - TLSAttribute.tls_version: self._ssl_object.version, - } - - -@dataclass(eq=False) -class TLSListener(Listener[TLSStream]): - """ - A convenience listener that wraps another listener and auto-negotiates a TLS session - on every accepted connection. - - If the TLS handshake times out or raises an exception, - :meth:`handle_handshake_error` is called to do whatever post-mortem processing is - deemed necessary. - - Supports only the :attr:`~TLSAttribute.standard_compatible` extra attribute. - - :param Listener listener: the listener to wrap - :param ssl_context: the SSL context object - :param standard_compatible: a flag passed through to :meth:`TLSStream.wrap` - :param handshake_timeout: time limit for the TLS handshake - (passed to :func:`~anyio.fail_after`) - """ - - listener: Listener[Any] - ssl_context: ssl.SSLContext - standard_compatible: bool = True - handshake_timeout: float = 30 - - @staticmethod - async def handle_handshake_error(exc: BaseException, stream: AnyByteStream) -> None: - """ - Handle an exception raised during the TLS handshake. - - This method does 3 things: - - #. Forcefully closes the original stream - #. Logs the exception (unless it was a cancellation exception) using the - ``anyio.streams.tls`` logger - #. Reraises the exception if it was a base exception or a cancellation exception - - :param exc: the exception - :param stream: the original stream - - """ - await aclose_forcefully(stream) - - # Log all except cancellation exceptions - if not isinstance(exc, get_cancelled_exc_class()): - # CPython (as of 3.11.5) returns incorrect `sys.exc_info()` here when using - # any asyncio implementation, so we explicitly pass the exception to log - # (https://github.com/python/cpython/issues/108668). Trio does not have this - # issue because it works around the CPython bug. - logging.getLogger(__name__).exception( - "Error during TLS handshake", exc_info=exc - ) - - # Only reraise base exceptions and cancellation exceptions - if not isinstance(exc, Exception) or isinstance(exc, get_cancelled_exc_class()): - raise - - async def serve( - self, - handler: Callable[[TLSStream], Any], - task_group: TaskGroup | None = None, - ) -> None: - @wraps(handler) - async def handler_wrapper(stream: AnyByteStream) -> None: - from .. import fail_after - - try: - with fail_after(self.handshake_timeout): - wrapped_stream = await TLSStream.wrap( - stream, - ssl_context=self.ssl_context, - standard_compatible=self.standard_compatible, - ) - except BaseException as exc: - await self.handle_handshake_error(exc, stream) - else: - await handler(wrapped_stream) - - await self.listener.serve(handler_wrapper, task_group) - - async def aclose(self) -> None: - await self.listener.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return { - TLSAttribute.standard_compatible: lambda: self.standard_compatible, - } - - -class TLSConnectable(ByteStreamConnectable): - """ - Wraps another connectable and does TLS negotiation after a successful connection. - - :param connectable: the connectable to wrap - :param hostname: host name of the server (if host name checking is desired) - :param ssl_context: the SSLContext object to use (if not provided, a secure default - will be created) - :param standard_compatible: if ``False``, skip the closing handshake when closing - the connection, and don't raise an exception if the server does the same - """ - - def __init__( - self, - connectable: AnyByteStreamConnectable, - *, - hostname: str | None = None, - ssl_context: ssl.SSLContext | None = None, - standard_compatible: bool = True, - ) -> None: - self.connectable = connectable - self.ssl_context: SSLContext = ssl_context or ssl.create_default_context( - ssl.Purpose.SERVER_AUTH - ) - if not isinstance(self.ssl_context, ssl.SSLContext): - raise TypeError( - "ssl_context must be an instance of ssl.SSLContext, not " - f"{type(self.ssl_context).__name__}" - ) - self.hostname = hostname - self.standard_compatible = standard_compatible - - @override - async def connect(self) -> TLSStream: - stream = await self.connectable.connect() - try: - return await TLSStream.wrap( - stream, - hostname=self.hostname, - ssl_context=self.ssl_context, - standard_compatible=self.standard_compatible, - ) - except BaseException: - await aclose_forcefully(stream) - raise diff --git a/.venv/lib/python3.12/site-packages/anyio/to_interpreter.py b/.venv/lib/python3.12/site-packages/anyio/to_interpreter.py deleted file mode 100644 index 694dbe77..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/to_interpreter.py +++ /dev/null @@ -1,246 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "run_sync", - "current_default_interpreter_limiter", -) - -import atexit -import os -import sys -from collections import deque -from collections.abc import Callable -from typing import Any, Final, TypeVar - -from . import current_time, to_thread -from ._core._exceptions import BrokenWorkerInterpreter -from ._core._synchronization import CapacityLimiter -from .lowlevel import RunVar - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -if sys.version_info >= (3, 14): - from concurrent.interpreters import ExecutionFailed, create - - def _interp_call( - func: Callable[..., Any], args: tuple[Any, ...] - ) -> tuple[Any, bool]: - try: - retval = func(*args) - except BaseException as exc: - return exc, True - else: - return retval, False - - class _Worker: - last_used: float = 0 - - def __init__(self) -> None: - self._interpreter = create() - - def destroy(self) -> None: - self._interpreter.close() - - def call( - self, - func: Callable[..., T_Retval], - args: tuple[Any, ...], - ) -> T_Retval: - try: - res, is_exception = self._interpreter.call(_interp_call, func, args) - except ExecutionFailed as exc: - raise BrokenWorkerInterpreter(exc.excinfo) from exc - - if is_exception: - raise res - - return res -elif sys.version_info >= (3, 13): - import _interpqueues - import _interpreters - - UNBOUND: Final = 2 # I have no clue how this works, but it was used in the stdlib - FMT_UNPICKLED: Final = 0 - FMT_PICKLED: Final = 1 - QUEUE_PICKLE_ARGS: Final = (FMT_PICKLED, UNBOUND) - QUEUE_UNPICKLE_ARGS: Final = (FMT_UNPICKLED, UNBOUND) - - _run_func = compile( - """ -import _interpqueues -from _interpreters import NotShareableError -from pickle import loads, dumps, HIGHEST_PROTOCOL - -QUEUE_PICKLE_ARGS = (1, 2) -QUEUE_UNPICKLE_ARGS = (0, 2) - -item = _interpqueues.get(queue_id)[0] -try: - func, args = loads(item) - retval = func(*args) -except BaseException as exc: - is_exception = True - retval = exc -else: - is_exception = False - -try: - _interpqueues.put(queue_id, (retval, is_exception), *QUEUE_UNPICKLE_ARGS) -except NotShareableError: - retval = dumps(retval, HIGHEST_PROTOCOL) - _interpqueues.put(queue_id, (retval, is_exception), *QUEUE_PICKLE_ARGS) - """, - "", - "exec", - ) - - class _Worker: - last_used: float = 0 - - def __init__(self) -> None: - self._interpreter_id = _interpreters.create() - self._queue_id = _interpqueues.create(1, *QUEUE_UNPICKLE_ARGS) - _interpreters.set___main___attrs( - self._interpreter_id, {"queue_id": self._queue_id} - ) - - def destroy(self) -> None: - _interpqueues.destroy(self._queue_id) - _interpreters.destroy(self._interpreter_id) - - def call( - self, - func: Callable[..., T_Retval], - args: tuple[Any, ...], - ) -> T_Retval: - import pickle - - item = pickle.dumps((func, args), pickle.HIGHEST_PROTOCOL) - _interpqueues.put(self._queue_id, item, *QUEUE_PICKLE_ARGS) - exc_info = _interpreters.exec(self._interpreter_id, _run_func) - if exc_info: - raise BrokenWorkerInterpreter(exc_info) - - res = _interpqueues.get(self._queue_id) - (res, is_exception), fmt = res[:2] - if fmt == FMT_PICKLED: - res = pickle.loads(res) - - if is_exception: - raise res - - return res -else: - - class _Worker: - last_used: float = 0 - - def __init__(self) -> None: - raise RuntimeError("subinterpreters require at least Python 3.13") - - def call( - self, - func: Callable[..., T_Retval], - args: tuple[Any, ...], - ) -> T_Retval: - raise NotImplementedError - - def destroy(self) -> None: - pass - - -DEFAULT_CPU_COUNT: Final = 8 # this is just an arbitrarily selected value -MAX_WORKER_IDLE_TIME = ( - 30 # seconds a subinterpreter can be idle before becoming eligible for pruning -) - -T_Retval = TypeVar("T_Retval") -PosArgsT = TypeVarTuple("PosArgsT") - -_idle_workers = RunVar[deque[_Worker]]("_available_workers") -_default_interpreter_limiter = RunVar[CapacityLimiter]("_default_interpreter_limiter") - - -def _stop_workers(workers: deque[_Worker]) -> None: - for worker in workers: - worker.destroy() - - workers.clear() - - -async def run_sync( - func: Callable[[Unpack[PosArgsT]], T_Retval], - *args: Unpack[PosArgsT], - limiter: CapacityLimiter | None = None, -) -> T_Retval: - """ - Call the given function with the given arguments in a subinterpreter. - - .. warning:: On Python 3.13, the :mod:`concurrent.interpreters` module was not yet - available, so the code path for that Python version relies on an undocumented, - private API. As such, it is recommended to not rely on this function for anything - mission-critical on Python 3.13. - - :param func: a callable - :param args: the positional arguments for the callable - :param limiter: capacity limiter to use to limit the total number of subinterpreters - running (if omitted, the default limiter is used) - :return: the result of the call - :raises BrokenWorkerInterpreter: if there's an internal error in a subinterpreter - - """ - if limiter is None: - limiter = current_default_interpreter_limiter() - - try: - idle_workers = _idle_workers.get() - except LookupError: - idle_workers = deque() - _idle_workers.set(idle_workers) - atexit.register(_stop_workers, idle_workers) - - async with limiter: - try: - worker = idle_workers.pop() - except IndexError: - worker = _Worker() - - try: - return await to_thread.run_sync( - worker.call, - func, - args, - limiter=limiter, - ) - finally: - # Prune workers that have been idle for too long - now = current_time() - while idle_workers: - if now - idle_workers[0].last_used <= MAX_WORKER_IDLE_TIME: - break - - await to_thread.run_sync(idle_workers.popleft().destroy, limiter=limiter) - - worker.last_used = current_time() - idle_workers.append(worker) - - -def current_default_interpreter_limiter() -> CapacityLimiter: - """ - Return the capacity limiter used by default to limit the number of concurrently - running subinterpreters. - - Defaults to the number of CPU cores. - - :return: a capacity limiter object - - """ - try: - return _default_interpreter_limiter.get() - except LookupError: - limiter = CapacityLimiter(os.cpu_count() or DEFAULT_CPU_COUNT) - _default_interpreter_limiter.set(limiter) - return limiter diff --git a/.venv/lib/python3.12/site-packages/anyio/to_process.py b/.venv/lib/python3.12/site-packages/anyio/to_process.py deleted file mode 100644 index fd65b18c..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/to_process.py +++ /dev/null @@ -1,268 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "current_default_process_limiter", - "process_worker", - "run_sync", -) - -import os -import pickle -import runpy -import subprocess -import sys -from collections import deque -from collections.abc import Callable -from types import ModuleType -from typing import TypeVar, cast - -from ._core._eventloop import current_time, get_async_backend, get_cancelled_exc_class -from ._core._exceptions import BrokenWorkerProcess -from ._core._subprocesses import open_process -from ._core._synchronization import CapacityLimiter -from ._core._tasks import CancelScope, fail_after -from .abc import ByteReceiveStream, ByteSendStream, Process -from .lowlevel import RunVar, checkpoint_if_cancelled -from .streams.buffered import BufferedByteReceiveStream - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -WORKER_MAX_IDLE_TIME = 300 # 5 minutes - -T_Retval = TypeVar("T_Retval") -PosArgsT = TypeVarTuple("PosArgsT") - -_process_pool_workers: RunVar[set[Process]] = RunVar("_process_pool_workers") -_process_pool_idle_workers: RunVar[deque[tuple[Process, float]]] = RunVar( - "_process_pool_idle_workers" -) -_default_process_limiter: RunVar[CapacityLimiter] = RunVar("_default_process_limiter") - - -async def run_sync( # type: ignore[return] - func: Callable[[Unpack[PosArgsT]], T_Retval], - *args: Unpack[PosArgsT], - cancellable: bool = False, - limiter: CapacityLimiter | None = None, -) -> T_Retval: - """ - Call the given function with the given arguments in a worker process. - - If the ``cancellable`` option is enabled and the task waiting for its completion is - cancelled, the worker process running it will be abruptly terminated using SIGKILL - (or ``terminateProcess()`` on Windows). - - :param func: a callable - :param args: positional arguments for the callable - :param cancellable: ``True`` to allow cancellation of the operation while it's - running - :param limiter: capacity limiter to use to limit the total amount of processes - running (if omitted, the default limiter is used) - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - :return: an awaitable that yields the return value of the function. - - """ - - async def send_raw_command(pickled_cmd: bytes) -> object: - try: - await stdin.send(pickled_cmd) - response = await buffered.receive_until(b"\n", 50) - status, length = response.split(b" ") - if status not in (b"RETURN", b"EXCEPTION"): - raise RuntimeError( - f"Worker process returned unexpected response: {response!r}" - ) - - pickled_response = await buffered.receive_exactly(int(length)) - except BaseException as exc: - workers.discard(process) - try: - process.kill() - with CancelScope(shield=True): - await process.aclose() - except ProcessLookupError: - pass - - if isinstance(exc, get_cancelled_exc_class()): - raise - else: - raise BrokenWorkerProcess from exc - - retval = pickle.loads(pickled_response) - if status == b"EXCEPTION": - assert isinstance(retval, BaseException) - raise retval - else: - return retval - - # First pickle the request before trying to reserve a worker process - await checkpoint_if_cancelled() - request = pickle.dumps(("run", func, args), protocol=pickle.HIGHEST_PROTOCOL) - - # If this is the first run in this event loop thread, set up the necessary variables - try: - workers = _process_pool_workers.get() - idle_workers = _process_pool_idle_workers.get() - except LookupError: - workers = set() - idle_workers = deque() - _process_pool_workers.set(workers) - _process_pool_idle_workers.set(idle_workers) - get_async_backend().setup_process_pool_exit_at_shutdown(workers) - - async with limiter or current_default_process_limiter(): - # Pop processes from the pool (starting from the most recently used) until we - # find one that hasn't exited yet - process: Process - while idle_workers: - process, idle_since = idle_workers.pop() - if process.returncode is None: - stdin = cast(ByteSendStream, process.stdin) - buffered = BufferedByteReceiveStream( - cast(ByteReceiveStream, process.stdout) - ) - - # Prune any other workers that have been idle for WORKER_MAX_IDLE_TIME - # seconds or longer - now = current_time() - killed_processes: list[Process] = [] - while idle_workers: - if now - idle_workers[0][1] < WORKER_MAX_IDLE_TIME: - break - - process_to_kill, idle_since = idle_workers.popleft() - process_to_kill.kill() - workers.remove(process_to_kill) - killed_processes.append(process_to_kill) - - with CancelScope(shield=True): - for killed_process in killed_processes: - await killed_process.aclose() - - break - - workers.remove(process) - else: - command = [sys.executable, "-u", "-m", __name__] - process = await open_process( - command, stdin=subprocess.PIPE, stdout=subprocess.PIPE - ) - try: - stdin = cast(ByteSendStream, process.stdin) - buffered = BufferedByteReceiveStream( - cast(ByteReceiveStream, process.stdout) - ) - with fail_after(20): - message = await buffered.receive(6) - - if message != b"READY\n": - raise BrokenWorkerProcess( - f"Worker process returned unexpected response: {message!r}" - ) - - main_module_path = getattr(sys.modules["__main__"], "__file__", None) - pickled = pickle.dumps( - ("init", sys.path, main_module_path), - protocol=pickle.HIGHEST_PROTOCOL, - ) - await send_raw_command(pickled) - except (BrokenWorkerProcess, get_cancelled_exc_class()): - raise - except BaseException as exc: - process.kill() - raise BrokenWorkerProcess( - "Error during worker process initialization" - ) from exc - - workers.add(process) - - with CancelScope(shield=not cancellable): - try: - return cast(T_Retval, await send_raw_command(request)) - finally: - if process in workers: - idle_workers.append((process, current_time())) - - -def current_default_process_limiter() -> CapacityLimiter: - """ - Return the capacity limiter that is used by default to limit the number of worker - processes. - - :return: a capacity limiter object - - """ - try: - return _default_process_limiter.get() - except LookupError: - limiter = CapacityLimiter(os.cpu_count() or 2) - _default_process_limiter.set(limiter) - return limiter - - -def process_worker() -> None: - # Redirect standard streams to os.devnull so that user code won't interfere with the - # parent-worker communication - stdin = sys.stdin - stdout = sys.stdout - sys.stdin = open(os.devnull) - sys.stdout = open(os.devnull, "w") - - stdout.buffer.write(b"READY\n") - while True: - retval = exception = None - try: - command, *args = pickle.load(stdin.buffer) - except EOFError: - return - except BaseException as exc: - exception = exc - else: - if command == "run": - func, args = args - try: - retval = func(*args) - except BaseException as exc: - exception = exc - elif command == "init": - main_module_path: str | None - sys.path, main_module_path = args - del sys.modules["__main__"] - if main_module_path and os.path.isfile(main_module_path): - # Load the parent's main module but as __mp_main__ instead of - # __main__ (like multiprocessing does) to avoid infinite recursion - try: - main = ModuleType("__mp_main__") - main_content = runpy.run_path( - main_module_path, run_name="__mp_main__" - ) - main.__dict__.update(main_content) - sys.modules["__main__"] = sys.modules["__mp_main__"] = main - except BaseException as exc: - exception = exc - try: - if exception is not None: - status = b"EXCEPTION" - pickled = pickle.dumps(exception, pickle.HIGHEST_PROTOCOL) - else: - status = b"RETURN" - pickled = pickle.dumps(retval, pickle.HIGHEST_PROTOCOL) - except BaseException as exc: - exception = exc - status = b"EXCEPTION" - pickled = pickle.dumps(exc, pickle.HIGHEST_PROTOCOL) - - stdout.buffer.write(b"%s %d\n" % (status, len(pickled))) - stdout.buffer.write(pickled) - - # Respect SIGTERM - if isinstance(exception, SystemExit): - raise exception - - -if __name__ == "__main__": - process_worker() diff --git a/.venv/lib/python3.12/site-packages/anyio/to_thread.py b/.venv/lib/python3.12/site-packages/anyio/to_thread.py deleted file mode 100644 index 83c79d1c..00000000 --- a/.venv/lib/python3.12/site-packages/anyio/to_thread.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import annotations - -__all__ = ( - "run_sync", - "current_default_thread_limiter", -) - -import sys -from collections.abc import Callable -from typing import TypeVar -from warnings import warn - -from ._core._eventloop import get_async_backend -from .abc import CapacityLimiter - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -T_Retval = TypeVar("T_Retval") -PosArgsT = TypeVarTuple("PosArgsT") - - -async def run_sync( - func: Callable[[Unpack[PosArgsT]], T_Retval], - *args: Unpack[PosArgsT], - abandon_on_cancel: bool = False, - cancellable: bool | None = None, - limiter: CapacityLimiter | None = None, -) -> T_Retval: - """ - Call the given function with the given arguments in a worker thread. - - If the ``abandon_on_cancel`` option is enabled and the task waiting for its - completion is cancelled, the thread will still run its course but its - return value (or any raised exception) will be ignored. - - :param func: a callable - :param args: positional arguments for the callable - :param abandon_on_cancel: ``True`` to abandon the thread (leaving it to run - unchecked on own) if the host task is cancelled, ``False`` to ignore - cancellations in the host task until the operation has completed in the worker - thread - :param cancellable: deprecated alias of ``abandon_on_cancel``; will override - ``abandon_on_cancel`` if both parameters are passed - :param limiter: capacity limiter to use to limit the total amount of threads running - (if omitted, the default limiter is used) - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - :return: an awaitable that yields the return value of the function. - - """ - if cancellable is not None: - abandon_on_cancel = cancellable - warn( - "The `cancellable=` keyword argument to `anyio.to_thread.run_sync` is " - "deprecated since AnyIO 4.1.0; use `abandon_on_cancel=` instead", - DeprecationWarning, - stacklevel=2, - ) - - return await get_async_backend().run_sync_in_worker_thread( - func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter - ) - - -def current_default_thread_limiter() -> CapacityLimiter: - """ - Return the capacity limiter that is used by default to limit the number of - concurrent threads. - - :return: a capacity limiter object - :raises NoEventLoopError: if no supported asynchronous event loop is running in the - current thread - - """ - return get_async_backend().current_default_thread_limiter() diff --git a/.venv/lib/python3.12/site-packages/attr/__init__.py b/.venv/lib/python3.12/site-packages/attr/__init__.py deleted file mode 100644 index 5c6e0650..00000000 --- a/.venv/lib/python3.12/site-packages/attr/__init__.py +++ /dev/null @@ -1,104 +0,0 @@ -# SPDX-License-Identifier: MIT - -""" -Classes Without Boilerplate -""" - -from functools import partial -from typing import Callable, Literal, Protocol - -from . import converters, exceptions, filters, setters, validators -from ._cmp import cmp_using -from ._config import get_run_validators, set_run_validators -from ._funcs import asdict, assoc, astuple, has, resolve_types -from ._make import ( - NOTHING, - Attribute, - Converter, - Factory, - _Nothing, - attrib, - attrs, - evolve, - fields, - fields_dict, - make_class, - validate, -) -from ._next_gen import define, field, frozen, mutable -from ._version_info import VersionInfo - - -s = attributes = attrs -ib = attr = attrib -dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) - - -class AttrsInstance(Protocol): - pass - - -NothingType = Literal[_Nothing.NOTHING] - -__all__ = [ - "NOTHING", - "Attribute", - "AttrsInstance", - "Converter", - "Factory", - "NothingType", - "asdict", - "assoc", - "astuple", - "attr", - "attrib", - "attributes", - "attrs", - "cmp_using", - "converters", - "define", - "evolve", - "exceptions", - "field", - "fields", - "fields_dict", - "filters", - "frozen", - "get_run_validators", - "has", - "ib", - "make_class", - "mutable", - "resolve_types", - "s", - "set_run_validators", - "setters", - "validate", - "validators", -] - - -def _make_getattr(mod_name: str) -> Callable: - """ - Create a metadata proxy for packaging information that uses *mod_name* in - its warnings and errors. - """ - - def __getattr__(name: str) -> str: - if name not in ("__version__", "__version_info__"): - msg = f"module {mod_name} has no attribute {name}" - raise AttributeError(msg) - - from importlib.metadata import metadata - - meta = metadata("attrs") - - if name == "__version_info__": - return VersionInfo._from_version_string(meta["version"]) - - return meta["version"] - - return __getattr__ - - -__getattr__ = _make_getattr(__name__) diff --git a/.venv/lib/python3.12/site-packages/attr/__init__.pyi b/.venv/lib/python3.12/site-packages/attr/__init__.pyi deleted file mode 100644 index 758decf8..00000000 --- a/.venv/lib/python3.12/site-packages/attr/__init__.pyi +++ /dev/null @@ -1,389 +0,0 @@ -import enum -import sys - -from typing import ( - Any, - Callable, - Generic, - Literal, - Mapping, - Protocol, - Sequence, - TypeVar, - overload, -) - -# `import X as X` is required to make these public -from . import converters as converters -from . import exceptions as exceptions -from . import filters as filters -from . import setters as setters -from . import validators as validators -from ._cmp import cmp_using as cmp_using -from ._typing_compat import AttrsInstance_ -from ._version_info import VersionInfo -from attrs import ( - define as define, - field as field, - mutable as mutable, - frozen as frozen, - _EqOrderType, - _ValidatorType, - _ConverterType, - _ReprArgType, - _OnSetAttrType, - _OnSetAttrArgType, - _FieldTransformer, - _ValidatorArgType, -) - -if sys.version_info >= (3, 10): - from typing import TypeGuard, TypeAlias -else: - from typing_extensions import TypeGuard, TypeAlias - -if sys.version_info >= (3, 11): - from typing import dataclass_transform -else: - from typing_extensions import dataclass_transform - -__version__: str -__version_info__: VersionInfo -__title__: str -__description__: str -__url__: str -__uri__: str -__author__: str -__email__: str -__license__: str -__copyright__: str - -_T = TypeVar("_T") -_C = TypeVar("_C", bound=type) - -_FilterType = Callable[["Attribute[_T]", _T], bool] - -# We subclass this here to keep the protocol's qualified name clean. -class AttrsInstance(AttrsInstance_, Protocol): - pass - -_A = TypeVar("_A", bound=type[AttrsInstance]) - -class _Nothing(enum.Enum): - NOTHING = enum.auto() - -NOTHING = _Nothing.NOTHING -NothingType: TypeAlias = Literal[_Nothing.NOTHING] - -# NOTE: Factory lies about its return type to make this possible: -# `x: List[int] # = Factory(list)` -# Work around mypy issue #4554 in the common case by using an overload. - -@overload -def Factory(factory: Callable[[], _T]) -> _T: ... -@overload -def Factory( - factory: Callable[[Any], _T], - takes_self: Literal[True], -) -> _T: ... -@overload -def Factory( - factory: Callable[[], _T], - takes_self: Literal[False], -) -> _T: ... - -In = TypeVar("In") -Out = TypeVar("Out") - -class Converter(Generic[In, Out]): - @overload - def __init__(self, converter: Callable[[In], Out]) -> None: ... - @overload - def __init__( - self, - converter: Callable[[In, AttrsInstance, Attribute], Out], - *, - takes_self: Literal[True], - takes_field: Literal[True], - ) -> None: ... - @overload - def __init__( - self, - converter: Callable[[In, Attribute], Out], - *, - takes_field: Literal[True], - ) -> None: ... - @overload - def __init__( - self, - converter: Callable[[In, AttrsInstance], Out], - *, - takes_self: Literal[True], - ) -> None: ... - -class Attribute(Generic[_T]): - name: str - default: _T | None - validator: _ValidatorType[_T] | None - repr: _ReprArgType - cmp: _EqOrderType - eq: _EqOrderType - order: _EqOrderType - hash: bool | None - init: bool - converter: Converter | None - metadata: dict[Any, Any] - type: type[_T] | None - kw_only: bool - on_setattr: _OnSetAttrType - alias: str | None - - def evolve(self, **changes: Any) -> "Attribute[Any]": ... - -# NOTE: We had several choices for the annotation to use for type arg: -# 1) Type[_T] -# - Pros: Handles simple cases correctly -# - Cons: Might produce less informative errors in the case of conflicting -# TypeVars e.g. `attr.ib(default='bad', type=int)` -# 2) Callable[..., _T] -# - Pros: Better error messages than #1 for conflicting TypeVars -# - Cons: Terrible error messages for validator checks. -# e.g. attr.ib(type=int, validator=validate_str) -# -> error: Cannot infer function type argument -# 3) type (and do all of the work in the mypy plugin) -# - Pros: Simple here, and we could customize the plugin with our own errors. -# - Cons: Would need to write mypy plugin code to handle all the cases. -# We chose option #1. - -# `attr` lies about its return type to make the following possible: -# attr() -> Any -# attr(8) -> int -# attr(validator=) -> Whatever the callable expects. -# This makes this type of assignments possible: -# x: int = attr(8) -# -# This form catches explicit None or no default but with no other arguments -# returns Any. -@overload -def attrib( - default: None = ..., - validator: None = ..., - repr: _ReprArgType = ..., - cmp: _EqOrderType | None = ..., - hash: bool | None = ..., - init: bool = ..., - metadata: Mapping[Any, Any] | None = ..., - type: None = ..., - converter: None = ..., - factory: None = ..., - kw_only: bool | None = ..., - eq: _EqOrderType | None = ..., - order: _EqOrderType | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - alias: str | None = ..., -) -> Any: ... - -# This form catches an explicit None or no default and infers the type from the -# other arguments. -@overload -def attrib( - default: None = ..., - validator: _ValidatorArgType[_T] | None = ..., - repr: _ReprArgType = ..., - cmp: _EqOrderType | None = ..., - hash: bool | None = ..., - init: bool = ..., - metadata: Mapping[Any, Any] | None = ..., - type: type[_T] | None = ..., - converter: _ConverterType - | list[_ConverterType] - | tuple[_ConverterType] - | None = ..., - factory: Callable[[], _T] | None = ..., - kw_only: bool | None = ..., - eq: _EqOrderType | None = ..., - order: _EqOrderType | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - alias: str | None = ..., -) -> _T: ... - -# This form catches an explicit default argument. -@overload -def attrib( - default: _T, - validator: _ValidatorArgType[_T] | None = ..., - repr: _ReprArgType = ..., - cmp: _EqOrderType | None = ..., - hash: bool | None = ..., - init: bool = ..., - metadata: Mapping[Any, Any] | None = ..., - type: type[_T] | None = ..., - converter: _ConverterType - | list[_ConverterType] - | tuple[_ConverterType] - | None = ..., - factory: Callable[[], _T] | None = ..., - kw_only: bool | None = ..., - eq: _EqOrderType | None = ..., - order: _EqOrderType | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - alias: str | None = ..., -) -> _T: ... - -# This form covers type=non-Type: e.g. forward references (str), Any -@overload -def attrib( - default: _T | None = ..., - validator: _ValidatorArgType[_T] | None = ..., - repr: _ReprArgType = ..., - cmp: _EqOrderType | None = ..., - hash: bool | None = ..., - init: bool = ..., - metadata: Mapping[Any, Any] | None = ..., - type: object = ..., - converter: _ConverterType - | list[_ConverterType] - | tuple[_ConverterType] - | None = ..., - factory: Callable[[], _T] | None = ..., - kw_only: bool | None = ..., - eq: _EqOrderType | None = ..., - order: _EqOrderType | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - alias: str | None = ..., -) -> Any: ... -@overload -@dataclass_transform(order_default=True, field_specifiers=(attrib, field)) -def attrs( - maybe_cls: _C, - these: dict[str, Any] | None = ..., - repr_ns: str | None = ..., - repr: bool = ..., - cmp: _EqOrderType | None = ..., - hash: bool | None = ..., - init: bool = ..., - slots: bool = ..., - frozen: bool = ..., - weakref_slot: bool = ..., - str: bool = ..., - auto_attribs: bool = ..., - kw_only: bool = ..., - cache_hash: bool = ..., - auto_exc: bool = ..., - eq: _EqOrderType | None = ..., - order: _EqOrderType | None = ..., - auto_detect: bool = ..., - collect_by_mro: bool = ..., - getstate_setstate: bool | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - field_transformer: _FieldTransformer | None = ..., - match_args: bool = ..., - unsafe_hash: bool | None = ..., -) -> _C: ... -@overload -@dataclass_transform(order_default=True, field_specifiers=(attrib, field)) -def attrs( - maybe_cls: None = ..., - these: dict[str, Any] | None = ..., - repr_ns: str | None = ..., - repr: bool = ..., - cmp: _EqOrderType | None = ..., - hash: bool | None = ..., - init: bool = ..., - slots: bool = ..., - frozen: bool = ..., - weakref_slot: bool = ..., - str: bool = ..., - auto_attribs: bool = ..., - kw_only: bool = ..., - cache_hash: bool = ..., - auto_exc: bool = ..., - eq: _EqOrderType | None = ..., - order: _EqOrderType | None = ..., - auto_detect: bool = ..., - collect_by_mro: bool = ..., - getstate_setstate: bool | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - field_transformer: _FieldTransformer | None = ..., - match_args: bool = ..., - unsafe_hash: bool | None = ..., -) -> Callable[[_C], _C]: ... -def fields(cls: type[AttrsInstance] | AttrsInstance) -> Any: ... -def fields_dict(cls: type[AttrsInstance]) -> dict[str, Attribute[Any]]: ... -def validate(inst: AttrsInstance) -> None: ... -def resolve_types( - cls: _A, - globalns: dict[str, Any] | None = ..., - localns: dict[str, Any] | None = ..., - attribs: list[Attribute[Any]] | None = ..., - include_extras: bool = ..., -) -> _A: ... - -# TODO: add support for returning a proper attrs class from the mypy plugin -# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', -# [attr.ib()])` is valid -def make_class( - name: str, - attrs: list[str] | tuple[str, ...] | dict[str, Any], - bases: tuple[type, ...] = ..., - class_body: dict[str, Any] | None = ..., - repr_ns: str | None = ..., - repr: bool = ..., - cmp: _EqOrderType | None = ..., - hash: bool | None = ..., - init: bool = ..., - slots: bool = ..., - frozen: bool = ..., - weakref_slot: bool = ..., - str: bool = ..., - auto_attribs: bool = ..., - kw_only: bool = ..., - cache_hash: bool = ..., - auto_exc: bool = ..., - eq: _EqOrderType | None = ..., - order: _EqOrderType | None = ..., - collect_by_mro: bool = ..., - on_setattr: _OnSetAttrArgType | None = ..., - field_transformer: _FieldTransformer | None = ..., -) -> type: ... - -# _funcs -- - -# TODO: add support for returning TypedDict from the mypy plugin -# FIXME: asdict/astuple do not honor their factory args. Waiting on one of -# these: -# https://github.com/python/mypy/issues/4236 -# https://github.com/python/typing/issues/253 -# XXX: remember to fix attrs.asdict/astuple too! -def asdict( - inst: AttrsInstance, - recurse: bool = ..., - filter: _FilterType[Any] | None = ..., - dict_factory: type[Mapping[Any, Any]] = ..., - retain_collection_types: bool = ..., - value_serializer: Callable[[type, Attribute[Any], Any], Any] | None = ..., - tuple_keys: bool | None = ..., -) -> dict[str, Any]: ... - -# TODO: add support for returning NamedTuple from the mypy plugin -def astuple( - inst: AttrsInstance, - recurse: bool = ..., - filter: _FilterType[Any] | None = ..., - tuple_factory: type[Sequence[Any]] = ..., - retain_collection_types: bool = ..., -) -> tuple[Any, ...]: ... -def has(cls: type) -> TypeGuard[type[AttrsInstance]]: ... -def assoc(inst: _T, **changes: Any) -> _T: ... -def evolve(inst: _T, **changes: Any) -> _T: ... - -# _config -- - -def set_run_validators(run: bool) -> None: ... -def get_run_validators() -> bool: ... - -# aliases -- - -s = attributes = attrs -ib = attr = attrib -dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;) diff --git a/.venv/lib/python3.12/site-packages/attr/_cmp.py b/.venv/lib/python3.12/site-packages/attr/_cmp.py deleted file mode 100644 index 09bab491..00000000 --- a/.venv/lib/python3.12/site-packages/attr/_cmp.py +++ /dev/null @@ -1,160 +0,0 @@ -# SPDX-License-Identifier: MIT - - -import functools -import types - -from ._make import __ne__ - - -_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="} - - -def cmp_using( - eq=None, - lt=None, - le=None, - gt=None, - ge=None, - require_same_type=True, - class_name="Comparable", -): - """ - Create a class that can be passed into `attrs.field`'s ``eq``, ``order``, - and ``cmp`` arguments to customize field comparison. - - The resulting class will have a full set of ordering methods if at least - one of ``{lt, le, gt, ge}`` and ``eq`` are provided. - - Args: - eq (typing.Callable | None): - Callable used to evaluate equality of two objects. - - lt (typing.Callable | None): - Callable used to evaluate whether one object is less than another - object. - - le (typing.Callable | None): - Callable used to evaluate whether one object is less than or equal - to another object. - - gt (typing.Callable | None): - Callable used to evaluate whether one object is greater than - another object. - - ge (typing.Callable | None): - Callable used to evaluate whether one object is greater than or - equal to another object. - - require_same_type (bool): - When `True`, equality and ordering methods will return - `NotImplemented` if objects are not of the same type. - - class_name (str | None): Name of class. Defaults to "Comparable". - - See `comparison` for more details. - - .. versionadded:: 21.1.0 - """ - - body = { - "__slots__": ["value"], - "__init__": _make_init(), - "_requirements": [], - "_is_comparable_to": _is_comparable_to, - } - - # Add operations. - num_order_functions = 0 - has_eq_function = False - - if eq is not None: - has_eq_function = True - body["__eq__"] = _make_operator("eq", eq) - body["__ne__"] = __ne__ - - if lt is not None: - num_order_functions += 1 - body["__lt__"] = _make_operator("lt", lt) - - if le is not None: - num_order_functions += 1 - body["__le__"] = _make_operator("le", le) - - if gt is not None: - num_order_functions += 1 - body["__gt__"] = _make_operator("gt", gt) - - if ge is not None: - num_order_functions += 1 - body["__ge__"] = _make_operator("ge", ge) - - type_ = types.new_class( - class_name, (object,), {}, lambda ns: ns.update(body) - ) - - # Add same type requirement. - if require_same_type: - type_._requirements.append(_check_same_type) - - # Add total ordering if at least one operation was defined. - if 0 < num_order_functions < 4: - if not has_eq_function: - # functools.total_ordering requires __eq__ to be defined, - # so raise early error here to keep a nice stack. - msg = "eq must be define is order to complete ordering from lt, le, gt, ge." - raise ValueError(msg) - type_ = functools.total_ordering(type_) - - return type_ - - -def _make_init(): - """ - Create __init__ method. - """ - - def __init__(self, value): - """ - Initialize object with *value*. - """ - self.value = value - - return __init__ - - -def _make_operator(name, func): - """ - Create operator method. - """ - - def method(self, other): - if not self._is_comparable_to(other): - return NotImplemented - - result = func(self.value, other.value) - if result is NotImplemented: - return NotImplemented - - return result - - method.__name__ = f"__{name}__" - method.__doc__ = ( - f"Return a {_operation_names[name]} b. Computed by attrs." - ) - - return method - - -def _is_comparable_to(self, other): - """ - Check whether `other` is comparable to `self`. - """ - return all(func(self, other) for func in self._requirements) - - -def _check_same_type(self, other): - """ - Return True if *self* and *other* are of the same type, False otherwise. - """ - return other.value.__class__ is self.value.__class__ diff --git a/.venv/lib/python3.12/site-packages/attr/_cmp.pyi b/.venv/lib/python3.12/site-packages/attr/_cmp.pyi deleted file mode 100644 index cc7893b0..00000000 --- a/.venv/lib/python3.12/site-packages/attr/_cmp.pyi +++ /dev/null @@ -1,13 +0,0 @@ -from typing import Any, Callable - -_CompareWithType = Callable[[Any, Any], bool] - -def cmp_using( - eq: _CompareWithType | None = ..., - lt: _CompareWithType | None = ..., - le: _CompareWithType | None = ..., - gt: _CompareWithType | None = ..., - ge: _CompareWithType | None = ..., - require_same_type: bool = ..., - class_name: str = ..., -) -> type: ... diff --git a/.venv/lib/python3.12/site-packages/attr/_compat.py b/.venv/lib/python3.12/site-packages/attr/_compat.py deleted file mode 100644 index bc68ed9e..00000000 --- a/.venv/lib/python3.12/site-packages/attr/_compat.py +++ /dev/null @@ -1,99 +0,0 @@ -# SPDX-License-Identifier: MIT - -import inspect -import platform -import sys -import threading - -from collections.abc import Mapping, Sequence # noqa: F401 -from typing import _GenericAlias - - -PYPY = platform.python_implementation() == "PyPy" -PY_3_10_PLUS = sys.version_info[:2] >= (3, 10) -PY_3_11_PLUS = sys.version_info[:2] >= (3, 11) -PY_3_12_PLUS = sys.version_info[:2] >= (3, 12) -PY_3_13_PLUS = sys.version_info[:2] >= (3, 13) -PY_3_14_PLUS = sys.version_info[:2] >= (3, 14) - - -if PY_3_14_PLUS: - import annotationlib - - # We request forward-ref annotations to not break in the presence of - # forward references. - - def _get_annotations(cls): - return annotationlib.get_annotations( - cls, format=annotationlib.Format.FORWARDREF - ) - -else: - - def _get_annotations(cls): - """ - Get annotations for *cls*. - """ - return cls.__dict__.get("__annotations__", {}) - - -class _AnnotationExtractor: - """ - Extract type annotations from a callable, returning None whenever there - is none. - """ - - __slots__ = ["sig"] - - def __init__(self, callable): - try: - self.sig = inspect.signature(callable) - except (ValueError, TypeError): # inspect failed - self.sig = None - - def get_first_param_type(self): - """ - Return the type annotation of the first argument if it's not empty. - """ - if not self.sig: - return None - - params = list(self.sig.parameters.values()) - if params and params[0].annotation is not inspect.Parameter.empty: - return params[0].annotation - - return None - - def get_return_type(self): - """ - Return the return type if it's not empty. - """ - if ( - self.sig - and self.sig.return_annotation is not inspect.Signature.empty - ): - return self.sig.return_annotation - - return None - - -# Thread-local global to track attrs instances which are already being repr'd. -# This is needed because there is no other (thread-safe) way to pass info -# about the instances that are already being repr'd through the call stack -# in order to ensure we don't perform infinite recursion. -# -# For instance, if an instance contains a dict which contains that instance, -# we need to know that we're already repr'ing the outside instance from within -# the dict's repr() call. -# -# This lives here rather than in _make.py so that the functions in _make.py -# don't have a direct reference to the thread-local in their globals dict. -# If they have such a reference, it breaks cloudpickle. -repr_context = threading.local() - - -def get_generic_base(cl): - """If this is a generic class (A[str]), return the generic base for it.""" - if cl.__class__ is _GenericAlias: - return cl.__origin__ - return None diff --git a/.venv/lib/python3.12/site-packages/attr/_config.py b/.venv/lib/python3.12/site-packages/attr/_config.py deleted file mode 100644 index 4b257726..00000000 --- a/.venv/lib/python3.12/site-packages/attr/_config.py +++ /dev/null @@ -1,31 +0,0 @@ -# SPDX-License-Identifier: MIT - -__all__ = ["get_run_validators", "set_run_validators"] - -_run_validators = True - - -def set_run_validators(run): - """ - Set whether or not validators are run. By default, they are run. - - .. deprecated:: 21.3.0 It will not be removed, but it also will not be - moved to new ``attrs`` namespace. Use `attrs.validators.set_disabled()` - instead. - """ - if not isinstance(run, bool): - msg = "'run' must be bool." - raise TypeError(msg) - global _run_validators - _run_validators = run - - -def get_run_validators(): - """ - Return whether or not validators are run. - - .. deprecated:: 21.3.0 It will not be removed, but it also will not be - moved to new ``attrs`` namespace. Use `attrs.validators.get_disabled()` - instead. - """ - return _run_validators diff --git a/.venv/lib/python3.12/site-packages/attr/_funcs.py b/.venv/lib/python3.12/site-packages/attr/_funcs.py deleted file mode 100644 index 1adb5002..00000000 --- a/.venv/lib/python3.12/site-packages/attr/_funcs.py +++ /dev/null @@ -1,497 +0,0 @@ -# SPDX-License-Identifier: MIT - - -import copy - -from ._compat import get_generic_base -from ._make import _OBJ_SETATTR, NOTHING, fields -from .exceptions import AttrsAttributeNotFoundError - - -_ATOMIC_TYPES = frozenset( - { - type(None), - bool, - int, - float, - str, - complex, - bytes, - type(...), - type, - range, - property, - } -) - - -def asdict( - inst, - recurse=True, - filter=None, - dict_factory=dict, - retain_collection_types=False, - value_serializer=None, -): - """ - Return the *attrs* attribute values of *inst* as a dict. - - Optionally recurse into other *attrs*-decorated classes. - - Args: - inst: Instance of an *attrs*-decorated class. - - recurse (bool): Recurse into classes that are also *attrs*-decorated. - - filter (~typing.Callable): - A callable whose return code determines whether an attribute or - element is included (`True`) or dropped (`False`). Is called with - the `attrs.Attribute` as the first argument and the value as the - second argument. - - dict_factory (~typing.Callable): - A callable to produce dictionaries from. For example, to produce - ordered dictionaries instead of normal Python dictionaries, pass in - ``collections.OrderedDict``. - - retain_collection_types (bool): - Do not convert to `list` when encountering an attribute whose type - is `tuple` or `set`. Only meaningful if *recurse* is `True`. - - value_serializer (typing.Callable | None): - A hook that is called for every attribute or dict key/value. It - receives the current instance, field and value and must return the - (updated) value. The hook is run *after* the optional *filter* has - been applied. - - Returns: - Return type of *dict_factory*. - - Raises: - attrs.exceptions.NotAnAttrsClassError: - If *cls* is not an *attrs* class. - - .. versionadded:: 16.0.0 *dict_factory* - .. versionadded:: 16.1.0 *retain_collection_types* - .. versionadded:: 20.3.0 *value_serializer* - .. versionadded:: 21.3.0 - If a dict has a collection for a key, it is serialized as a tuple. - """ - attrs = fields(inst.__class__) - rv = dict_factory() - for a in attrs: - v = getattr(inst, a.name) - if filter is not None and not filter(a, v): - continue - - if value_serializer is not None: - v = value_serializer(inst, a, v) - - if recurse is True: - value_type = type(v) - if value_type in _ATOMIC_TYPES: - rv[a.name] = v - elif has(value_type): - rv[a.name] = asdict( - v, - recurse=True, - filter=filter, - dict_factory=dict_factory, - retain_collection_types=retain_collection_types, - value_serializer=value_serializer, - ) - elif issubclass(value_type, (tuple, list, set, frozenset)): - cf = value_type if retain_collection_types is True else list - items = [ - _asdict_anything( - i, - is_key=False, - filter=filter, - dict_factory=dict_factory, - retain_collection_types=retain_collection_types, - value_serializer=value_serializer, - ) - for i in v - ] - try: - rv[a.name] = cf(items) - except TypeError: - if not issubclass(cf, tuple): - raise - # Workaround for TypeError: cf.__new__() missing 1 required - # positional argument (which appears, for a namedturle) - rv[a.name] = cf(*items) - elif issubclass(value_type, dict): - df = dict_factory - rv[a.name] = df( - ( - _asdict_anything( - kk, - is_key=True, - filter=filter, - dict_factory=df, - retain_collection_types=retain_collection_types, - value_serializer=value_serializer, - ), - _asdict_anything( - vv, - is_key=False, - filter=filter, - dict_factory=df, - retain_collection_types=retain_collection_types, - value_serializer=value_serializer, - ), - ) - for kk, vv in v.items() - ) - else: - rv[a.name] = v - else: - rv[a.name] = v - return rv - - -def _asdict_anything( - val, - is_key, - filter, - dict_factory, - retain_collection_types, - value_serializer, -): - """ - ``asdict`` only works on attrs instances, this works on anything. - """ - val_type = type(val) - if val_type in _ATOMIC_TYPES: - rv = val - if value_serializer is not None: - rv = value_serializer(None, None, rv) - elif getattr(val_type, "__attrs_attrs__", None) is not None: - # Attrs class. - rv = asdict( - val, - recurse=True, - filter=filter, - dict_factory=dict_factory, - retain_collection_types=retain_collection_types, - value_serializer=value_serializer, - ) - elif issubclass(val_type, (tuple, list, set, frozenset)): - if retain_collection_types is True: - cf = val.__class__ - elif is_key: - cf = tuple - else: - cf = list - - rv = cf( - [ - _asdict_anything( - i, - is_key=False, - filter=filter, - dict_factory=dict_factory, - retain_collection_types=retain_collection_types, - value_serializer=value_serializer, - ) - for i in val - ] - ) - elif issubclass(val_type, dict): - df = dict_factory - rv = df( - ( - _asdict_anything( - kk, - is_key=True, - filter=filter, - dict_factory=df, - retain_collection_types=retain_collection_types, - value_serializer=value_serializer, - ), - _asdict_anything( - vv, - is_key=False, - filter=filter, - dict_factory=df, - retain_collection_types=retain_collection_types, - value_serializer=value_serializer, - ), - ) - for kk, vv in val.items() - ) - else: - rv = val - if value_serializer is not None: - rv = value_serializer(None, None, rv) - - return rv - - -def astuple( - inst, - recurse=True, - filter=None, - tuple_factory=tuple, - retain_collection_types=False, -): - """ - Return the *attrs* attribute values of *inst* as a tuple. - - Optionally recurse into other *attrs*-decorated classes. - - Args: - inst: Instance of an *attrs*-decorated class. - - recurse (bool): - Recurse into classes that are also *attrs*-decorated. - - filter (~typing.Callable): - A callable whose return code determines whether an attribute or - element is included (`True`) or dropped (`False`). Is called with - the `attrs.Attribute` as the first argument and the value as the - second argument. - - tuple_factory (~typing.Callable): - A callable to produce tuples from. For example, to produce lists - instead of tuples. - - retain_collection_types (bool): - Do not convert to `list` or `dict` when encountering an attribute - which type is `tuple`, `dict` or `set`. Only meaningful if - *recurse* is `True`. - - Returns: - Return type of *tuple_factory* - - Raises: - attrs.exceptions.NotAnAttrsClassError: - If *cls* is not an *attrs* class. - - .. versionadded:: 16.2.0 - """ - attrs = fields(inst.__class__) - rv = [] - retain = retain_collection_types # Very long. :/ - for a in attrs: - v = getattr(inst, a.name) - if filter is not None and not filter(a, v): - continue - value_type = type(v) - if recurse is True: - if value_type in _ATOMIC_TYPES: - rv.append(v) - elif has(value_type): - rv.append( - astuple( - v, - recurse=True, - filter=filter, - tuple_factory=tuple_factory, - retain_collection_types=retain, - ) - ) - elif issubclass(value_type, (tuple, list, set, frozenset)): - cf = v.__class__ if retain is True else list - items = [ - ( - astuple( - j, - recurse=True, - filter=filter, - tuple_factory=tuple_factory, - retain_collection_types=retain, - ) - if has(j.__class__) - else j - ) - for j in v - ] - try: - rv.append(cf(items)) - except TypeError: - if not issubclass(cf, tuple): - raise - # Workaround for TypeError: cf.__new__() missing 1 required - # positional argument (which appears, for a namedturle) - rv.append(cf(*items)) - elif issubclass(value_type, dict): - df = value_type if retain is True else dict - rv.append( - df( - ( - ( - astuple( - kk, - tuple_factory=tuple_factory, - retain_collection_types=retain, - ) - if has(kk.__class__) - else kk - ), - ( - astuple( - vv, - tuple_factory=tuple_factory, - retain_collection_types=retain, - ) - if has(vv.__class__) - else vv - ), - ) - for kk, vv in v.items() - ) - ) - else: - rv.append(v) - else: - rv.append(v) - - return rv if tuple_factory is list else tuple_factory(rv) - - -def has(cls): - """ - Check whether *cls* is a class with *attrs* attributes. - - Args: - cls (type): Class to introspect. - - Raises: - TypeError: If *cls* is not a class. - - Returns: - bool: - """ - attrs = getattr(cls, "__attrs_attrs__", None) - if attrs is not None: - return True - - # No attrs, maybe it's a specialized generic (A[str])? - generic_base = get_generic_base(cls) - if generic_base is not None: - generic_attrs = getattr(generic_base, "__attrs_attrs__", None) - if generic_attrs is not None: - # Stick it on here for speed next time. - cls.__attrs_attrs__ = generic_attrs - return generic_attrs is not None - return False - - -def assoc(inst, **changes): - """ - Copy *inst* and apply *changes*. - - This is different from `evolve` that applies the changes to the arguments - that create the new instance. - - `evolve`'s behavior is preferable, but there are `edge cases`_ where it - doesn't work. Therefore `assoc` is deprecated, but will not be removed. - - .. _`edge cases`: https://github.com/python-attrs/attrs/issues/251 - - Args: - inst: Instance of a class with *attrs* attributes. - - changes: Keyword changes in the new copy. - - Returns: - A copy of inst with *changes* incorporated. - - Raises: - attrs.exceptions.AttrsAttributeNotFoundError: - If *attr_name* couldn't be found on *cls*. - - attrs.exceptions.NotAnAttrsClassError: - If *cls* is not an *attrs* class. - - .. deprecated:: 17.1.0 - Use `attrs.evolve` instead if you can. This function will not be - removed du to the slightly different approach compared to - `attrs.evolve`, though. - """ - new = copy.copy(inst) - attrs = fields(inst.__class__) - for k, v in changes.items(): - a = getattr(attrs, k, NOTHING) - if a is NOTHING: - msg = f"{k} is not an attrs attribute on {new.__class__}." - raise AttrsAttributeNotFoundError(msg) - _OBJ_SETATTR(new, k, v) - return new - - -def resolve_types( - cls, globalns=None, localns=None, attribs=None, include_extras=True -): - """ - Resolve any strings and forward annotations in type annotations. - - This is only required if you need concrete types in :class:`Attribute`'s - *type* field. In other words, you don't need to resolve your types if you - only use them for static type checking. - - With no arguments, names will be looked up in the module in which the class - was created. If this is not what you want, for example, if the name only - exists inside a method, you may pass *globalns* or *localns* to specify - other dictionaries in which to look up these names. See the docs of - `typing.get_type_hints` for more details. - - Args: - cls (type): Class to resolve. - - globalns (dict | None): Dictionary containing global variables. - - localns (dict | None): Dictionary containing local variables. - - attribs (list | None): - List of attribs for the given class. This is necessary when calling - from inside a ``field_transformer`` since *cls* is not an *attrs* - class yet. - - include_extras (bool): - Resolve more accurately, if possible. Pass ``include_extras`` to - ``typing.get_hints``, if supported by the typing module. On - supported Python versions (3.9+), this resolves the types more - accurately. - - Raises: - TypeError: If *cls* is not a class. - - attrs.exceptions.NotAnAttrsClassError: - If *cls* is not an *attrs* class and you didn't pass any attribs. - - NameError: If types cannot be resolved because of missing variables. - - Returns: - *cls* so you can use this function also as a class decorator. Please - note that you have to apply it **after** `attrs.define`. That means the - decorator has to come in the line **before** `attrs.define`. - - .. versionadded:: 20.1.0 - .. versionadded:: 21.1.0 *attribs* - .. versionadded:: 23.1.0 *include_extras* - """ - # Since calling get_type_hints is expensive we cache whether we've - # done it already. - if getattr(cls, "__attrs_types_resolved__", None) != cls: - import typing - - kwargs = { - "globalns": globalns, - "localns": localns, - "include_extras": include_extras, - } - - hints = typing.get_type_hints(cls, **kwargs) - for field in fields(cls) if attribs is None else attribs: - if field.name in hints: - # Since fields have been frozen we must work around it. - _OBJ_SETATTR(field, "type", hints[field.name]) - # We store the class we resolved so that subclasses know they haven't - # been resolved. - cls.__attrs_types_resolved__ = cls - - # Return the class so you can use it as a decorator too. - return cls diff --git a/.venv/lib/python3.12/site-packages/attr/_make.py b/.venv/lib/python3.12/site-packages/attr/_make.py deleted file mode 100644 index 4b32d6a7..00000000 --- a/.venv/lib/python3.12/site-packages/attr/_make.py +++ /dev/null @@ -1,3406 +0,0 @@ -# SPDX-License-Identifier: MIT - -from __future__ import annotations - -import abc -import contextlib -import copy -import enum -import inspect -import itertools -import linecache -import sys -import types -import unicodedata -import weakref - -from collections.abc import Callable, Mapping -from functools import cached_property -from typing import Any, NamedTuple, TypeVar - -# We need to import _compat itself in addition to the _compat members to avoid -# having the thread-local in the globals here. -from . import _compat, _config, setters -from ._compat import ( - PY_3_10_PLUS, - PY_3_11_PLUS, - PY_3_13_PLUS, - _AnnotationExtractor, - _get_annotations, - get_generic_base, -) -from .exceptions import ( - DefaultAlreadySetError, - FrozenInstanceError, - NotAnAttrsClassError, - UnannotatedAttributeError, -) - - -# This is used at least twice, so cache it here. -_OBJ_SETATTR = object.__setattr__ -_INIT_FACTORY_PAT = "__attr_factory_%s" -_CLASSVAR_PREFIXES = ( - "typing.ClassVar", - "t.ClassVar", - "ClassVar", - "typing_extensions.ClassVar", -) -# we don't use a double-underscore prefix because that triggers -# name mangling when trying to create a slot for the field -# (when slots=True) -_HASH_CACHE_FIELD = "_attrs_cached_hash" - -_EMPTY_METADATA_SINGLETON = types.MappingProxyType({}) - -# Unique object for unequivocal getattr() defaults. -_SENTINEL = object() - -_DEFAULT_ON_SETATTR = setters.pipe(setters.convert, setters.validate) - - -class _Nothing(enum.Enum): - """ - Sentinel to indicate the lack of a value when `None` is ambiguous. - - If extending attrs, you can use ``typing.Literal[NOTHING]`` to show - that a value may be ``NOTHING``. - - .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. - .. versionchanged:: 22.2.0 ``NOTHING`` is now an ``enum.Enum`` variant. - """ - - NOTHING = enum.auto() - - def __repr__(self): - return "NOTHING" - - def __bool__(self): - return False - - -NOTHING = _Nothing.NOTHING -""" -Sentinel to indicate the lack of a value when `None` is ambiguous. - -When using in 3rd party code, use `attrs.NothingType` for type annotations. -""" - - -class _CacheHashWrapper(int): - """ - An integer subclass that pickles / copies as None - - This is used for non-slots classes with ``cache_hash=True``, to avoid - serializing a potentially (even likely) invalid hash value. Since `None` - is the default value for uncalculated hashes, whenever this is copied, - the copy's value for the hash should automatically reset. - - See GH #613 for more details. - """ - - def __reduce__(self, _none_constructor=type(None), _args=()): # noqa: B008 - return _none_constructor, _args - - -def attrib( - default=NOTHING, - validator=None, - repr=True, - cmp=None, - hash=None, - init=True, - metadata=None, - type=None, - converter=None, - factory=None, - kw_only=None, - eq=None, - order=None, - on_setattr=None, - alias=None, -): - """ - Create a new field / attribute on a class. - - Identical to `attrs.field`, except it's not keyword-only. - - Consider using `attrs.field` in new code (``attr.ib`` will *never* go away, - though). - - .. warning:: - - Does **nothing** unless the class is also decorated with - `attr.s` (or similar)! - - - .. versionadded:: 15.2.0 *convert* - .. versionadded:: 16.3.0 *metadata* - .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. - .. versionchanged:: 17.1.0 - *hash* is `None` and therefore mirrors *eq* by default. - .. versionadded:: 17.3.0 *type* - .. deprecated:: 17.4.0 *convert* - .. versionadded:: 17.4.0 - *converter* as a replacement for the deprecated *convert* to achieve - consistency with other noun-based arguments. - .. versionadded:: 18.1.0 - ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. - .. versionadded:: 18.2.0 *kw_only* - .. versionchanged:: 19.2.0 *convert* keyword argument removed. - .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. - .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. - .. versionadded:: 19.2.0 *eq* and *order* - .. versionadded:: 20.1.0 *on_setattr* - .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 - .. versionchanged:: 21.1.0 - *eq*, *order*, and *cmp* also accept a custom callable - .. versionchanged:: 21.1.0 *cmp* undeprecated - .. versionadded:: 22.2.0 *alias* - .. versionchanged:: 25.4.0 - *kw_only* can now be None, and its default is also changed from False to - None. - """ - eq, eq_key, order, order_key = _determine_attrib_eq_order( - cmp, eq, order, True - ) - - if hash is not None and hash is not True and hash is not False: - msg = "Invalid value for hash. Must be True, False, or None." - raise TypeError(msg) - - if factory is not None: - if default is not NOTHING: - msg = ( - "The `default` and `factory` arguments are mutually exclusive." - ) - raise ValueError(msg) - if not callable(factory): - msg = "The `factory` argument must be a callable." - raise ValueError(msg) - default = Factory(factory) - - if metadata is None: - metadata = {} - - # Apply syntactic sugar by auto-wrapping. - if isinstance(on_setattr, (list, tuple)): - on_setattr = setters.pipe(*on_setattr) - - if validator and isinstance(validator, (list, tuple)): - validator = and_(*validator) - - if converter and isinstance(converter, (list, tuple)): - converter = pipe(*converter) - - return _CountingAttr( - default=default, - validator=validator, - repr=repr, - cmp=None, - hash=hash, - init=init, - converter=converter, - metadata=metadata, - type=type, - kw_only=kw_only, - eq=eq, - eq_key=eq_key, - order=order, - order_key=order_key, - on_setattr=on_setattr, - alias=alias, - ) - - -def _compile_and_eval( - script: str, - globs: dict[str, Any] | None, - locs: Mapping[str, object] | None = None, - filename: str = "", -) -> None: - """ - Evaluate the script with the given global (globs) and local (locs) - variables. - """ - bytecode = compile(script, filename, "exec") - eval(bytecode, globs, locs) - - -def _linecache_and_compile( - script: str, - filename: str, - globs: dict[str, Any] | None, - locals: Mapping[str, object] | None = None, -) -> dict[str, Any]: - """ - Cache the script with _linecache_, compile it and return the _locals_. - """ - - locs = {} if locals is None else locals - - # In order of debuggers like PDB being able to step through the code, - # we add a fake linecache entry. - count = 1 - base_filename = filename - while True: - linecache_tuple = ( - len(script), - None, - script.splitlines(True), - filename, - ) - old_val = linecache.cache.setdefault(filename, linecache_tuple) - if old_val == linecache_tuple: - break - - filename = f"{base_filename[:-1]}-{count}>" - count += 1 - - _compile_and_eval(script, globs, locs, filename) - - return locs - - -def _make_attr_tuple_class(cls_name: str, attr_names: list[str]) -> type: - """ - Create a tuple subclass to hold `Attribute`s for an `attrs` class. - - The subclass is a bare tuple with properties for names. - - class MyClassAttributes(tuple): - __slots__ = () - x = property(itemgetter(0)) - """ - attr_class_name = f"{cls_name}Attributes" - body = {} - for i, attr_name in enumerate(attr_names): - - def getter(self, i=i): - return self[i] - - body[attr_name] = property(getter) - return type(attr_class_name, (tuple,), body) - - -# Tuple class for extracted attributes from a class definition. -# `base_attrs` is a subset of `attrs`. -class _Attributes(NamedTuple): - attrs: type - base_attrs: list[Attribute] - base_attrs_map: dict[str, type] - - -def _is_class_var(annot): - """ - Check whether *annot* is a typing.ClassVar. - - The string comparison hack is used to avoid evaluating all string - annotations which would put attrs-based classes at a performance - disadvantage compared to plain old classes. - """ - annot = str(annot) - - # Annotation can be quoted. - if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): - annot = annot[1:-1] - - return annot.startswith(_CLASSVAR_PREFIXES) - - -def _has_own_attribute(cls, attrib_name): - """ - Check whether *cls* defines *attrib_name* (and doesn't just inherit it). - """ - return attrib_name in cls.__dict__ - - -def _collect_base_attrs( - cls, taken_attr_names -) -> tuple[list[Attribute], dict[str, type]]: - """ - Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. - """ - base_attrs = [] - base_attr_map = {} # A dictionary of base attrs to their classes. - - # Traverse the MRO and collect attributes. - for base_cls in reversed(cls.__mro__[1:-1]): - for a in getattr(base_cls, "__attrs_attrs__", []): - if a.inherited or a.name in taken_attr_names: - continue - - a = a.evolve(inherited=True) # noqa: PLW2901 - base_attrs.append(a) - base_attr_map[a.name] = base_cls - - # For each name, only keep the freshest definition i.e. the furthest at the - # back. base_attr_map is fine because it gets overwritten with every new - # instance. - filtered = [] - seen = set() - for a in reversed(base_attrs): - if a.name in seen: - continue - filtered.insert(0, a) - seen.add(a.name) - - return filtered, base_attr_map - - -def _collect_base_attrs_broken(cls, taken_attr_names): - """ - Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. - - N.B. *taken_attr_names* will be mutated. - - Adhere to the old incorrect behavior. - - Notably it collects from the front and considers inherited attributes which - leads to the buggy behavior reported in #428. - """ - base_attrs = [] - base_attr_map = {} # A dictionary of base attrs to their classes. - - # Traverse the MRO and collect attributes. - for base_cls in cls.__mro__[1:-1]: - for a in getattr(base_cls, "__attrs_attrs__", []): - if a.name in taken_attr_names: - continue - - a = a.evolve(inherited=True) # noqa: PLW2901 - taken_attr_names.add(a.name) - base_attrs.append(a) - base_attr_map[a.name] = base_cls - - return base_attrs, base_attr_map - - -def _transform_attrs( - cls, - these, - auto_attribs, - kw_only, - collect_by_mro, - field_transformer, -) -> _Attributes: - """ - Transform all `_CountingAttr`s on a class into `Attribute`s. - - If *these* is passed, use that and don't look for them on the class. - - If *collect_by_mro* is True, collect them in the correct MRO order, - otherwise use the old -- incorrect -- order. See #428. - - Return an `_Attributes`. - """ - cd = cls.__dict__ - anns = _get_annotations(cls) - - if these is not None: - ca_list = list(these.items()) - elif auto_attribs is True: - ca_names = { - name - for name, attr in cd.items() - if attr.__class__ is _CountingAttr - } - ca_list = [] - annot_names = set() - for attr_name, type in anns.items(): - if _is_class_var(type): - continue - annot_names.add(attr_name) - a = cd.get(attr_name, NOTHING) - - if a.__class__ is not _CountingAttr: - a = attrib(a) - ca_list.append((attr_name, a)) - - unannotated = ca_names - annot_names - if unannotated: - raise UnannotatedAttributeError( - "The following `attr.ib`s lack a type annotation: " - + ", ".join( - sorted(unannotated, key=lambda n: cd.get(n).counter) - ) - + "." - ) - else: - ca_list = sorted( - ( - (name, attr) - for name, attr in cd.items() - if attr.__class__ is _CountingAttr - ), - key=lambda e: e[1].counter, - ) - - fca = Attribute.from_counting_attr - no = ClassProps.KeywordOnly.NO - own_attrs = [ - fca( - attr_name, - ca, - kw_only is not no, - anns.get(attr_name), - ) - for attr_name, ca in ca_list - ] - - if collect_by_mro: - base_attrs, base_attr_map = _collect_base_attrs( - cls, {a.name for a in own_attrs} - ) - else: - base_attrs, base_attr_map = _collect_base_attrs_broken( - cls, {a.name for a in own_attrs} - ) - - if kw_only is ClassProps.KeywordOnly.FORCE: - own_attrs = [a.evolve(kw_only=True) for a in own_attrs] - base_attrs = [a.evolve(kw_only=True) for a in base_attrs] - - attrs = base_attrs + own_attrs - - # Resolve default field alias before executing field_transformer, so that - # the transformer receives fully populated Attribute objects with usable - # alias values. - for a in attrs: - if not a.alias: - # Evolve is very slow, so we hold our nose and do it dirty. - _OBJ_SETATTR.__get__(a)("alias", _default_init_alias_for(a.name)) - _OBJ_SETATTR.__get__(a)("alias_is_default", True) - - if field_transformer is not None: - attrs = tuple(field_transformer(cls, attrs)) - - # Check attr order after executing the field_transformer. - # Mandatory vs non-mandatory attr order only matters when they are part of - # the __init__ signature and when they aren't kw_only (which are moved to - # the end and can be mandatory or non-mandatory in any order, as they will - # be specified as keyword args anyway). Check the order of those attrs: - had_default = False - for a in (a for a in attrs if a.init is not False and a.kw_only is False): - if had_default is True and a.default is NOTHING: - msg = f"No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: {a!r}" - raise ValueError(msg) - - if had_default is False and a.default is not NOTHING: - had_default = True - - # Resolve default field alias for any new attributes that the - # field_transformer may have added without setting an alias. - for a in attrs: - if not a.alias: - _OBJ_SETATTR.__get__(a)("alias", _default_init_alias_for(a.name)) - _OBJ_SETATTR.__get__(a)("alias_is_default", True) - - # Create AttrsClass *after* applying the field_transformer since it may - # add or remove attributes! - attr_names = [a.name for a in attrs] - AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) - - return _Attributes(AttrsClass(attrs), base_attrs, base_attr_map) - - -def _make_cached_property_getattr(cached_properties, original_getattr, cls): - lines = [ - # Wrapped to get `__class__` into closure cell for super() - # (It will be replaced with the newly constructed class after construction). - "def wrapper(_cls):", - " __class__ = _cls", - " def __getattr__(self, item, cached_properties=cached_properties, original_getattr=original_getattr, _cached_setattr_get=_cached_setattr_get):", - " func = cached_properties.get(item)", - " if func is not None:", - " result = func(self)", - " _setter = _cached_setattr_get(self)", - " _setter(item, result)", - " return result", - ] - if original_getattr is not None: - lines.append( - " return original_getattr(self, item)", - ) - else: - lines.extend( - [ - " try:", - " return super().__getattribute__(item)", - " except AttributeError:", - " if not hasattr(super(), '__getattr__'):", - " raise", - " return super().__getattr__(item)", - " original_error = f\"'{self.__class__.__name__}' object has no attribute '{item}'\"", - " raise AttributeError(original_error)", - ] - ) - - lines.extend( - [ - " return __getattr__", - "__getattr__ = wrapper(_cls)", - ] - ) - - unique_filename = _generate_unique_filename(cls, "getattr") - - glob = { - "cached_properties": cached_properties, - "_cached_setattr_get": _OBJ_SETATTR.__get__, - "original_getattr": original_getattr, - } - - return _linecache_and_compile( - "\n".join(lines), unique_filename, glob, locals={"_cls": cls} - )["__getattr__"] - - -def _frozen_setattrs(self, name, value): - """ - Attached to frozen classes as __setattr__. - """ - if isinstance(self, BaseException) and name in ( - "__cause__", - "__context__", - "__traceback__", - "__suppress_context__", - "__notes__", - ): - BaseException.__setattr__(self, name, value) - return - - raise FrozenInstanceError - - -def _frozen_delattrs(self, name): - """ - Attached to frozen classes as __delattr__. - """ - if isinstance(self, BaseException) and name == "__notes__": - BaseException.__delattr__(self, name) - return - - raise FrozenInstanceError - - -def evolve(*args, **changes): - """ - Create a new instance, based on the first positional argument with - *changes* applied. - - .. tip:: - - On Python 3.13 and later, you can also use `copy.replace` instead. - - Args: - - inst: - Instance of a class with *attrs* attributes. *inst* must be passed - as a positional argument. - - changes: - Keyword changes in the new copy. - - Returns: - A copy of inst with *changes* incorporated. - - Raises: - TypeError: - If *attr_name* couldn't be found in the class ``__init__``. - - attrs.exceptions.NotAnAttrsClassError: - If *cls* is not an *attrs* class. - - .. versionadded:: 17.1.0 - .. deprecated:: 23.1.0 - It is now deprecated to pass the instance using the keyword argument - *inst*. It will raise a warning until at least April 2024, after which - it will become an error. Always pass the instance as a positional - argument. - .. versionchanged:: 24.1.0 - *inst* can't be passed as a keyword argument anymore. - """ - try: - (inst,) = args - except ValueError: - msg = ( - f"evolve() takes 1 positional argument, but {len(args)} were given" - ) - raise TypeError(msg) from None - - cls = inst.__class__ - attrs = fields(cls) - for a in attrs: - if not a.init: - continue - attr_name = a.name # To deal with private attributes. - init_name = a.alias - if init_name not in changes: - changes[init_name] = getattr(inst, attr_name) - - return cls(**changes) - - -class _ClassBuilder: - """ - Iteratively build *one* class. - """ - - __slots__ = ( - "_add_method_dunders", - "_attr_names", - "_attrs", - "_base_attr_map", - "_base_names", - "_cache_hash", - "_cls", - "_cls_dict", - "_delete_attribs", - "_frozen", - "_has_custom_setattr", - "_has_post_init", - "_has_pre_init", - "_is_exc", - "_on_setattr", - "_pre_init_has_args", - "_repr_added", - "_script_snippets", - "_slots", - "_weakref_slot", - "_wrote_own_setattr", - ) - - def __init__( - self, - cls: type, - these, - auto_attribs: bool, - props: ClassProps, - has_custom_setattr: bool, - ): - attrs, base_attrs, base_map = _transform_attrs( - cls, - these, - auto_attribs, - props.kw_only, - props.collected_fields_by_mro, - props.field_transformer, - ) - - self._cls = cls - self._cls_dict = dict(cls.__dict__) if props.is_slotted else {} - self._attrs = attrs - self._base_names = {a.name for a in base_attrs} - self._base_attr_map = base_map - self._attr_names = tuple(a.name for a in attrs) - self._slots = props.is_slotted - self._frozen = props.is_frozen - self._weakref_slot = props.has_weakref_slot - self._cache_hash = ( - props.hashability is ClassProps.Hashability.HASHABLE_CACHED - ) - self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) - self._pre_init_has_args = False - if self._has_pre_init: - # Check if the pre init method has more arguments than just `self` - # We want to pass arguments if pre init expects arguments - pre_init_func = cls.__attrs_pre_init__ - pre_init_signature = inspect.signature(pre_init_func) - self._pre_init_has_args = len(pre_init_signature.parameters) > 1 - self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) - self._delete_attribs = not bool(these) - self._is_exc = props.is_exception - self._on_setattr = props.on_setattr_hook - - self._has_custom_setattr = has_custom_setattr - self._wrote_own_setattr = False - - self._cls_dict["__attrs_attrs__"] = self._attrs - self._cls_dict["__attrs_props__"] = props - - if props.is_frozen: - self._cls_dict["__setattr__"] = _frozen_setattrs - self._cls_dict["__delattr__"] = _frozen_delattrs - - self._wrote_own_setattr = True - elif self._on_setattr in ( - _DEFAULT_ON_SETATTR, - setters.validate, - setters.convert, - ): - has_validator = has_converter = False - for a in attrs: - if a.validator is not None: - has_validator = True - if a.converter is not None: - has_converter = True - - if has_validator and has_converter: - break - if ( - ( - self._on_setattr == _DEFAULT_ON_SETATTR - and not (has_validator or has_converter) - ) - or (self._on_setattr == setters.validate and not has_validator) - or (self._on_setattr == setters.convert and not has_converter) - ): - # If class-level on_setattr is set to convert + validate, but - # there's no field to convert or validate, pretend like there's - # no on_setattr. - self._on_setattr = None - - if props.added_pickling: - ( - self._cls_dict["__getstate__"], - self._cls_dict["__setstate__"], - ) = self._make_getstate_setstate() - - # tuples of script, globs, hook - self._script_snippets: list[ - tuple[str, dict, Callable[[dict, dict], Any]] - ] = [] - self._repr_added = False - - # We want to only do this check once; in 99.9% of cases these - # exist. - if not hasattr(self._cls, "__module__") or not hasattr( - self._cls, "__qualname__" - ): - self._add_method_dunders = self._add_method_dunders_safe - else: - self._add_method_dunders = self._add_method_dunders_unsafe - - def __repr__(self): - return f"<_ClassBuilder(cls={self._cls.__name__})>" - - def _eval_snippets(self) -> None: - """ - Evaluate any registered snippets in one go. - """ - script = "\n".join([snippet[0] for snippet in self._script_snippets]) - globs = {} - for _, snippet_globs, _ in self._script_snippets: - globs.update(snippet_globs) - - locs = _linecache_and_compile( - script, - _generate_unique_filename(self._cls, "methods"), - globs, - ) - - for _, _, hook in self._script_snippets: - hook(self._cls_dict, locs) - - def build_class(self): - """ - Finalize class based on the accumulated configuration. - - Builder cannot be used after calling this method. - """ - self._eval_snippets() - if self._slots is True: - cls = self._create_slots_class() - self._cls.__attrs_base_of_slotted__ = weakref.ref(cls) - else: - cls = self._patch_original_class() - if PY_3_10_PLUS: - cls = abc.update_abstractmethods(cls) - - # The method gets only called if it's not inherited from a base class. - # _has_own_attribute does NOT work properly for classmethods. - if ( - getattr(cls, "__attrs_init_subclass__", None) - and "__attrs_init_subclass__" not in cls.__dict__ - ): - cls.__attrs_init_subclass__() - - return cls - - def _patch_original_class(self): - """ - Apply accumulated methods and return the class. - """ - cls = self._cls - base_names = self._base_names - - # Clean class of attribute definitions (`attr.ib()`s). - if self._delete_attribs: - for name in self._attr_names: - if ( - name not in base_names - and getattr(cls, name, _SENTINEL) is not _SENTINEL - ): - # An AttributeError can happen if a base class defines a - # class variable and we want to set an attribute with the - # same name by using only a type annotation. - with contextlib.suppress(AttributeError): - delattr(cls, name) - - # Attach our dunder methods. - for name, value in self._cls_dict.items(): - setattr(cls, name, value) - - # If we've inherited an attrs __setattr__ and don't write our own, - # reset it to object's. - if not self._wrote_own_setattr and getattr( - cls, "__attrs_own_setattr__", False - ): - cls.__attrs_own_setattr__ = False - - if not self._has_custom_setattr: - cls.__setattr__ = _OBJ_SETATTR - - return cls - - def _create_slots_class(self): - """ - Build and return a new class with a `__slots__` attribute. - """ - cd = { - k: v - for k, v in self._cls_dict.items() - if k not in (*tuple(self._attr_names), "__dict__", "__weakref__") - } - - # 3.14.0rc2+ - if hasattr(sys, "_clear_type_descriptors"): - sys._clear_type_descriptors(self._cls) - - # If our class doesn't have its own implementation of __setattr__ - # (either from the user or by us), check the bases, if one of them has - # an attrs-made __setattr__, that needs to be reset. We don't walk the - # MRO because we only care about our immediate base classes. - # XXX: This can be confused by subclassing a slotted attrs class with - # XXX: a non-attrs class and subclass the resulting class with an attrs - # XXX: class. See `test_slotted_confused` for details. For now that's - # XXX: OK with us. - if not self._wrote_own_setattr: - cd["__attrs_own_setattr__"] = False - - if not self._has_custom_setattr: - for base_cls in self._cls.__bases__: - if base_cls.__dict__.get("__attrs_own_setattr__", False): - cd["__setattr__"] = _OBJ_SETATTR - break - - # Traverse the MRO to collect existing slots - # and check for an existing __weakref__. - existing_slots = {} - weakref_inherited = False - for base_cls in self._cls.__mro__[1:-1]: - if base_cls.__dict__.get("__weakref__", None) is not None: - weakref_inherited = True - existing_slots.update( - { - name: getattr(base_cls, name) - for name in getattr(base_cls, "__slots__", []) - } - ) - - base_names = set(self._base_names) - - names = self._attr_names - if ( - self._weakref_slot - and "__weakref__" not in getattr(self._cls, "__slots__", ()) - and "__weakref__" not in names - and not weakref_inherited - ): - names += ("__weakref__",) - - cached_properties = { - name: cached_prop.func - for name, cached_prop in cd.items() - if isinstance(cached_prop, cached_property) - } - - # Collect methods with a `__class__` reference that are shadowed in the new class. - # To know to update them. - additional_closure_functions_to_update = [] - if cached_properties: - class_annotations = _get_annotations(self._cls) - for name, func in cached_properties.items(): - # Add cached properties to names for slotting. - names += (name,) - # Clear out function from class to avoid clashing. - del cd[name] - additional_closure_functions_to_update.append(func) - annotation = inspect.signature(func).return_annotation - if annotation is not inspect.Parameter.empty: - class_annotations[name] = annotation - - original_getattr = cd.get("__getattr__") - if original_getattr is not None: - additional_closure_functions_to_update.append(original_getattr) - - cd["__getattr__"] = _make_cached_property_getattr( - cached_properties, original_getattr, self._cls - ) - - # We only add the names of attributes that aren't inherited. - # Setting __slots__ to inherited attributes wastes memory. - slot_names = [name for name in names if name not in base_names] - - # There are slots for attributes from current class - # that are defined in parent classes. - # As their descriptors may be overridden by a child class, - # we collect them here and update the class dict - reused_slots = { - slot: slot_descriptor - for slot, slot_descriptor in existing_slots.items() - if slot in slot_names - } - slot_names = [name for name in slot_names if name not in reused_slots] - cd.update(reused_slots) - if self._cache_hash: - slot_names.append(_HASH_CACHE_FIELD) - - cd["__slots__"] = tuple(slot_names) - - cd["__qualname__"] = self._cls.__qualname__ - - # Create new class based on old class and our methods. - cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd) - - # The following is a fix for - # . - # If a method mentions `__class__` or uses the no-arg super(), the - # compiler will bake a reference to the class in the method itself - # as `method.__closure__`. Since we replace the class with a - # clone, we rewrite these references so it keeps working. - for item in itertools.chain( - cls.__dict__.values(), additional_closure_functions_to_update - ): - if isinstance(item, (classmethod, staticmethod)): - # Class- and staticmethods hide their functions inside. - # These might need to be rewritten as well. - closure_cells = getattr(item.__func__, "__closure__", None) - elif isinstance(item, property): - # Workaround for property `super()` shortcut (PY3-only). - # There is no universal way for other descriptors. - closure_cells = getattr(item.fget, "__closure__", None) - else: - closure_cells = getattr(item, "__closure__", None) - - if not closure_cells: # Catch None or the empty list. - continue - for cell in closure_cells: - try: - match = cell.cell_contents is self._cls - except ValueError: # noqa: PERF203 - # ValueError: Cell is empty - pass - else: - if match: - cell.cell_contents = cls - return cls - - def add_repr(self, ns): - script, globs = _make_repr_script(self._attrs, ns) - - def _attach_repr(cls_dict, globs): - cls_dict["__repr__"] = self._add_method_dunders(globs["__repr__"]) - - self._script_snippets.append((script, globs, _attach_repr)) - self._repr_added = True - return self - - def add_str(self): - if not self._repr_added: - msg = "__str__ can only be generated if a __repr__ exists." - raise ValueError(msg) - - def __str__(self): - return self.__repr__() - - self._cls_dict["__str__"] = self._add_method_dunders(__str__) - return self - - def _make_getstate_setstate(self): - """ - Create custom __setstate__ and __getstate__ methods. - """ - # __weakref__ is not writable. - state_attr_names = tuple( - an for an in self._attr_names if an != "__weakref__" - ) - - def slots_getstate(self): - """ - Automatically created by attrs. - """ - return {name: getattr(self, name) for name in state_attr_names} - - hash_caching_enabled = self._cache_hash - - def slots_setstate(self, state): - """ - Automatically created by attrs. - """ - __bound_setattr = _OBJ_SETATTR.__get__(self) - if isinstance(state, tuple): - # Backward compatibility with attrs instances pickled with - # attrs versions before v22.2.0 which stored tuples. - for name, value in zip(state_attr_names, state): - __bound_setattr(name, value) - else: - for name in state_attr_names: - if name in state: - __bound_setattr(name, state[name]) - - # The hash code cache is not included when the object is - # serialized, but it still needs to be initialized to None to - # indicate that the first call to __hash__ should be a cache - # miss. - if hash_caching_enabled: - __bound_setattr(_HASH_CACHE_FIELD, None) - - return slots_getstate, slots_setstate - - def make_unhashable(self): - self._cls_dict["__hash__"] = None - return self - - def add_hash(self): - script, globs = _make_hash_script( - self._cls, - self._attrs, - frozen=self._frozen, - cache_hash=self._cache_hash, - ) - - def attach_hash(cls_dict: dict, locs: dict) -> None: - cls_dict["__hash__"] = self._add_method_dunders(locs["__hash__"]) - - self._script_snippets.append((script, globs, attach_hash)) - - return self - - def add_init(self): - script, globs, annotations = _make_init_script( - self._cls, - self._attrs, - self._has_pre_init, - self._pre_init_has_args, - self._has_post_init, - self._frozen, - self._slots, - self._cache_hash, - self._base_attr_map, - self._is_exc, - self._on_setattr, - attrs_init=False, - ) - - def _attach_init(cls_dict, globs): - init = globs["__init__"] - init.__annotations__ = annotations - cls_dict["__init__"] = self._add_method_dunders(init) - - self._script_snippets.append((script, globs, _attach_init)) - - return self - - def add_replace(self): - self._cls_dict["__replace__"] = self._add_method_dunders(evolve) - return self - - def add_match_args(self): - self._cls_dict["__match_args__"] = tuple( - field.name - for field in self._attrs - if field.init and not field.kw_only - ) - - def add_attrs_init(self): - script, globs, annotations = _make_init_script( - self._cls, - self._attrs, - self._has_pre_init, - self._pre_init_has_args, - self._has_post_init, - self._frozen, - self._slots, - self._cache_hash, - self._base_attr_map, - self._is_exc, - self._on_setattr, - attrs_init=True, - ) - - def _attach_attrs_init(cls_dict, globs): - init = globs["__attrs_init__"] - init.__annotations__ = annotations - cls_dict["__attrs_init__"] = self._add_method_dunders(init) - - self._script_snippets.append((script, globs, _attach_attrs_init)) - - return self - - def add_eq(self): - cd = self._cls_dict - - script, globs = _make_eq_script(self._attrs) - - def _attach_eq(cls_dict, globs): - cls_dict["__eq__"] = self._add_method_dunders(globs["__eq__"]) - - self._script_snippets.append((script, globs, _attach_eq)) - - cd["__ne__"] = __ne__ - - return self - - def add_order(self): - cd = self._cls_dict - - cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = ( - self._add_method_dunders(meth) - for meth in _make_order(self._cls, self._attrs) - ) - - return self - - def add_setattr(self): - sa_attrs = {} - for a in self._attrs: - on_setattr = a.on_setattr or self._on_setattr - if on_setattr and on_setattr is not setters.NO_OP: - sa_attrs[a.name] = a, on_setattr - - if not sa_attrs: - return self - - if self._has_custom_setattr: - # We need to write a __setattr__ but there already is one! - msg = "Can't combine custom __setattr__ with on_setattr hooks." - raise ValueError(msg) - - # docstring comes from _add_method_dunders - def __setattr__(self, name, val): - try: - a, hook = sa_attrs[name] - except KeyError: - nval = val - else: - nval = hook(self, a, val) - - _OBJ_SETATTR(self, name, nval) - - self._cls_dict["__attrs_own_setattr__"] = True - self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__) - self._wrote_own_setattr = True - - return self - - def _add_method_dunders_unsafe(self, method: Callable) -> Callable: - """ - Add __module__ and __qualname__ to a *method*. - """ - method.__module__ = self._cls.__module__ - - method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}" - - method.__doc__ = ( - f"Method generated by attrs for class {self._cls.__qualname__}." - ) - - return method - - def _add_method_dunders_safe(self, method: Callable) -> Callable: - """ - Add __module__ and __qualname__ to a *method* if possible. - """ - with contextlib.suppress(AttributeError): - method.__module__ = self._cls.__module__ - - with contextlib.suppress(AttributeError): - method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}" - - with contextlib.suppress(AttributeError): - method.__doc__ = f"Method generated by attrs for class {self._cls.__qualname__}." - - return method - - -def _determine_attrs_eq_order(cmp, eq, order, default_eq): - """ - Validate the combination of *cmp*, *eq*, and *order*. Derive the effective - values of eq and order. If *eq* is None, set it to *default_eq*. - """ - if cmp is not None and any((eq is not None, order is not None)): - msg = "Don't mix `cmp` with `eq' and `order`." - raise ValueError(msg) - - # cmp takes precedence due to bw-compatibility. - if cmp is not None: - return cmp, cmp - - # If left None, equality is set to the specified default and ordering - # mirrors equality. - if eq is None: - eq = default_eq - - if order is None: - order = eq - - if eq is False and order is True: - msg = "`order` can only be True if `eq` is True too." - raise ValueError(msg) - - return eq, order - - -def _determine_attrib_eq_order(cmp, eq, order, default_eq): - """ - Validate the combination of *cmp*, *eq*, and *order*. Derive the effective - values of eq and order. If *eq* is None, set it to *default_eq*. - """ - if cmp is not None and any((eq is not None, order is not None)): - msg = "Don't mix `cmp` with `eq' and `order`." - raise ValueError(msg) - - def decide_callable_or_boolean(value): - """ - Decide whether a key function is used. - """ - if callable(value): - value, key = True, value - else: - key = None - return value, key - - # cmp takes precedence due to bw-compatibility. - if cmp is not None: - cmp, cmp_key = decide_callable_or_boolean(cmp) - return cmp, cmp_key, cmp, cmp_key - - # If left None, equality is set to the specified default and ordering - # mirrors equality. - if eq is None: - eq, eq_key = default_eq, None - else: - eq, eq_key = decide_callable_or_boolean(eq) - - if order is None: - order, order_key = eq, eq_key - else: - order, order_key = decide_callable_or_boolean(order) - - if eq is False and order is True: - msg = "`order` can only be True if `eq` is True too." - raise ValueError(msg) - - return eq, eq_key, order, order_key - - -def _determine_whether_to_implement( - cls, flag, auto_detect, dunders, default=True -): - """ - Check whether we should implement a set of methods for *cls*. - - *flag* is the argument passed into @attr.s like 'init', *auto_detect* the - same as passed into @attr.s and *dunders* is a tuple of attribute names - whose presence signal that the user has implemented it themselves. - - Return *default* if no reason for either for or against is found. - """ - if flag is True or flag is False: - return flag - - if flag is None and auto_detect is False: - return default - - # Logically, flag is None and auto_detect is True here. - for dunder in dunders: - if _has_own_attribute(cls, dunder): - return False - - return default - - -def attrs( - maybe_cls=None, - these=None, - repr_ns=None, - repr=None, - cmp=None, - hash=None, - init=None, - slots=False, - frozen=False, - weakref_slot=True, - str=False, - auto_attribs=False, - kw_only=False, - cache_hash=False, - auto_exc=False, - eq=None, - order=None, - auto_detect=False, - collect_by_mro=False, - getstate_setstate=None, - on_setattr=None, - field_transformer=None, - match_args=True, - unsafe_hash=None, - force_kw_only=True, -): - r""" - A class decorator that adds :term:`dunder methods` according to the - specified attributes using `attr.ib` or the *these* argument. - - Consider using `attrs.define` / `attrs.frozen` in new code (``attr.s`` will - *never* go away, though). - - Args: - repr_ns (str): - When using nested classes, there was no way in Python 2 to - automatically detect that. This argument allows to set a custom - name for a more meaningful ``repr`` output. This argument is - pointless in Python 3 and is therefore deprecated. - - .. caution:: - Refer to `attrs.define` for the rest of the parameters, but note that they - can have different defaults. - - Notably, leaving *on_setattr* as `None` will **not** add any hooks. - - .. versionadded:: 16.0.0 *slots* - .. versionadded:: 16.1.0 *frozen* - .. versionadded:: 16.3.0 *str* - .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``. - .. versionchanged:: 17.1.0 - *hash* supports `None` as value which is also the default now. - .. versionadded:: 17.3.0 *auto_attribs* - .. versionchanged:: 18.1.0 - If *these* is passed, no attributes are deleted from the class body. - .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained. - .. versionadded:: 18.2.0 *weakref_slot* - .. deprecated:: 18.2.0 - ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a - `DeprecationWarning` if the classes compared are subclasses of - each other. ``__eq`` and ``__ne__`` never tried to compared subclasses - to each other. - .. versionchanged:: 19.2.0 - ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider - subclasses comparable anymore. - .. versionadded:: 18.2.0 *kw_only* - .. versionadded:: 18.2.0 *cache_hash* - .. versionadded:: 19.1.0 *auto_exc* - .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. - .. versionadded:: 19.2.0 *eq* and *order* - .. versionadded:: 20.1.0 *auto_detect* - .. versionadded:: 20.1.0 *collect_by_mro* - .. versionadded:: 20.1.0 *getstate_setstate* - .. versionadded:: 20.1.0 *on_setattr* - .. versionadded:: 20.3.0 *field_transformer* - .. versionchanged:: 21.1.0 - ``init=False`` injects ``__attrs_init__`` - .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` - .. versionchanged:: 21.1.0 *cmp* undeprecated - .. versionadded:: 21.3.0 *match_args* - .. versionadded:: 22.2.0 - *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance). - .. deprecated:: 24.1.0 *repr_ns* - .. versionchanged:: 24.1.0 - Instances are not compared as tuples of attributes anymore, but using a - big ``and`` condition. This is faster and has more correct behavior for - uncomparable values like `math.nan`. - .. versionadded:: 24.1.0 - If a class has an *inherited* classmethod called - ``__attrs_init_subclass__``, it is executed after the class is created. - .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*. - .. versionchanged:: 25.4.0 - *kw_only* now only applies to attributes defined in the current class, - and respects attribute-level ``kw_only=False`` settings. - .. versionadded:: 25.4.0 *force_kw_only* - """ - if repr_ns is not None: - import warnings - - warnings.warn( - DeprecationWarning( - "The `repr_ns` argument is deprecated and will be removed in or after August 2025." - ), - stacklevel=2, - ) - - eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) - - # unsafe_hash takes precedence due to PEP 681. - if unsafe_hash is not None: - hash = unsafe_hash - - if isinstance(on_setattr, (list, tuple)): - on_setattr = setters.pipe(*on_setattr) - - def wrap(cls): - nonlocal hash - is_frozen = frozen or _has_frozen_base_class(cls) - is_exc = auto_exc is True and issubclass(cls, BaseException) - has_own_setattr = auto_detect and _has_own_attribute( - cls, "__setattr__" - ) - - if has_own_setattr and is_frozen: - msg = "Can't freeze a class with a custom __setattr__." - raise ValueError(msg) - - eq = not is_exc and _determine_whether_to_implement( - cls, eq_, auto_detect, ("__eq__", "__ne__") - ) - - Hashability = ClassProps.Hashability - - if is_exc: - hashability = Hashability.LEAVE_ALONE - elif hash is True: - hashability = ( - Hashability.HASHABLE_CACHED - if cache_hash - else Hashability.HASHABLE - ) - elif hash is False: - hashability = Hashability.LEAVE_ALONE - elif hash is None: - if auto_detect is True and _has_own_attribute(cls, "__hash__"): - hashability = Hashability.LEAVE_ALONE - elif eq is True and is_frozen is True: - hashability = ( - Hashability.HASHABLE_CACHED - if cache_hash - else Hashability.HASHABLE - ) - elif eq is False: - hashability = Hashability.LEAVE_ALONE - else: - hashability = Hashability.UNHASHABLE - else: - msg = "Invalid value for hash. Must be True, False, or None." - raise TypeError(msg) - - KeywordOnly = ClassProps.KeywordOnly - if kw_only: - kwo = KeywordOnly.FORCE if force_kw_only else KeywordOnly.YES - else: - kwo = KeywordOnly.NO - - props = ClassProps( - is_exception=is_exc, - is_frozen=is_frozen, - is_slotted=slots, - collected_fields_by_mro=collect_by_mro, - added_init=_determine_whether_to_implement( - cls, init, auto_detect, ("__init__",) - ), - added_repr=_determine_whether_to_implement( - cls, repr, auto_detect, ("__repr__",) - ), - added_eq=eq, - added_ordering=not is_exc - and _determine_whether_to_implement( - cls, - order_, - auto_detect, - ("__lt__", "__le__", "__gt__", "__ge__"), - ), - hashability=hashability, - added_match_args=match_args, - kw_only=kwo, - has_weakref_slot=weakref_slot, - added_str=str, - added_pickling=_determine_whether_to_implement( - cls, - getstate_setstate, - auto_detect, - ("__getstate__", "__setstate__"), - default=slots, - ), - on_setattr_hook=on_setattr, - field_transformer=field_transformer, - ) - - if not props.is_hashable and cache_hash: - msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled." - raise TypeError(msg) - - builder = _ClassBuilder( - cls, - these, - auto_attribs=auto_attribs, - props=props, - has_custom_setattr=has_own_setattr, - ) - - if props.added_repr: - builder.add_repr(repr_ns) - - if props.added_str: - builder.add_str() - - if props.added_eq: - builder.add_eq() - if props.added_ordering: - builder.add_order() - - if not frozen: - builder.add_setattr() - - if props.is_hashable: - builder.add_hash() - elif props.hashability is Hashability.UNHASHABLE: - builder.make_unhashable() - - if props.added_init: - builder.add_init() - else: - builder.add_attrs_init() - if cache_hash: - msg = "Invalid value for cache_hash. To use hash caching, init must be True." - raise TypeError(msg) - - if PY_3_13_PLUS and not _has_own_attribute(cls, "__replace__"): - builder.add_replace() - - if ( - PY_3_10_PLUS - and match_args - and not _has_own_attribute(cls, "__match_args__") - ): - builder.add_match_args() - - return builder.build_class() - - # maybe_cls's type depends on the usage of the decorator. It's a class - # if it's used as `@attrs` but `None` if used as `@attrs()`. - if maybe_cls is None: - return wrap - - return wrap(maybe_cls) - - -_attrs = attrs -""" -Internal alias so we can use it in functions that take an argument called -*attrs*. -""" - - -def _has_frozen_base_class(cls): - """ - Check whether *cls* has a frozen ancestor by looking at its - __setattr__. - """ - return cls.__setattr__ is _frozen_setattrs - - -def _generate_unique_filename(cls: type, func_name: str) -> str: - """ - Create a "filename" suitable for a function being generated. - """ - return ( - f"" - ) - - -def _make_hash_script( - cls: type, attrs: list[Attribute], frozen: bool, cache_hash: bool -) -> tuple[str, dict]: - attrs = tuple( - a for a in attrs if a.hash is True or (a.hash is None and a.eq is True) - ) - - tab = " " - - type_hash = hash(_generate_unique_filename(cls, "hash")) - # If eq is custom generated, we need to include the functions in globs - globs = {} - - hash_def = "def __hash__(self" - hash_func = "hash((" - closing_braces = "))" - if not cache_hash: - hash_def += "):" - else: - hash_def += ", *" - - hash_def += ", _cache_wrapper=__import__('attr._make')._make._CacheHashWrapper):" - hash_func = "_cache_wrapper(" + hash_func - closing_braces += ")" - - method_lines = [hash_def] - - def append_hash_computation_lines(prefix, indent): - """ - Generate the code for actually computing the hash code. - Below this will either be returned directly or used to compute - a value which is then cached, depending on the value of cache_hash - """ - - method_lines.extend( - [ - indent + prefix + hash_func, - indent + f" {type_hash},", - ] - ) - - for a in attrs: - if a.eq_key: - cmp_name = f"_{a.name}_key" - globs[cmp_name] = a.eq_key - method_lines.append( - indent + f" {cmp_name}(self.{a.name})," - ) - else: - method_lines.append(indent + f" self.{a.name},") - - method_lines.append(indent + " " + closing_braces) - - if cache_hash: - method_lines.append(tab + f"if self.{_HASH_CACHE_FIELD} is None:") - if frozen: - append_hash_computation_lines( - f"object.__setattr__(self, '{_HASH_CACHE_FIELD}', ", tab * 2 - ) - method_lines.append(tab * 2 + ")") # close __setattr__ - else: - append_hash_computation_lines( - f"self.{_HASH_CACHE_FIELD} = ", tab * 2 - ) - method_lines.append(tab + f"return self.{_HASH_CACHE_FIELD}") - else: - append_hash_computation_lines("return ", tab) - - script = "\n".join(method_lines) - return script, globs - - -def _add_hash(cls: type, attrs: list[Attribute]): - """ - Add a hash method to *cls*. - """ - script, globs = _make_hash_script( - cls, attrs, frozen=False, cache_hash=False - ) - _compile_and_eval( - script, globs, filename=_generate_unique_filename(cls, "__hash__") - ) - cls.__hash__ = globs["__hash__"] - return cls - - -def __ne__(self, other): - """ - Check equality and either forward a NotImplemented or - return the result negated. - """ - result = self.__eq__(other) - if result is NotImplemented: - return NotImplemented - - return not result - - -def _make_eq_script(attrs: list) -> tuple[str, dict]: - """ - Create __eq__ method for *cls* with *attrs*. - """ - attrs = [a for a in attrs if a.eq] - - lines = [ - "def __eq__(self, other):", - " if other.__class__ is not self.__class__:", - " return NotImplemented", - ] - - globs = {} - if attrs: - lines.append(" return (") - for a in attrs: - if a.eq_key: - cmp_name = f"_{a.name}_key" - # Add the key function to the global namespace - # of the evaluated function. - globs[cmp_name] = a.eq_key - lines.append( - f" {cmp_name}(self.{a.name}) == {cmp_name}(other.{a.name})" - ) - else: - lines.append(f" self.{a.name} == other.{a.name}") - if a is not attrs[-1]: - lines[-1] = f"{lines[-1]} and" - lines.append(" )") - else: - lines.append(" return True") - - script = "\n".join(lines) - - return script, globs - - -def _make_order(cls, attrs): - """ - Create ordering methods for *cls* with *attrs*. - """ - attrs = [a for a in attrs if a.order] - - def attrs_to_tuple(obj): - """ - Save us some typing. - """ - return tuple( - key(value) if key else value - for value, key in ( - (getattr(obj, a.name), a.order_key) for a in attrs - ) - ) - - def __lt__(self, other): - """ - Automatically created by attrs. - """ - if other.__class__ is self.__class__: - return attrs_to_tuple(self) < attrs_to_tuple(other) - - return NotImplemented - - def __le__(self, other): - """ - Automatically created by attrs. - """ - if other.__class__ is self.__class__: - return attrs_to_tuple(self) <= attrs_to_tuple(other) - - return NotImplemented - - def __gt__(self, other): - """ - Automatically created by attrs. - """ - if other.__class__ is self.__class__: - return attrs_to_tuple(self) > attrs_to_tuple(other) - - return NotImplemented - - def __ge__(self, other): - """ - Automatically created by attrs. - """ - if other.__class__ is self.__class__: - return attrs_to_tuple(self) >= attrs_to_tuple(other) - - return NotImplemented - - return __lt__, __le__, __gt__, __ge__ - - -def _add_eq(cls, attrs=None): - """ - Add equality methods to *cls* with *attrs*. - """ - if attrs is None: - attrs = cls.__attrs_attrs__ - - script, globs = _make_eq_script(attrs) - _compile_and_eval( - script, globs, filename=_generate_unique_filename(cls, "__eq__") - ) - cls.__eq__ = globs["__eq__"] - cls.__ne__ = __ne__ - - return cls - - -def _make_repr_script(attrs, ns) -> tuple[str, dict]: - """ - Create the source and globs for a __repr__ and return it. - """ - # Figure out which attributes to include, and which function to use to - # format them. The a.repr value can be either bool or a custom - # callable. - attr_names_with_reprs = tuple( - (a.name, (repr if a.repr is True else a.repr), a.init) - for a in attrs - if a.repr is not False - ) - globs = { - name + "_repr": r for name, r, _ in attr_names_with_reprs if r != repr - } - globs["_compat"] = _compat - globs["AttributeError"] = AttributeError - globs["NOTHING"] = NOTHING - attribute_fragments = [] - for name, r, i in attr_names_with_reprs: - accessor = ( - "self." + name if i else 'getattr(self, "' + name + '", NOTHING)' - ) - fragment = ( - "%s={%s!r}" % (name, accessor) - if r == repr - else "%s={%s_repr(%s)}" % (name, name, accessor) - ) - attribute_fragments.append(fragment) - repr_fragment = ", ".join(attribute_fragments) - - if ns is None: - cls_name_fragment = '{self.__class__.__qualname__.rsplit(">.", 1)[-1]}' - else: - cls_name_fragment = ns + ".{self.__class__.__name__}" - - lines = [ - "def __repr__(self):", - " try:", - " already_repring = _compat.repr_context.already_repring", - " except AttributeError:", - " already_repring = {id(self),}", - " _compat.repr_context.already_repring = already_repring", - " else:", - " if id(self) in already_repring:", - " return '...'", - " else:", - " already_repring.add(id(self))", - " try:", - f" return f'{cls_name_fragment}({repr_fragment})'", - " finally:", - " already_repring.remove(id(self))", - ] - - return "\n".join(lines), globs - - -def _add_repr(cls, ns=None, attrs=None): - """ - Add a repr method to *cls*. - """ - if attrs is None: - attrs = cls.__attrs_attrs__ - - script, globs = _make_repr_script(attrs, ns) - _compile_and_eval( - script, globs, filename=_generate_unique_filename(cls, "__repr__") - ) - cls.__repr__ = globs["__repr__"] - return cls - - -def fields(cls): - """ - Return the tuple of *attrs* attributes for a class or instance. - - The tuple also allows accessing the fields by their names (see below for - examples). - - Args: - cls (type): Class or instance to introspect. - - Raises: - TypeError: If *cls* is neither a class nor an *attrs* instance. - - attrs.exceptions.NotAnAttrsClassError: - If *cls* is not an *attrs* class. - - Returns: - tuple (with name accessors) of `attrs.Attribute` - - .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields - by name. - .. versionchanged:: 23.1.0 Add support for generic classes. - .. versionchanged:: 26.1.0 Add support for instances. - """ - generic_base = get_generic_base(cls) - - if generic_base is None and not isinstance(cls, type): - type_ = type(cls) - if getattr(type_, "__attrs_attrs__", None) is None: - msg = "Passed object must be a class or attrs instance." - raise TypeError(msg) - - return fields(type_) - - attrs = getattr(cls, "__attrs_attrs__", None) - - if attrs is None: - if generic_base is not None: - attrs = getattr(generic_base, "__attrs_attrs__", None) - if attrs is not None: - # Even though this is global state, stick it on here to speed - # it up. We rely on `cls` being cached for this to be - # efficient. - cls.__attrs_attrs__ = attrs - return attrs - msg = f"{cls!r} is not an attrs-decorated class." - raise NotAnAttrsClassError(msg) - - return attrs - - -def fields_dict(cls): - """ - Return an ordered dictionary of *attrs* attributes for a class, whose keys - are the attribute names. - - Args: - cls (type): Class to introspect. - - Raises: - TypeError: If *cls* is not a class. - - attrs.exceptions.NotAnAttrsClassError: - If *cls* is not an *attrs* class. - - Returns: - dict[str, attrs.Attribute]: Dict of attribute name to definition - - .. versionadded:: 18.1.0 - """ - if not isinstance(cls, type): - msg = "Passed object must be a class." - raise TypeError(msg) - attrs = getattr(cls, "__attrs_attrs__", None) - if attrs is None: - msg = f"{cls!r} is not an attrs-decorated class." - raise NotAnAttrsClassError(msg) - return {a.name: a for a in attrs} - - -def validate(inst): - """ - Validate all attributes on *inst* that have a validator. - - Leaves all exceptions through. - - Args: - inst: Instance of a class with *attrs* attributes. - """ - if _config._run_validators is False: - return - - for a in fields(inst.__class__): - v = a.validator - if v is not None: - v(inst, a, getattr(inst, a.name)) - - -def _is_slot_attr(a_name, base_attr_map): - """ - Check if the attribute name comes from a slot class. - """ - cls = base_attr_map.get(a_name) - return cls and "__slots__" in cls.__dict__ - - -def _make_init_script( - cls, - attrs, - pre_init, - pre_init_has_args, - post_init, - frozen, - slots, - cache_hash, - base_attr_map, - is_exc, - cls_on_setattr, - attrs_init, -) -> tuple[str, dict, dict]: - has_cls_on_setattr = ( - cls_on_setattr is not None and cls_on_setattr is not setters.NO_OP - ) - - if frozen and has_cls_on_setattr: - msg = "Frozen classes can't use on_setattr." - raise ValueError(msg) - - needs_cached_setattr = cache_hash or frozen - filtered_attrs = [] - attr_dict = {} - for a in attrs: - if not a.init and a.default is NOTHING: - continue - - filtered_attrs.append(a) - attr_dict[a.name] = a - - if a.on_setattr is not None: - if frozen is True and a.on_setattr is not setters.NO_OP: - msg = "Frozen classes can't use on_setattr." - raise ValueError(msg) - - needs_cached_setattr = True - elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP: - needs_cached_setattr = True - - script, globs, annotations = _attrs_to_init_script( - filtered_attrs, - frozen, - slots, - pre_init, - pre_init_has_args, - post_init, - cache_hash, - base_attr_map, - is_exc, - needs_cached_setattr, - has_cls_on_setattr, - "__attrs_init__" if attrs_init else "__init__", - ) - if cls.__module__ in sys.modules: - # This makes typing.get_type_hints(CLS.__init__) resolve string types. - globs.update(sys.modules[cls.__module__].__dict__) - - globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) - - if needs_cached_setattr: - # Save the lookup overhead in __init__ if we need to circumvent - # setattr hooks. - globs["_cached_setattr_get"] = _OBJ_SETATTR.__get__ - - return script, globs, annotations - - -def _setattr(attr_name: str, value_var: str, has_on_setattr: bool) -> str: - """ - Use the cached object.setattr to set *attr_name* to *value_var*. - """ - return f"_setattr('{attr_name}', {value_var})" - - -def _setattr_with_converter( - attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter -) -> str: - """ - Use the cached object.setattr to set *attr_name* to *value_var*, but run - its converter first. - """ - return f"_setattr('{attr_name}', {converter._fmt_converter_call(attr_name, value_var)})" - - -def _assign(attr_name: str, value: str, has_on_setattr: bool) -> str: - """ - Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise - relegate to _setattr. - """ - if has_on_setattr: - return _setattr(attr_name, value, True) - - return f"self.{attr_name} = {value}" - - -def _assign_with_converter( - attr_name: str, value_var: str, has_on_setattr: bool, converter: Converter -) -> str: - """ - Unless *attr_name* has an on_setattr hook, use normal assignment after - conversion. Otherwise relegate to _setattr_with_converter. - """ - if has_on_setattr: - return _setattr_with_converter(attr_name, value_var, True, converter) - - return f"self.{attr_name} = {converter._fmt_converter_call(attr_name, value_var)}" - - -def _determine_setters( - frozen: bool, slots: bool, base_attr_map: dict[str, type] -): - """ - Determine the correct setter functions based on whether a class is frozen - and/or slotted. - """ - if frozen is True: - if slots is True: - return (), _setattr, _setattr_with_converter - - # Dict frozen classes assign directly to __dict__. - # But only if the attribute doesn't come from an ancestor slot - # class. - # Note _inst_dict will be used again below if cache_hash is True - - def fmt_setter( - attr_name: str, value_var: str, has_on_setattr: bool - ) -> str: - if _is_slot_attr(attr_name, base_attr_map): - return _setattr(attr_name, value_var, has_on_setattr) - - return f"_inst_dict['{attr_name}'] = {value_var}" - - def fmt_setter_with_converter( - attr_name: str, - value_var: str, - has_on_setattr: bool, - converter: Converter, - ) -> str: - if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): - return _setattr_with_converter( - attr_name, value_var, has_on_setattr, converter - ) - - return f"_inst_dict['{attr_name}'] = {converter._fmt_converter_call(attr_name, value_var)}" - - return ( - ("_inst_dict = self.__dict__",), - fmt_setter, - fmt_setter_with_converter, - ) - - # Not frozen -- we can just assign directly. - return (), _assign, _assign_with_converter - - -def _attrs_to_init_script( - attrs: list[Attribute], - is_frozen: bool, - is_slotted: bool, - call_pre_init: bool, - pre_init_has_args: bool, - call_post_init: bool, - does_cache_hash: bool, - base_attr_map: dict[str, type], - is_exc: bool, - needs_cached_setattr: bool, - has_cls_on_setattr: bool, - method_name: str, -) -> tuple[str, dict, dict]: - """ - Return a script of an initializer for *attrs*, a dict of globals, and - annotations for the initializer. - - The globals are required by the generated script. - """ - lines = ["self.__attrs_pre_init__()"] if call_pre_init else [] - - if needs_cached_setattr: - lines.append( - # Circumvent the __setattr__ descriptor to save one lookup per - # assignment. Note _setattr will be used again below if - # does_cache_hash is True. - "_setattr = _cached_setattr_get(self)" - ) - - extra_lines, fmt_setter, fmt_setter_with_converter = _determine_setters( - is_frozen, is_slotted, base_attr_map - ) - lines.extend(extra_lines) - - args = [] # Parameters in the definition of __init__ - pre_init_args = [] # Parameters in the call to __attrs_pre_init__ - kw_only_args = [] # Used for both 'args' and 'pre_init_args' above - attrs_to_validate = [] - - # This is a dictionary of names to validator and converter callables. - # Injecting this into __init__ globals lets us avoid lookups. - names_for_globals = {} - annotations = {"return": None} - - for a in attrs: - if a.validator: - attrs_to_validate.append(a) - - attr_name = a.name - has_on_setattr = a.on_setattr is not None or ( - a.on_setattr is not setters.NO_OP and has_cls_on_setattr - ) - # a.alias is set to maybe-mangled attr_name in _ClassBuilder if not - # explicitly provided - arg_name = a.alias - - has_factory = isinstance(a.default, Factory) - maybe_self = "self" if has_factory and a.default.takes_self else "" - - if a.converter is not None and not isinstance(a.converter, Converter): - converter = Converter(a.converter) - else: - converter = a.converter - - if a.init is False: - if has_factory: - init_factory_name = _INIT_FACTORY_PAT % (a.name,) - if converter is not None: - lines.append( - fmt_setter_with_converter( - attr_name, - init_factory_name + f"({maybe_self})", - has_on_setattr, - converter, - ) - ) - names_for_globals[converter._get_global_name(a.name)] = ( - converter.converter - ) - else: - lines.append( - fmt_setter( - attr_name, - init_factory_name + f"({maybe_self})", - has_on_setattr, - ) - ) - names_for_globals[init_factory_name] = a.default.factory - elif converter is not None: - lines.append( - fmt_setter_with_converter( - attr_name, - f"attr_dict['{attr_name}'].default", - has_on_setattr, - converter, - ) - ) - names_for_globals[converter._get_global_name(a.name)] = ( - converter.converter - ) - else: - lines.append( - fmt_setter( - attr_name, - f"attr_dict['{attr_name}'].default", - has_on_setattr, - ) - ) - elif a.default is not NOTHING and not has_factory: - arg = f"{arg_name}=attr_dict['{attr_name}'].default" - if a.kw_only: - kw_only_args.append(arg) - else: - args.append(arg) - pre_init_args.append(arg_name) - - if converter is not None: - lines.append( - fmt_setter_with_converter( - attr_name, arg_name, has_on_setattr, converter - ) - ) - names_for_globals[converter._get_global_name(a.name)] = ( - converter.converter - ) - else: - lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) - - elif has_factory: - arg = f"{arg_name}=NOTHING" - if a.kw_only: - kw_only_args.append(arg) - else: - args.append(arg) - pre_init_args.append(arg_name) - lines.append(f"if {arg_name} is not NOTHING:") - - init_factory_name = _INIT_FACTORY_PAT % (a.name,) - if converter is not None: - lines.append( - " " - + fmt_setter_with_converter( - attr_name, arg_name, has_on_setattr, converter - ) - ) - lines.append("else:") - lines.append( - " " - + fmt_setter_with_converter( - attr_name, - init_factory_name + "(" + maybe_self + ")", - has_on_setattr, - converter, - ) - ) - names_for_globals[converter._get_global_name(a.name)] = ( - converter.converter - ) - else: - lines.append( - " " + fmt_setter(attr_name, arg_name, has_on_setattr) - ) - lines.append("else:") - lines.append( - " " - + fmt_setter( - attr_name, - init_factory_name + "(" + maybe_self + ")", - has_on_setattr, - ) - ) - names_for_globals[init_factory_name] = a.default.factory - else: - if a.kw_only: - kw_only_args.append(arg_name) - else: - args.append(arg_name) - pre_init_args.append(arg_name) - - if converter is not None: - lines.append( - fmt_setter_with_converter( - attr_name, arg_name, has_on_setattr, converter - ) - ) - names_for_globals[converter._get_global_name(a.name)] = ( - converter.converter - ) - else: - lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) - - if a.init is True: - if a.type is not None and converter is None: - annotations[arg_name] = a.type - elif converter is not None and converter._first_param_type: - # Use the type from the converter if present. - annotations[arg_name] = converter._first_param_type - - if attrs_to_validate: # we can skip this if there are no validators. - names_for_globals["_config"] = _config - lines.append("if _config._run_validators is True:") - for a in attrs_to_validate: - val_name = "__attr_validator_" + a.name - attr_name = "__attr_" + a.name - lines.append(f" {val_name}(self, {attr_name}, self.{a.name})") - names_for_globals[val_name] = a.validator - names_for_globals[attr_name] = a - - if call_post_init: - lines.append("self.__attrs_post_init__()") - - # Because this is set only after __attrs_post_init__ is called, a crash - # will result if post-init tries to access the hash code. This seemed - # preferable to setting this beforehand, in which case alteration to field - # values during post-init combined with post-init accessing the hash code - # would result in silent bugs. - if does_cache_hash: - if is_frozen: - if is_slotted: - init_hash_cache = f"_setattr('{_HASH_CACHE_FIELD}', None)" - else: - init_hash_cache = f"_inst_dict['{_HASH_CACHE_FIELD}'] = None" - else: - init_hash_cache = f"self.{_HASH_CACHE_FIELD} = None" - lines.append(init_hash_cache) - - # For exceptions we rely on BaseException.__init__ for proper - # initialization. - if is_exc: - vals = ",".join(f"self.{a.name}" for a in attrs if a.init) - - lines.append(f"BaseException.__init__(self, {vals})") - - args = ", ".join(args) - pre_init_args = ", ".join(pre_init_args) - if kw_only_args: - # leading comma & kw_only args - args += f"{', ' if args else ''}*, {', '.join(kw_only_args)}" - pre_init_kw_only_args = ", ".join( - [ - f"{kw_arg_name}={kw_arg_name}" - # We need to remove the defaults from the kw_only_args. - for kw_arg_name in (kwa.split("=")[0] for kwa in kw_only_args) - ] - ) - pre_init_args += ", " if pre_init_args else "" - pre_init_args += pre_init_kw_only_args - - if call_pre_init and pre_init_has_args: - # If pre init method has arguments, pass the values given to __init__. - lines[0] = f"self.__attrs_pre_init__({pre_init_args})" - - # Python <3.12 doesn't allow backslashes in f-strings. - NL = "\n " - return ( - f"""def {method_name}(self, {args}): - {NL.join(lines) if lines else "pass"} -""", - names_for_globals, - annotations, - ) - - -def _default_init_alias_for(name: str) -> str: - """ - The default __init__ parameter name for a field. - - This performs private-name adjustment via leading-unscore stripping, - and is the default value of Attribute.alias if not provided. - """ - - return name.lstrip("_") - - -class Attribute: - """ - *Read-only* representation of an attribute. - - .. warning:: - - You should never instantiate this class yourself. - - The class has *all* arguments of `attr.ib` (except for ``factory`` which is - only syntactic sugar for ``default=Factory(...)`` plus the following: - - - ``name`` (`str`): The name of the attribute. - - ``alias`` (`str`): The __init__ parameter name of the attribute, after - any explicit overrides and default private-attribute-name handling. - - ``alias_is_default`` (`bool`): Whether the ``alias`` was automatically - generated (``True``) or explicitly provided by the user (``False``). - - ``inherited`` (`bool`): Whether or not that attribute has been inherited - from a base class. - - ``eq_key`` and ``order_key`` (`typing.Callable` or `None`): The - callables that are used for comparing and ordering objects by this - attribute, respectively. These are set by passing a callable to - `attr.ib`'s ``eq``, ``order``, or ``cmp`` arguments. See also - :ref:`comparison customization `. - - Instances of this class are frequently used for introspection purposes - like: - - - `fields` returns a tuple of them. - - Validators get them passed as the first argument. - - The :ref:`field transformer ` hook receives a list of - them. - - The ``alias`` property exposes the __init__ parameter name of the field, - with any overrides and default private-attribute handling applied. - - - .. versionadded:: 20.1.0 *inherited* - .. versionadded:: 20.1.0 *on_setattr* - .. versionchanged:: 20.2.0 *inherited* is not taken into account for - equality checks and hashing anymore. - .. versionadded:: 21.1.0 *eq_key* and *order_key* - .. versionadded:: 22.2.0 *alias* - .. versionadded:: 26.1.0 *alias_is_default* - - For the full version history of the fields, see `attr.ib`. - """ - - # These slots must NOT be reordered because we use them later for - # instantiation. - __slots__ = ( # noqa: RUF023 - "name", - "default", - "validator", - "repr", - "eq", - "eq_key", - "order", - "order_key", - "hash", - "init", - "metadata", - "type", - "converter", - "kw_only", - "inherited", - "on_setattr", - "alias", - "alias_is_default", - ) - - def __init__( - self, - name, - default, - validator, - repr, - cmp, # XXX: unused, remove along with other cmp code. - hash, - init, - inherited, - metadata=None, - type=None, - converter=None, - kw_only=False, - eq=None, - eq_key=None, - order=None, - order_key=None, - on_setattr=None, - alias=None, - alias_is_default=None, - ): - eq, eq_key, order, order_key = _determine_attrib_eq_order( - cmp, eq_key or eq, order_key or order, True - ) - - # Cache this descriptor here to speed things up later. - bound_setattr = _OBJ_SETATTR.__get__(self) - - # Despite the big red warning, people *do* instantiate `Attribute` - # themselves. - bound_setattr("name", name) - bound_setattr("default", default) - bound_setattr("validator", validator) - bound_setattr("repr", repr) - bound_setattr("eq", eq) - bound_setattr("eq_key", eq_key) - bound_setattr("order", order) - bound_setattr("order_key", order_key) - bound_setattr("hash", hash) - bound_setattr("init", init) - bound_setattr("converter", converter) - bound_setattr( - "metadata", - ( - types.MappingProxyType(dict(metadata)) # Shallow copy - if metadata - else _EMPTY_METADATA_SINGLETON - ), - ) - bound_setattr("type", type) - bound_setattr("kw_only", kw_only) - bound_setattr("inherited", inherited) - bound_setattr("on_setattr", on_setattr) - bound_setattr("alias", alias) - bound_setattr( - "alias_is_default", - alias is None if alias_is_default is None else alias_is_default, - ) - - def __setattr__(self, name, value): - raise FrozenInstanceError - - @classmethod - def from_counting_attr( - cls, name: str, ca: _CountingAttr, kw_only: bool, type=None - ): - # The 'kw_only' argument is the class-level setting, and is used if the - # attribute itself does not explicitly set 'kw_only'. - # type holds the annotated value. deal with conflicts: - if type is None: - type = ca.type - elif ca.type is not None: - msg = f"Type annotation and type argument cannot both be present for '{name}'." - raise ValueError(msg) - return cls( - name, - ca._default, - ca._validator, - ca.repr, - None, - ca.hash, - ca.init, - False, - ca.metadata, - type, - ca.converter, - kw_only if ca.kw_only is None else ca.kw_only, - ca.eq, - ca.eq_key, - ca.order, - ca.order_key, - ca.on_setattr, - ca.alias, - ca.alias is None, - ) - - # Don't use attrs.evolve since fields(Attribute) doesn't work - def evolve(self, **changes): - """ - Copy *self* and apply *changes*. - - This works similarly to `attrs.evolve` but that function does not work - with :class:`attrs.Attribute`. - - It is mainly meant to be used for `transform-fields`. - - .. versionadded:: 20.3.0 - """ - new = copy.copy(self) - - new._setattrs(changes.items()) - - if "alias" in changes and "alias_is_default" not in changes: - # Explicit alias provided -- no longer the default. - _OBJ_SETATTR.__get__(new)("alias_is_default", False) - elif ( - "name" in changes - and "alias" not in changes - # Don't auto-generate alias if the user picked picked the old one. - and self.alias_is_default - ): - # Name changed, alias was auto-generated -- update it. - _OBJ_SETATTR.__get__(new)( - "alias", _default_init_alias_for(new.name) - ) - - return new - - # Don't use _add_pickle since fields(Attribute) doesn't work - def __getstate__(self): - """ - Play nice with pickle. - """ - return tuple( - getattr(self, name) if name != "metadata" else dict(self.metadata) - for name in self.__slots__ - ) - - def __setstate__(self, state): - """ - Play nice with pickle. - """ - if len(state) < len(self.__slots__): - # Pre-26.1.0 pickle without alias_is_default -- infer it - # heuristically. - state_dict = dict(zip(self.__slots__, state)) - alias_is_default = state_dict.get( - "alias" - ) is None or state_dict.get("alias") == _default_init_alias_for( - state_dict["name"] - ) - state = (*state, alias_is_default) - - self._setattrs(zip(self.__slots__, state)) - - def _setattrs(self, name_values_pairs): - bound_setattr = _OBJ_SETATTR.__get__(self) - for name, value in name_values_pairs: - if name != "metadata": - bound_setattr(name, value) - else: - bound_setattr( - name, - ( - types.MappingProxyType(dict(value)) - if value - else _EMPTY_METADATA_SINGLETON - ), - ) - - -_a = [ - Attribute( - name=name, - default=NOTHING, - validator=None, - repr=(name != "alias_is_default"), - cmp=None, - eq=True, - order=False, - hash=(name != "metadata"), - init=True, - inherited=False, - alias=_default_init_alias_for(name), - ) - for name in Attribute.__slots__ -] - -Attribute = _add_hash( - _add_eq( - _add_repr(Attribute, attrs=_a), - attrs=[a for a in _a if a.name != "inherited"], - ), - attrs=[a for a in _a if a.hash and a.name != "inherited"], -) - - -class _CountingAttr: - """ - Intermediate representation of attributes that uses a counter to preserve - the order in which the attributes have been defined. - - *Internal* data structure of the attrs library. Running into is most - likely the result of a bug like a forgotten `@attr.s` decorator. - """ - - __slots__ = ( - "_default", - "_validator", - "alias", - "converter", - "counter", - "eq", - "eq_key", - "hash", - "init", - "kw_only", - "metadata", - "on_setattr", - "order", - "order_key", - "repr", - "type", - ) - __attrs_attrs__ = ( - *tuple( - Attribute( - name=name, - alias=_default_init_alias_for(name), - default=NOTHING, - validator=None, - repr=True, - cmp=None, - hash=True, - init=True, - kw_only=False, - eq=True, - eq_key=None, - order=False, - order_key=None, - inherited=False, - on_setattr=None, - ) - for name in ( - "counter", - "_default", - "repr", - "eq", - "order", - "hash", - "init", - "on_setattr", - "alias", - ) - ), - Attribute( - name="metadata", - alias="metadata", - default=None, - validator=None, - repr=True, - cmp=None, - hash=False, - init=True, - kw_only=False, - eq=True, - eq_key=None, - order=False, - order_key=None, - inherited=False, - on_setattr=None, - ), - ) - cls_counter = 0 - - def __init__( - self, - default, - validator, - repr, - cmp, - hash, - init, - converter, - metadata, - type, - kw_only, - eq, - eq_key, - order, - order_key, - on_setattr, - alias, - ): - _CountingAttr.cls_counter += 1 - self.counter = _CountingAttr.cls_counter - self._default = default - self._validator = validator - self.converter = converter - self.repr = repr - self.eq = eq - self.eq_key = eq_key - self.order = order - self.order_key = order_key - self.hash = hash - self.init = init - self.metadata = metadata - self.type = type - self.kw_only = kw_only - self.on_setattr = on_setattr - self.alias = alias - - def validator(self, meth): - """ - Decorator that adds *meth* to the list of validators. - - Returns *meth* unchanged. - - .. versionadded:: 17.1.0 - """ - if self._validator is None: - self._validator = meth - else: - self._validator = and_(self._validator, meth) - return meth - - def default(self, meth): - """ - Decorator that allows to set the default for an attribute. - - Returns *meth* unchanged. - - Raises: - DefaultAlreadySetError: If default has been set before. - - .. versionadded:: 17.1.0 - """ - if self._default is not NOTHING: - raise DefaultAlreadySetError - - self._default = Factory(meth, takes_self=True) - - return meth - - -_CountingAttr = _add_eq(_add_repr(_CountingAttr)) - - -class ClassProps: - """ - Effective class properties as derived from parameters to `attr.s()` or - `define()` decorators. - - This is the same data structure that *attrs* uses internally to decide how - to construct the final class. - - Warning: - - This feature is currently **experimental** and is not covered by our - strict backwards-compatibility guarantees. - - - Attributes: - is_exception (bool): - Whether the class is treated as an exception class. - - is_slotted (bool): - Whether the class is `slotted `. - - has_weakref_slot (bool): - Whether the class has a slot for weak references. - - is_frozen (bool): - Whether the class is frozen. - - kw_only (KeywordOnly): - Whether / how the class enforces keyword-only arguments on the - ``__init__`` method. - - collected_fields_by_mro (bool): - Whether the class fields were collected by method resolution order. - That is, correctly but unlike `dataclasses`. - - added_init (bool): - Whether the class has an *attrs*-generated ``__init__`` method. - - added_repr (bool): - Whether the class has an *attrs*-generated ``__repr__`` method. - - added_eq (bool): - Whether the class has *attrs*-generated equality methods. - - added_ordering (bool): - Whether the class has *attrs*-generated ordering methods. - - hashability (Hashability): How `hashable ` the class is. - - added_match_args (bool): - Whether the class supports positional `match ` over its - fields. - - added_str (bool): - Whether the class has an *attrs*-generated ``__str__`` method. - - added_pickling (bool): - Whether the class has *attrs*-generated ``__getstate__`` and - ``__setstate__`` methods for `pickle`. - - on_setattr_hook (Callable[[Any, Attribute[Any], Any], Any] | None): - The class's ``__setattr__`` hook. - - field_transformer (Callable[[Attribute[Any]], Attribute[Any]] | None): - The class's `field transformers `. - - .. versionadded:: 25.4.0 - """ - - class Hashability(enum.Enum): - """ - The hashability of a class. - - .. versionadded:: 25.4.0 - """ - - HASHABLE = "hashable" - """Write a ``__hash__``.""" - HASHABLE_CACHED = "hashable_cache" - """Write a ``__hash__`` and cache the hash.""" - UNHASHABLE = "unhashable" - """Set ``__hash__`` to ``None``.""" - LEAVE_ALONE = "leave_alone" - """Don't touch ``__hash__``.""" - - class KeywordOnly(enum.Enum): - """ - How attributes should be treated regarding keyword-only parameters. - - .. versionadded:: 25.4.0 - """ - - NO = "no" - """Attributes are not keyword-only.""" - YES = "yes" - """Attributes in current class without kw_only=False are keyword-only.""" - FORCE = "force" - """All attributes are keyword-only.""" - - __slots__ = ( # noqa: RUF023 -- order matters for __init__ - "is_exception", - "is_slotted", - "has_weakref_slot", - "is_frozen", - "kw_only", - "collected_fields_by_mro", - "added_init", - "added_repr", - "added_eq", - "added_ordering", - "hashability", - "added_match_args", - "added_str", - "added_pickling", - "on_setattr_hook", - "field_transformer", - ) - - def __init__( - self, - is_exception, - is_slotted, - has_weakref_slot, - is_frozen, - kw_only, - collected_fields_by_mro, - added_init, - added_repr, - added_eq, - added_ordering, - hashability, - added_match_args, - added_str, - added_pickling, - on_setattr_hook, - field_transformer, - ): - self.is_exception = is_exception - self.is_slotted = is_slotted - self.has_weakref_slot = has_weakref_slot - self.is_frozen = is_frozen - self.kw_only = kw_only - self.collected_fields_by_mro = collected_fields_by_mro - self.added_init = added_init - self.added_repr = added_repr - self.added_eq = added_eq - self.added_ordering = added_ordering - self.hashability = hashability - self.added_match_args = added_match_args - self.added_str = added_str - self.added_pickling = added_pickling - self.on_setattr_hook = on_setattr_hook - self.field_transformer = field_transformer - - @property - def is_hashable(self): - return ( - self.hashability is ClassProps.Hashability.HASHABLE - or self.hashability is ClassProps.Hashability.HASHABLE_CACHED - ) - - -_cas = [ - Attribute( - name=name, - default=NOTHING, - validator=None, - repr=True, - cmp=None, - eq=True, - order=False, - hash=True, - init=True, - inherited=False, - alias=_default_init_alias_for(name), - ) - for name in ClassProps.__slots__ -] - -ClassProps = _add_eq(_add_repr(ClassProps, attrs=_cas), attrs=_cas) - - -class Factory: - """ - Stores a factory callable. - - If passed as the default value to `attrs.field`, the factory is used to - generate a new value. - - Args: - factory (typing.Callable): - A callable that takes either none or exactly one mandatory - positional argument depending on *takes_self*. - - takes_self (bool): - Pass the partially initialized instance that is being initialized - as a positional argument. - - .. versionadded:: 17.1.0 *takes_self* - """ - - __slots__ = ("factory", "takes_self") - - def __init__(self, factory, takes_self=False): - self.factory = factory - self.takes_self = takes_self - - def __getstate__(self): - """ - Play nice with pickle. - """ - return tuple(getattr(self, name) for name in self.__slots__) - - def __setstate__(self, state): - """ - Play nice with pickle. - """ - for name, value in zip(self.__slots__, state): - setattr(self, name, value) - - -_f = [ - Attribute( - name=name, - default=NOTHING, - validator=None, - repr=True, - cmp=None, - eq=True, - order=False, - hash=True, - init=True, - inherited=False, - ) - for name in Factory.__slots__ -] - -Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f) - - -class Converter: - """ - Stores a converter callable. - - Allows for the wrapped converter to take additional arguments. The - arguments are passed in the order they are documented. - - Args: - converter (Callable): A callable that converts the passed value. - - takes_self (bool): - Pass the partially initialized instance that is being initialized - as a positional argument. (default: `False`) - - takes_field (bool): - Pass the field definition (an :class:`Attribute`) into the - converter as a positional argument. (default: `False`) - - .. versionadded:: 24.1.0 - """ - - __slots__ = ( - "__call__", - "_first_param_type", - "_global_name", - "converter", - "takes_field", - "takes_self", - ) - - def __init__(self, converter, *, takes_self=False, takes_field=False): - self.converter = converter - self.takes_self = takes_self - self.takes_field = takes_field - - ex = _AnnotationExtractor(converter) - self._first_param_type = ex.get_first_param_type() - - if not (self.takes_self or self.takes_field): - self.__call__ = lambda value, _, __: self.converter(value) - elif self.takes_self and not self.takes_field: - self.__call__ = lambda value, instance, __: self.converter( - value, instance - ) - elif not self.takes_self and self.takes_field: - self.__call__ = lambda value, __, field: self.converter( - value, field - ) - else: - self.__call__ = self.converter - - rt = ex.get_return_type() - if rt is not None: - self.__call__.__annotations__["return"] = rt - - @staticmethod - def _get_global_name(attr_name: str) -> str: - """ - Return the name that a converter for an attribute name *attr_name* - would have. - """ - return f"__attr_converter_{attr_name}" - - def _fmt_converter_call(self, attr_name: str, value_var: str) -> str: - """ - Return a string that calls the converter for an attribute name - *attr_name* and the value in variable named *value_var* according to - `self.takes_self` and `self.takes_field`. - """ - if not (self.takes_self or self.takes_field): - return f"{self._get_global_name(attr_name)}({value_var})" - - if self.takes_self and self.takes_field: - return f"{self._get_global_name(attr_name)}({value_var}, self, attr_dict['{attr_name}'])" - - if self.takes_self: - return f"{self._get_global_name(attr_name)}({value_var}, self)" - - return f"{self._get_global_name(attr_name)}({value_var}, attr_dict['{attr_name}'])" - - def __getstate__(self): - """ - Return a dict containing only converter and takes_self -- the rest gets - computed when loading. - """ - return { - "converter": self.converter, - "takes_self": self.takes_self, - "takes_field": self.takes_field, - } - - def __setstate__(self, state): - """ - Load instance from state. - """ - self.__init__(**state) - - -_f = [ - Attribute( - name=name, - default=NOTHING, - validator=None, - repr=True, - cmp=None, - eq=True, - order=False, - hash=True, - init=True, - inherited=False, - ) - for name in ("converter", "takes_self", "takes_field") -] - -Converter = _add_hash( - _add_eq(_add_repr(Converter, attrs=_f), attrs=_f), attrs=_f -) - - -def make_class( - name, attrs, bases=(object,), class_body=None, **attributes_arguments -): - r""" - A quick way to create a new class called *name* with *attrs*. - - .. note:: - - ``make_class()`` is a thin wrapper around `attr.s`, not `attrs.define` - which means that it doesn't come with some of the improved defaults. - - For example, if you want the same ``on_setattr`` behavior as in - `attrs.define`, you have to pass the hooks yourself: ``make_class(..., - on_setattr=setters.pipe(setters.convert, setters.validate)`` - - .. warning:: - - It is *your* duty to ensure that the class name and the attribute names - are valid identifiers. ``make_class()`` will *not* validate them for - you. - - Args: - name (str): The name for the new class. - - attrs (list | dict): - A list of names or a dictionary of mappings of names to `attr.ib`\ - s / `attrs.field`\ s. - - The order is deduced from the order of the names or attributes - inside *attrs*. Otherwise the order of the definition of the - attributes is used. - - bases (tuple[type, ...]): Classes that the new class will subclass. - - class_body (dict): - An optional dictionary of class attributes for the new class. - - attributes_arguments: Passed unmodified to `attr.s`. - - Returns: - type: A new class with *attrs*. - - .. versionadded:: 17.1.0 *bases* - .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained. - .. versionchanged:: 23.2.0 *class_body* - .. versionchanged:: 25.2.0 Class names can now be unicode. - """ - # Class identifiers are converted into the normal form NFKC while parsing - name = unicodedata.normalize("NFKC", name) - - if isinstance(attrs, dict): - cls_dict = attrs - elif isinstance(attrs, (list, tuple)): - cls_dict = {a: attrib() for a in attrs} - else: - msg = "attrs argument must be a dict or a list." - raise TypeError(msg) - - pre_init = cls_dict.pop("__attrs_pre_init__", None) - post_init = cls_dict.pop("__attrs_post_init__", None) - user_init = cls_dict.pop("__init__", None) - - body = {} - if class_body is not None: - body.update(class_body) - if pre_init is not None: - body["__attrs_pre_init__"] = pre_init - if post_init is not None: - body["__attrs_post_init__"] = post_init - if user_init is not None: - body["__init__"] = user_init - - type_ = types.new_class(name, bases, {}, lambda ns: ns.update(body)) - - # For pickling to work, the __module__ variable needs to be set to the - # frame where the class is created. Bypass this step in environments where - # sys._getframe is not defined (Jython for example) or sys._getframe is not - # defined for arguments greater than 0 (IronPython). - with contextlib.suppress(AttributeError, ValueError): - type_.__module__ = sys._getframe(1).f_globals.get( - "__name__", "__main__" - ) - - # We do it here for proper warnings with meaningful stacklevel. - cmp = attributes_arguments.pop("cmp", None) - ( - attributes_arguments["eq"], - attributes_arguments["order"], - ) = _determine_attrs_eq_order( - cmp, - attributes_arguments.get("eq"), - attributes_arguments.get("order"), - True, - ) - - cls = _attrs(these=cls_dict, **attributes_arguments)(type_) - # Only add type annotations now or "_attrs()" will complain: - cls.__annotations__ = { - k: v.type for k, v in cls_dict.items() if v.type is not None - } - return cls - - -# These are required by within this module so we define them here and merely -# import into .validators / .converters. - - -@attrs(slots=True, unsafe_hash=True) -class _AndValidator: - """ - Compose many validators to a single one. - """ - - _validators = attrib() - - def __call__(self, inst, attr, value): - for v in self._validators: - v(inst, attr, value) - - -def and_(*validators): - """ - A validator that composes multiple validators into one. - - When called on a value, it runs all wrapped validators. - - Args: - validators (~collections.abc.Iterable[typing.Callable]): - Arbitrary number of validators. - - .. versionadded:: 17.1.0 - """ - vals = [] - for validator in validators: - vals.extend( - validator._validators - if isinstance(validator, _AndValidator) - else [validator] - ) - - return _AndValidator(tuple(vals)) - - -def pipe(*converters): - """ - A converter that composes multiple converters into one. - - When called on a value, it runs all wrapped converters, returning the - *last* value. - - Type annotations will be inferred from the wrapped converters', if they - have any. - - converters (~collections.abc.Iterable[typing.Callable]): - Arbitrary number of converters. - - .. versionadded:: 20.1.0 - """ - - return_instance = any(isinstance(c, Converter) for c in converters) - - if return_instance: - - def pipe_converter(val, inst, field): - for c in converters: - val = ( - c(val, inst, field) if isinstance(c, Converter) else c(val) - ) - - return val - - else: - - def pipe_converter(val): - for c in converters: - val = c(val) - - return val - - if not converters: - # If the converter list is empty, pipe_converter is the identity. - A = TypeVar("A") - pipe_converter.__annotations__.update({"val": A, "return": A}) - else: - # Get parameter type from first converter. - t = _AnnotationExtractor(converters[0]).get_first_param_type() - if t: - pipe_converter.__annotations__["val"] = t - - last = converters[-1] - if not PY_3_11_PLUS and isinstance(last, Converter): - last = last.__call__ - - # Get return type from last converter. - rt = _AnnotationExtractor(last).get_return_type() - if rt: - pipe_converter.__annotations__["return"] = rt - - if return_instance: - return Converter(pipe_converter, takes_self=True, takes_field=True) - return pipe_converter diff --git a/.venv/lib/python3.12/site-packages/attr/_next_gen.py b/.venv/lib/python3.12/site-packages/attr/_next_gen.py deleted file mode 100644 index 4ccd0da2..00000000 --- a/.venv/lib/python3.12/site-packages/attr/_next_gen.py +++ /dev/null @@ -1,674 +0,0 @@ -# SPDX-License-Identifier: MIT - -""" -These are keyword-only APIs that call `attr.s` and `attr.ib` with different -default values. -""" - -from functools import partial - -from . import setters -from ._funcs import asdict as _asdict -from ._funcs import astuple as _astuple -from ._make import ( - _DEFAULT_ON_SETATTR, - NOTHING, - _frozen_setattrs, - attrib, - attrs, -) -from .exceptions import NotAnAttrsClassError, UnannotatedAttributeError - - -def define( - maybe_cls=None, - *, - these=None, - repr=None, - unsafe_hash=None, - hash=None, - init=None, - slots=True, - frozen=False, - weakref_slot=True, - str=False, - auto_attribs=None, - kw_only=False, - cache_hash=False, - auto_exc=True, - eq=None, - order=False, - auto_detect=True, - getstate_setstate=None, - on_setattr=None, - field_transformer=None, - match_args=True, - force_kw_only=False, -): - r""" - A class decorator that adds :term:`dunder methods` according to - :term:`fields ` specified using :doc:`type annotations `, - `field()` calls, or the *these* argument. - - Since *attrs* patches or replaces an existing class, you cannot use - `object.__init_subclass__` with *attrs* classes, because it runs too early. - As a replacement, you can define ``__attrs_init_subclass__`` on your class. - It will be called by *attrs* classes that subclass it after they're - created. See also :ref:`init-subclass`. - - Args: - slots (bool): - Create a :term:`slotted class ` that's more - memory-efficient. Slotted classes are generally superior to the - default dict classes, but have some gotchas you should know about, - so we encourage you to read the :term:`glossary entry `. - - auto_detect (bool): - Instead of setting the *init*, *repr*, *eq*, and *hash* arguments - explicitly, assume they are set to True **unless any** of the - involved methods for one of the arguments is implemented in the - *current* class (meaning, it is *not* inherited from some base - class). - - So, for example by implementing ``__eq__`` on a class yourself, - *attrs* will deduce ``eq=False`` and will create *neither* - ``__eq__`` *nor* ``__ne__`` (but Python classes come with a - sensible ``__ne__`` by default, so it *should* be enough to only - implement ``__eq__`` in most cases). - - Passing :data:`True` or :data:`False` to *init*, *repr*, *eq*, or *hash* - overrides whatever *auto_detect* would determine. - - auto_exc (bool): - If the class subclasses `BaseException` (which implicitly includes - any subclass of any exception), the following happens to behave - like a well-behaved Python exception class: - - - the values for *eq*, *order*, and *hash* are ignored and the - instances compare and hash by the instance's ids [#]_ , - - all attributes that are either passed into ``__init__`` or have a - default value are additionally available as a tuple in the - ``args`` attribute, - - the value of *str* is ignored leaving ``__str__`` to base - classes. - - .. [#] - Note that *attrs* will *not* remove existing implementations of - ``__hash__`` or the equality methods. It just won't add own - ones. - - on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]): - A callable that is run whenever the user attempts to set an - attribute (either by assignment like ``i.x = 42`` or by using - `setattr` like ``setattr(i, "x", 42)``). It receives the same - arguments as validators: the instance, the attribute that is being - modified, and the new value. - - If no exception is raised, the attribute is set to the return value - of the callable. - - If a list of callables is passed, they're automatically wrapped in - an `attrs.setters.pipe`. - - If left None, the default behavior is to run converters and - validators whenever an attribute is set. - - init (bool): - Create a ``__init__`` method that initializes the *attrs* - attributes. Leading underscores are stripped for the argument name, - unless an alias is set on the attribute. - - .. seealso:: - `init` shows advanced ways to customize the generated - ``__init__`` method, including executing code before and after. - - repr(bool): - Create a ``__repr__`` method with a human readable representation - of *attrs* attributes. - - str (bool): - Create a ``__str__`` method that is identical to ``__repr__``. This - is usually not necessary except for `Exception`\ s. - - eq (bool | None): - If True or None (default), add ``__eq__`` and ``__ne__`` methods - that check two instances for equality. - - .. seealso:: - `comparison` describes how to customize the comparison behavior - going as far comparing NumPy arrays. - - order (bool | None): - If True, add ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` - methods that behave like *eq* above and allow instances to be - ordered. - - They compare the instances as if they were tuples of their *attrs* - attributes if and only if the types of both classes are - *identical*. - - If `None` mirror value of *eq*. - - .. seealso:: `comparison` - - unsafe_hash (bool | None): - If None (default), the ``__hash__`` method is generated according - how *eq* and *frozen* are set. - - 1. If *both* are True, *attrs* will generate a ``__hash__`` for - you. - 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set - to None, marking it unhashable (which it is). - 3. If *eq* is False, ``__hash__`` will be left untouched meaning - the ``__hash__`` method of the base class will be used. If the - base class is `object`, this means it will fall back to id-based - hashing. - - Although not recommended, you can decide for yourself and force - *attrs* to create one (for example, if the class is immutable even - though you didn't freeze it programmatically) by passing True or - not. Both of these cases are rather special and should be used - carefully. - - .. seealso:: - - - Our documentation on `hashing`, - - Python's documentation on `object.__hash__`, - - and the `GitHub issue that led to the default \ behavior - `_ for more - details. - - hash (bool | None): - Deprecated alias for *unsafe_hash*. *unsafe_hash* takes precedence. - - cache_hash (bool): - Ensure that the object's hash code is computed only once and stored - on the object. If this is set to True, hashing must be either - explicitly or implicitly enabled for this class. If the hash code - is cached, avoid any reassignments of fields involved in hash code - computation or mutations of the objects those fields point to after - object creation. If such changes occur, the behavior of the - object's hash code is undefined. - - frozen (bool): - Make instances immutable after initialization. If someone attempts - to modify a frozen instance, `attrs.exceptions.FrozenInstanceError` - is raised. - - .. note:: - - 1. This is achieved by installing a custom ``__setattr__`` - method on your class, so you can't implement your own. - - 2. True immutability is impossible in Python. - - 3. This *does* have a minor a runtime performance `impact - ` when initializing new instances. In other - words: ``__init__`` is slightly slower with ``frozen=True``. - - 4. If a class is frozen, you cannot modify ``self`` in - ``__attrs_post_init__`` or a self-written ``__init__``. You - can circumvent that limitation by using - ``object.__setattr__(self, "attribute_name", value)``. - - 5. Subclasses of a frozen class are frozen too. - - kw_only (bool): - Make attributes keyword-only in the generated ``__init__`` (if - *init* is False, this parameter is ignored). Attributes that - explicitly set ``kw_only=False`` are not affected; base class - attributes are also not affected. - - Also see *force_kw_only*. - - weakref_slot (bool): - Make instances weak-referenceable. This has no effect unless - *slots* is True. - - field_transformer (~typing.Callable | None): - A function that is called with the original class object and all - fields right before *attrs* finalizes the class. You can use this, - for example, to automatically add converters or validators to - fields based on their types. - - .. seealso:: `transform-fields` - - match_args (bool): - If True (default), set ``__match_args__`` on the class to support - :pep:`634` (*Structural Pattern Matching*). It is a tuple of all - non-keyword-only ``__init__`` parameter names on Python 3.10 and - later. Ignored on older Python versions. - - collect_by_mro (bool): - If True, *attrs* collects attributes from base classes correctly - according to the `method resolution order - `_. If False, *attrs* - will mimic the (wrong) behavior of `dataclasses` and :pep:`681`. - - See also `issue #428 - `_. - - force_kw_only (bool): - A back-compat flag for restoring pre-25.4.0 behavior. If True and - ``kw_only=True``, all attributes are made keyword-only, including - base class attributes, and those set to ``kw_only=False`` at the - attribute level. Defaults to False. - - See also `issue #980 - `_. - - getstate_setstate (bool | None): - .. note:: - - This is usually only interesting for slotted classes and you - should probably just set *auto_detect* to True. - - If True, ``__getstate__`` and ``__setstate__`` are generated and - attached to the class. This is necessary for slotted classes to be - pickleable. If left None, it's True by default for slotted classes - and False for dict classes. - - If *auto_detect* is True, and *getstate_setstate* is left None, and - **either** ``__getstate__`` or ``__setstate__`` is detected - directly on the class (meaning: not inherited), it is set to False - (this is usually what you want). - - auto_attribs (bool | None): - If True, look at type annotations to determine which attributes to - use, like `dataclasses`. If False, it will only look for explicit - :func:`field` class attributes, like classic *attrs*. - - If left None, it will guess: - - 1. If any attributes are annotated and no unannotated - `attrs.field`\ s are found, it assumes *auto_attribs=True*. - 2. Otherwise it assumes *auto_attribs=False* and tries to collect - `attrs.field`\ s. - - If *attrs* decides to look at type annotations, **all** fields - **must** be annotated. If *attrs* encounters a field that is set to - a :func:`field` / `attr.ib` but lacks a type annotation, an - `attrs.exceptions.UnannotatedAttributeError` is raised. Use - ``field_name: typing.Any = field(...)`` if you don't want to set a - type. - - .. warning:: - - For features that use the attribute name to create decorators - (for example, :ref:`validators `), you still *must* - assign :func:`field` / `attr.ib` to them. Otherwise Python will - either not find the name or try to use the default value to - call, for example, ``validator`` on it. - - Attributes annotated as `typing.ClassVar`, and attributes that are - neither annotated nor set to an `field()` are **ignored**. - - these (dict[str, object]): - A dictionary of name to the (private) return value of `field()` - mappings. This is useful to avoid the definition of your attributes - within the class body because you can't (for example, if you want - to add ``__repr__`` methods to Django models) or don't want to. - - If *these* is not `None`, *attrs* will *not* search the class body - for attributes and will *not* remove any attributes from it. - - The order is deduced from the order of the attributes inside - *these*. - - Arguably, this is a rather obscure feature. - - .. versionadded:: 20.1.0 - .. versionchanged:: 21.3.0 Converters are also run ``on_setattr``. - .. versionadded:: 22.2.0 - *unsafe_hash* as an alias for *hash* (for :pep:`681` compliance). - .. versionchanged:: 24.1.0 - Instances are not compared as tuples of attributes anymore, but using a - big ``and`` condition. This is faster and has more correct behavior for - uncomparable values like `math.nan`. - .. versionadded:: 24.1.0 - If a class has an *inherited* classmethod called - ``__attrs_init_subclass__``, it is executed after the class is created. - .. deprecated:: 24.1.0 *hash* is deprecated in favor of *unsafe_hash*. - .. versionadded:: 24.3.0 - Unless already present, a ``__replace__`` method is automatically - created for `copy.replace` (Python 3.13+ only). - .. versionchanged:: 25.4.0 - *kw_only* now only applies to attributes defined in the current class, - and respects attribute-level ``kw_only=False`` settings. - .. versionadded:: 25.4.0 - Added *force_kw_only* to go back to the previous *kw_only* behavior. - - .. note:: - - The main differences to the classic `attr.s` are: - - - Automatically detect whether or not *auto_attribs* should be `True` - (c.f. *auto_attribs* parameter). - - Converters and validators run when attributes are set by default -- - if *frozen* is `False`. - - *slots=True* - - Usually, this has only upsides and few visible effects in everyday - programming. But it *can* lead to some surprising behaviors, so - please make sure to read :term:`slotted classes`. - - - *auto_exc=True* - - *auto_detect=True* - - *order=False* - - *force_kw_only=False* - - Some options that were only relevant on Python 2 or were kept around - for backwards-compatibility have been removed. - - """ - - def do_it(cls, auto_attribs): - return attrs( - maybe_cls=cls, - these=these, - repr=repr, - hash=hash, - unsafe_hash=unsafe_hash, - init=init, - slots=slots, - frozen=frozen, - weakref_slot=weakref_slot, - str=str, - auto_attribs=auto_attribs, - kw_only=kw_only, - cache_hash=cache_hash, - auto_exc=auto_exc, - eq=eq, - order=order, - auto_detect=auto_detect, - collect_by_mro=True, - getstate_setstate=getstate_setstate, - on_setattr=on_setattr, - field_transformer=field_transformer, - match_args=match_args, - force_kw_only=force_kw_only, - ) - - def wrap(cls): - """ - Making this a wrapper ensures this code runs during class creation. - - We also ensure that frozen-ness of classes is inherited. - """ - nonlocal frozen, on_setattr - - had_on_setattr = on_setattr not in (None, setters.NO_OP) - - # By default, mutable classes convert & validate on setattr. - if frozen is False and on_setattr is None: - on_setattr = _DEFAULT_ON_SETATTR - - # However, if we subclass a frozen class, we inherit the immutability - # and disable on_setattr. - for base_cls in cls.__bases__: - if base_cls.__setattr__ is _frozen_setattrs: - if had_on_setattr: - msg = "Frozen classes can't use on_setattr (frozen-ness was inherited)." - raise ValueError(msg) - - on_setattr = setters.NO_OP - break - - if auto_attribs is not None: - return do_it(cls, auto_attribs) - - try: - return do_it(cls, True) - except UnannotatedAttributeError: - return do_it(cls, False) - - # maybe_cls's type depends on the usage of the decorator. It's a class - # if it's used as `@attrs` but `None` if used as `@attrs()`. - if maybe_cls is None: - return wrap - - return wrap(maybe_cls) - - -mutable = define -frozen = partial(define, frozen=True, on_setattr=None) - - -def field( - *, - default=NOTHING, - validator=None, - repr=True, - hash=None, - init=True, - metadata=None, - type=None, - converter=None, - factory=None, - kw_only=None, - eq=None, - order=None, - on_setattr=None, - alias=None, -): - """ - Create a new :term:`field` / :term:`attribute` on a class. - - .. warning:: - - Does **nothing** unless the class is also decorated with - `attrs.define` (or similar)! - - Args: - default: - A value that is used if an *attrs*-generated ``__init__`` is used - and no value is passed while instantiating or the attribute is - excluded using ``init=False``. - - If the value is an instance of `attrs.Factory`, its callable will - be used to construct a new value (useful for mutable data types - like lists or dicts). - - If a default is not set (or set manually to `attrs.NOTHING`), a - value *must* be supplied when instantiating; otherwise a - `TypeError` will be raised. - - .. seealso:: `defaults` - - factory (~typing.Callable): - Syntactic sugar for ``default=attr.Factory(factory)``. - - validator (~typing.Callable | list[~typing.Callable]): - Callable that is called by *attrs*-generated ``__init__`` methods - after the instance has been initialized. They receive the - initialized instance, the :func:`~attrs.Attribute`, and the passed - value. - - The return value is *not* inspected so the validator has to throw - an exception itself. - - If a `list` is passed, its items are treated as validators and must - all pass. - - Validators can be globally disabled and re-enabled using - `attrs.validators.get_disabled` / `attrs.validators.set_disabled`. - - The validator can also be set using decorator notation as shown - below. - - .. seealso:: :ref:`validators` - - repr (bool | ~typing.Callable): - Include this attribute in the generated ``__repr__`` method. If - True, include the attribute; if False, omit it. By default, the - built-in ``repr()`` function is used. To override how the attribute - value is formatted, pass a ``callable`` that takes a single value - and returns a string. Note that the resulting string is used as-is, - which means it will be used directly *instead* of calling - ``repr()`` (the default). - - eq (bool | ~typing.Callable): - If True (default), include this attribute in the generated - ``__eq__`` and ``__ne__`` methods that check two instances for - equality. To override how the attribute value is compared, pass a - callable that takes a single value and returns the value to be - compared. - - .. seealso:: `comparison` - - order (bool | ~typing.Callable): - If True (default), include this attributes in the generated - ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. To - override how the attribute value is ordered, pass a callable that - takes a single value and returns the value to be ordered. - - .. seealso:: `comparison` - - hash (bool | None): - Include this attribute in the generated ``__hash__`` method. If - None (default), mirror *eq*'s value. This is the correct behavior - according the Python spec. Setting this value to anything else - than None is *discouraged*. - - .. seealso:: `hashing` - - init (bool): - Include this attribute in the generated ``__init__`` method. - - It is possible to set this to False and set a default value. In - that case this attributed is unconditionally initialized with the - specified default value or factory. - - .. seealso:: `init` - - converter (typing.Callable | Converter): - A callable that is called by *attrs*-generated ``__init__`` methods - to convert attribute's value to the desired format. - - If a vanilla callable is passed, it is given the passed-in value as - the only positional argument. It is possible to receive additional - arguments by wrapping the callable in a `Converter`. - - Either way, the returned value will be used as the new value of the - attribute. The value is converted before being passed to the - validator, if any. - - .. seealso:: :ref:`converters` - - metadata (dict | None): - An arbitrary mapping, to be used by third-party code. - - .. seealso:: `extending-metadata`. - - type (type): - The type of the attribute. Nowadays, the preferred method to - specify the type is using a variable annotation (see :pep:`526`). - This argument is provided for backwards-compatibility and for usage - with `make_class`. Regardless of the approach used, the type will - be stored on ``Attribute.type``. - - Please note that *attrs* doesn't do anything with this metadata by - itself. You can use it as part of your own code or for `static type - checking `. - - kw_only (bool | None): - Make this attribute keyword-only in the generated ``__init__`` (if - *init* is False, this parameter is ignored). If None (default), - mirror the setting from `attrs.define`. - - on_setattr (~typing.Callable | list[~typing.Callable] | None | ~typing.Literal[attrs.setters.NO_OP]): - Allows to overwrite the *on_setattr* setting from `attr.s`. If left - None, the *on_setattr* value from `attr.s` is used. Set to - `attrs.setters.NO_OP` to run **no** `setattr` hooks for this - attribute -- regardless of the setting in `define()`. - - alias (str | None): - Override this attribute's parameter name in the generated - ``__init__`` method. If left None, default to ``name`` stripped - of leading underscores. See `private-attributes`. - - .. versionadded:: 20.1.0 - .. versionchanged:: 21.1.0 - *eq*, *order*, and *cmp* also accept a custom callable - .. versionadded:: 22.2.0 *alias* - .. versionadded:: 23.1.0 - The *type* parameter has been re-added; mostly for `attrs.make_class`. - Please note that type checkers ignore this metadata. - .. versionchanged:: 25.4.0 - *kw_only* can now be None, and its default is also changed from False to - None. - - .. seealso:: - - `attr.ib` - """ - return attrib( - default=default, - validator=validator, - repr=repr, - hash=hash, - init=init, - metadata=metadata, - type=type, - converter=converter, - factory=factory, - kw_only=kw_only, - eq=eq, - order=order, - on_setattr=on_setattr, - alias=alias, - ) - - -def asdict(inst, *, recurse=True, filter=None, value_serializer=None): - """ - Same as `attr.asdict`, except that collections types are always retained - and dict is always used as *dict_factory*. - - .. versionadded:: 21.3.0 - """ - return _asdict( - inst=inst, - recurse=recurse, - filter=filter, - value_serializer=value_serializer, - retain_collection_types=True, - ) - - -def astuple(inst, *, recurse=True, filter=None): - """ - Same as `attr.astuple`, except that collections types are always retained - and `tuple` is always used as the *tuple_factory*. - - .. versionadded:: 21.3.0 - """ - return _astuple( - inst=inst, recurse=recurse, filter=filter, retain_collection_types=True - ) - - -def inspect(cls): - """ - Inspect the class and return its effective build parameters. - - Warning: - This feature is currently **experimental** and is not covered by our - strict backwards-compatibility guarantees. - - Args: - cls: The *attrs*-decorated class to inspect. - - Returns: - The effective build parameters of the class. - - Raises: - NotAnAttrsClassError: If the class is not an *attrs*-decorated class. - - .. versionadded:: 25.4.0 - """ - try: - return cls.__dict__["__attrs_props__"] - except KeyError: - msg = f"{cls!r} is not an attrs-decorated class." - raise NotAnAttrsClassError(msg) from None diff --git a/.venv/lib/python3.12/site-packages/attr/_typing_compat.pyi b/.venv/lib/python3.12/site-packages/attr/_typing_compat.pyi deleted file mode 100644 index ca7b71e9..00000000 --- a/.venv/lib/python3.12/site-packages/attr/_typing_compat.pyi +++ /dev/null @@ -1,15 +0,0 @@ -from typing import Any, ClassVar, Protocol - -# MYPY is a special constant in mypy which works the same way as `TYPE_CHECKING`. -MYPY = False - -if MYPY: - # A protocol to be able to statically accept an attrs class. - class AttrsInstance_(Protocol): - __attrs_attrs__: ClassVar[Any] - -else: - # For type checkers without plug-in support use an empty protocol that - # will (hopefully) be combined into a union. - class AttrsInstance_(Protocol): - pass diff --git a/.venv/lib/python3.12/site-packages/attr/_version_info.py b/.venv/lib/python3.12/site-packages/attr/_version_info.py deleted file mode 100644 index 27f18884..00000000 --- a/.venv/lib/python3.12/site-packages/attr/_version_info.py +++ /dev/null @@ -1,89 +0,0 @@ -# SPDX-License-Identifier: MIT - - -from functools import total_ordering - -from ._funcs import astuple -from ._make import attrib, attrs - - -@total_ordering -@attrs(eq=False, order=False, slots=True, frozen=True) -class VersionInfo: - """ - A version object that can be compared to tuple of length 1--4: - - >>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2) - True - >>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1) - True - >>> vi = attr.VersionInfo(19, 2, 0, "final") - >>> vi < (19, 1, 1) - False - >>> vi < (19,) - False - >>> vi == (19, 2,) - True - >>> vi == (19, 2, 1) - False - - .. versionadded:: 19.2 - """ - - year = attrib(type=int) - minor = attrib(type=int) - micro = attrib(type=int) - releaselevel = attrib(type=str) - - @classmethod - def _from_version_string(cls, s): - """ - Parse *s* and return a _VersionInfo. - """ - v = s.split(".") - if len(v) == 3: - v.append("final") - - return cls( - year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3] - ) - - def _ensure_tuple(self, other): - """ - Ensure *other* is a tuple of a valid length. - - Returns a possibly transformed *other* and ourselves as a tuple of - the same length as *other*. - """ - - if self.__class__ is other.__class__: - other = astuple(other) - - if not isinstance(other, tuple): - raise NotImplementedError - - if not (1 <= len(other) <= 4): - raise NotImplementedError - - return astuple(self)[: len(other)], other - - def __eq__(self, other): - try: - us, them = self._ensure_tuple(other) - except NotImplementedError: - return NotImplemented - - return us == them - - def __lt__(self, other): - try: - us, them = self._ensure_tuple(other) - except NotImplementedError: - return NotImplemented - - # Since alphabetically "dev0" < "final" < "post1" < "post2", we don't - # have to do anything special with releaselevel for now. - return us < them - - def __hash__(self): - return hash((self.year, self.minor, self.micro, self.releaselevel)) diff --git a/.venv/lib/python3.12/site-packages/attr/_version_info.pyi b/.venv/lib/python3.12/site-packages/attr/_version_info.pyi deleted file mode 100644 index 45ced086..00000000 --- a/.venv/lib/python3.12/site-packages/attr/_version_info.pyi +++ /dev/null @@ -1,9 +0,0 @@ -class VersionInfo: - @property - def year(self) -> int: ... - @property - def minor(self) -> int: ... - @property - def micro(self) -> int: ... - @property - def releaselevel(self) -> str: ... diff --git a/.venv/lib/python3.12/site-packages/attr/converters.py b/.venv/lib/python3.12/site-packages/attr/converters.py deleted file mode 100644 index 0a79deef..00000000 --- a/.venv/lib/python3.12/site-packages/attr/converters.py +++ /dev/null @@ -1,162 +0,0 @@ -# SPDX-License-Identifier: MIT - -""" -Commonly useful converters. -""" - -import typing - -from ._compat import _AnnotationExtractor -from ._make import NOTHING, Converter, Factory, pipe - - -__all__ = [ - "default_if_none", - "optional", - "pipe", - "to_bool", -] - - -def optional(converter): - """ - A converter that allows an attribute to be optional. An optional attribute - is one which can be set to `None`. - - Type annotations will be inferred from the wrapped converter's, if it has - any. - - Args: - converter (typing.Callable): - the converter that is used for non-`None` values. - - .. versionadded:: 17.1.0 - """ - - if isinstance(converter, Converter): - - def optional_converter(val, inst, field): - if val is None: - return None - return converter(val, inst, field) - - else: - - def optional_converter(val): - if val is None: - return None - return converter(val) - - xtr = _AnnotationExtractor(converter) - - t = xtr.get_first_param_type() - if t: - optional_converter.__annotations__["val"] = typing.Optional[t] - - rt = xtr.get_return_type() - if rt: - optional_converter.__annotations__["return"] = typing.Optional[rt] - - if isinstance(converter, Converter): - return Converter(optional_converter, takes_self=True, takes_field=True) - - return optional_converter - - -def default_if_none(default=NOTHING, factory=None): - """ - A converter that allows to replace `None` values by *default* or the result - of *factory*. - - Args: - default: - Value to be used if `None` is passed. Passing an instance of - `attrs.Factory` is supported, however the ``takes_self`` option is - *not*. - - factory (typing.Callable): - A callable that takes no parameters whose result is used if `None` - is passed. - - Raises: - TypeError: If **neither** *default* or *factory* is passed. - - TypeError: If **both** *default* and *factory* are passed. - - ValueError: - If an instance of `attrs.Factory` is passed with - ``takes_self=True``. - - .. versionadded:: 18.2.0 - """ - if default is NOTHING and factory is None: - msg = "Must pass either `default` or `factory`." - raise TypeError(msg) - - if default is not NOTHING and factory is not None: - msg = "Must pass either `default` or `factory` but not both." - raise TypeError(msg) - - if factory is not None: - default = Factory(factory) - - if isinstance(default, Factory): - if default.takes_self: - msg = "`takes_self` is not supported by default_if_none." - raise ValueError(msg) - - def default_if_none_converter(val): - if val is not None: - return val - - return default.factory() - - else: - - def default_if_none_converter(val): - if val is not None: - return val - - return default - - return default_if_none_converter - - -def to_bool(val): - """ - Convert "boolean" strings (for example, from environment variables) to real - booleans. - - Values mapping to `True`: - - - ``True`` - - ``"true"`` / ``"t"`` - - ``"yes"`` / ``"y"`` - - ``"on"`` - - ``"1"`` - - ``1`` - - Values mapping to `False`: - - - ``False`` - - ``"false"`` / ``"f"`` - - ``"no"`` / ``"n"`` - - ``"off"`` - - ``"0"`` - - ``0`` - - Raises: - ValueError: For any other value. - - .. versionadded:: 21.3.0 - """ - if isinstance(val, str): - val = val.lower() - - if val in (True, "true", "t", "yes", "y", "on", "1", 1): - return True - if val in (False, "false", "f", "no", "n", "off", "0", 0): - return False - - msg = f"Cannot convert value to bool: {val!r}" - raise ValueError(msg) diff --git a/.venv/lib/python3.12/site-packages/attr/converters.pyi b/.venv/lib/python3.12/site-packages/attr/converters.pyi deleted file mode 100644 index 12bd0c4f..00000000 --- a/.venv/lib/python3.12/site-packages/attr/converters.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Callable, Any, overload - -from attrs import _ConverterType, _CallableConverterType - -@overload -def pipe(*validators: _CallableConverterType) -> _CallableConverterType: ... -@overload -def pipe(*validators: _ConverterType) -> _ConverterType: ... -@overload -def optional(converter: _CallableConverterType) -> _CallableConverterType: ... -@overload -def optional(converter: _ConverterType) -> _ConverterType: ... -@overload -def default_if_none(default: Any) -> _CallableConverterType: ... -@overload -def default_if_none( - *, factory: Callable[[], Any] -) -> _CallableConverterType: ... -def to_bool(val: str | int | bool) -> bool: ... diff --git a/.venv/lib/python3.12/site-packages/attr/exceptions.py b/.venv/lib/python3.12/site-packages/attr/exceptions.py deleted file mode 100644 index a207df40..00000000 --- a/.venv/lib/python3.12/site-packages/attr/exceptions.py +++ /dev/null @@ -1,95 +0,0 @@ -# SPDX-License-Identifier: MIT - -from __future__ import annotations - - -class FrozenError(AttributeError): - """ - A frozen/immutable instance or attribute have been attempted to be - modified. - - It mirrors the behavior of ``namedtuples`` by using the same error message - and subclassing `AttributeError`. - - .. versionadded:: 20.1.0 - """ - - def __init__(self): - msg = "can't set attribute" - super().__init__(msg) - self.msg = msg - - -class FrozenInstanceError(FrozenError): - """ - A frozen instance has been attempted to be modified. - - .. versionadded:: 16.1.0 - """ - - -class FrozenAttributeError(FrozenError): - """ - A frozen attribute has been attempted to be modified. - - .. versionadded:: 20.1.0 - """ - - -class AttrsAttributeNotFoundError(ValueError): - """ - An *attrs* function couldn't find an attribute that the user asked for. - - .. versionadded:: 16.2.0 - """ - - -class NotAnAttrsClassError(ValueError): - """ - A non-*attrs* class has been passed into an *attrs* function. - - .. versionadded:: 16.2.0 - """ - - -class DefaultAlreadySetError(RuntimeError): - """ - A default has been set when defining the field and is attempted to be reset - using the decorator. - - .. versionadded:: 17.1.0 - """ - - -class UnannotatedAttributeError(RuntimeError): - """ - A class with ``auto_attribs=True`` has a field without a type annotation. - - .. versionadded:: 17.3.0 - """ - - -class PythonTooOldError(RuntimeError): - """ - It was attempted to use an *attrs* feature that requires a newer Python - version. - - .. versionadded:: 18.2.0 - """ - - -class NotCallableError(TypeError): - """ - A field requiring a callable has been set with a value that is not - callable. - - .. versionadded:: 19.2.0 - """ - - def __init__(self, msg, value): - super(TypeError, self).__init__(msg, value) - self.msg = msg - self.value = value - - def __str__(self): - return str(self.msg) diff --git a/.venv/lib/python3.12/site-packages/attr/exceptions.pyi b/.venv/lib/python3.12/site-packages/attr/exceptions.pyi deleted file mode 100644 index f2680118..00000000 --- a/.venv/lib/python3.12/site-packages/attr/exceptions.pyi +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Any - -class FrozenError(AttributeError): - msg: str = ... - -class FrozenInstanceError(FrozenError): ... -class FrozenAttributeError(FrozenError): ... -class AttrsAttributeNotFoundError(ValueError): ... -class NotAnAttrsClassError(ValueError): ... -class DefaultAlreadySetError(RuntimeError): ... -class UnannotatedAttributeError(RuntimeError): ... -class PythonTooOldError(RuntimeError): ... - -class NotCallableError(TypeError): - msg: str = ... - value: Any = ... - def __init__(self, msg: str, value: Any) -> None: ... diff --git a/.venv/lib/python3.12/site-packages/attr/filters.py b/.venv/lib/python3.12/site-packages/attr/filters.py deleted file mode 100644 index 689b1705..00000000 --- a/.venv/lib/python3.12/site-packages/attr/filters.py +++ /dev/null @@ -1,72 +0,0 @@ -# SPDX-License-Identifier: MIT - -""" -Commonly useful filters for `attrs.asdict` and `attrs.astuple`. -""" - -from ._make import Attribute - - -def _split_what(what): - """ - Returns a tuple of `frozenset`s of classes and attributes. - """ - return ( - frozenset(cls for cls in what if isinstance(cls, type)), - frozenset(cls for cls in what if isinstance(cls, str)), - frozenset(cls for cls in what if isinstance(cls, Attribute)), - ) - - -def include(*what): - """ - Create a filter that only allows *what*. - - Args: - what (list[type, str, attrs.Attribute]): - What to include. Can be a type, a name, or an attribute. - - Returns: - Callable: - A callable that can be passed to `attrs.asdict`'s and - `attrs.astuple`'s *filter* argument. - - .. versionchanged:: 23.1.0 Accept strings with field names. - """ - cls, names, attrs = _split_what(what) - - def include_(attribute, value): - return ( - value.__class__ in cls - or attribute.name in names - or attribute in attrs - ) - - return include_ - - -def exclude(*what): - """ - Create a filter that does **not** allow *what*. - - Args: - what (list[type, str, attrs.Attribute]): - What to exclude. Can be a type, a name, or an attribute. - - Returns: - Callable: - A callable that can be passed to `attrs.asdict`'s and - `attrs.astuple`'s *filter* argument. - - .. versionchanged:: 23.3.0 Accept field name string as input argument - """ - cls, names, attrs = _split_what(what) - - def exclude_(attribute, value): - return not ( - value.__class__ in cls - or attribute.name in names - or attribute in attrs - ) - - return exclude_ diff --git a/.venv/lib/python3.12/site-packages/attr/filters.pyi b/.venv/lib/python3.12/site-packages/attr/filters.pyi deleted file mode 100644 index 974abdcd..00000000 --- a/.venv/lib/python3.12/site-packages/attr/filters.pyi +++ /dev/null @@ -1,6 +0,0 @@ -from typing import Any - -from . import Attribute, _FilterType - -def include(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ... -def exclude(*what: type | str | Attribute[Any]) -> _FilterType[Any]: ... diff --git a/.venv/lib/python3.12/site-packages/attr/py.typed b/.venv/lib/python3.12/site-packages/attr/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/attr/setters.py b/.venv/lib/python3.12/site-packages/attr/setters.py deleted file mode 100644 index 78b08398..00000000 --- a/.venv/lib/python3.12/site-packages/attr/setters.py +++ /dev/null @@ -1,79 +0,0 @@ -# SPDX-License-Identifier: MIT - -""" -Commonly used hooks for on_setattr. -""" - -from . import _config -from .exceptions import FrozenAttributeError - - -def pipe(*setters): - """ - Run all *setters* and return the return value of the last one. - - .. versionadded:: 20.1.0 - """ - - def wrapped_pipe(instance, attrib, new_value): - rv = new_value - - for setter in setters: - rv = setter(instance, attrib, rv) - - return rv - - return wrapped_pipe - - -def frozen(_, __, ___): - """ - Prevent an attribute to be modified. - - .. versionadded:: 20.1.0 - """ - raise FrozenAttributeError - - -def validate(instance, attrib, new_value): - """ - Run *attrib*'s validator on *new_value* if it has one. - - .. versionadded:: 20.1.0 - """ - if _config._run_validators is False: - return new_value - - v = attrib.validator - if not v: - return new_value - - v(instance, attrib, new_value) - - return new_value - - -def convert(instance, attrib, new_value): - """ - Run *attrib*'s converter -- if it has one -- on *new_value* and return the - result. - - .. versionadded:: 20.1.0 - """ - c = attrib.converter - if c: - # This can be removed once we drop 3.8 and use attrs.Converter instead. - from ._make import Converter - - if not isinstance(c, Converter): - return c(new_value) - - return c(new_value, instance, attrib) - - return new_value - - -# Sentinel for disabling class-wide *on_setattr* hooks for certain attributes. -# Sphinx's autodata stopped working, so the docstring is inlined in the API -# docs. -NO_OP = object() diff --git a/.venv/lib/python3.12/site-packages/attr/setters.pyi b/.venv/lib/python3.12/site-packages/attr/setters.pyi deleted file mode 100644 index 73abf36e..00000000 --- a/.venv/lib/python3.12/site-packages/attr/setters.pyi +++ /dev/null @@ -1,20 +0,0 @@ -from typing import Any, NewType, NoReturn, TypeVar - -from . import Attribute -from attrs import _OnSetAttrType - -_T = TypeVar("_T") - -def frozen( - instance: Any, attribute: Attribute[Any], new_value: Any -) -> NoReturn: ... -def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ... -def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ... - -# convert is allowed to return Any, because they can be chained using pipe. -def convert( - instance: Any, attribute: Attribute[Any], new_value: Any -) -> Any: ... - -_NoOpType = NewType("_NoOpType", object) -NO_OP: _NoOpType diff --git a/.venv/lib/python3.12/site-packages/attr/validators.py b/.venv/lib/python3.12/site-packages/attr/validators.py deleted file mode 100644 index 0b1a2944..00000000 --- a/.venv/lib/python3.12/site-packages/attr/validators.py +++ /dev/null @@ -1,750 +0,0 @@ -# SPDX-License-Identifier: MIT - -""" -Commonly useful validators. -""" - -import operator -import re - -from contextlib import contextmanager -from re import Pattern - -from ._config import get_run_validators, set_run_validators -from ._make import _AndValidator, and_, attrib, attrs -from .converters import default_if_none -from .exceptions import NotCallableError - - -__all__ = [ - "and_", - "deep_iterable", - "deep_mapping", - "disabled", - "ge", - "get_disabled", - "gt", - "in_", - "instance_of", - "is_callable", - "le", - "lt", - "matches_re", - "max_len", - "min_len", - "not_", - "optional", - "or_", - "set_disabled", -] - - -def set_disabled(disabled): - """ - Globally disable or enable running validators. - - By default, they are run. - - Args: - disabled (bool): If `True`, disable running all validators. - - .. warning:: - - This function is not thread-safe! - - .. versionadded:: 21.3.0 - """ - set_run_validators(not disabled) - - -def get_disabled(): - """ - Return a bool indicating whether validators are currently disabled or not. - - Returns: - bool:`True` if validators are currently disabled. - - .. versionadded:: 21.3.0 - """ - return not get_run_validators() - - -@contextmanager -def disabled(): - """ - Context manager that disables running validators within its context. - - .. warning:: - - This context manager is not thread-safe! - - .. versionadded:: 21.3.0 - .. versionchanged:: 26.1.0 The contextmanager is nestable. - """ - prev = get_run_validators() - set_run_validators(False) - try: - yield - finally: - set_run_validators(prev) - - -@attrs(repr=False, slots=True, unsafe_hash=True) -class _InstanceOfValidator: - type = attrib() - - def __call__(self, inst, attr, value): - """ - We use a callable class to be able to change the ``__repr__``. - """ - if not isinstance(value, self.type): - msg = f"'{attr.name}' must be {self.type!r} (got {value!r} that is a {value.__class__!r})." - raise TypeError( - msg, - attr, - self.type, - value, - ) - - def __repr__(self): - return f"" - - -def instance_of(type): - """ - A validator that raises a `TypeError` if the initializer is called with a - wrong type for this particular attribute (checks are performed using - `isinstance` therefore it's also valid to pass a tuple of types). - - Args: - type (type | tuple[type]): The type to check for. - - Raises: - TypeError: - With a human readable error message, the attribute (of type - `attrs.Attribute`), the expected type, and the value it got. - """ - return _InstanceOfValidator(type) - - -@attrs(repr=False, frozen=True, slots=True) -class _MatchesReValidator: - pattern = attrib() - match_func = attrib() - - def __call__(self, inst, attr, value): - """ - We use a callable class to be able to change the ``__repr__``. - """ - if not self.match_func(value): - msg = f"'{attr.name}' must match regex {self.pattern.pattern!r} ({value!r} doesn't)" - raise ValueError( - msg, - attr, - self.pattern, - value, - ) - - def __repr__(self): - return f"" - - -def matches_re(regex, flags=0, func=None): - r""" - A validator that raises `ValueError` if the initializer is called with a - string that doesn't match *regex*. - - Args: - regex (str, re.Pattern): - A regex string or precompiled pattern to match against - - flags (int): - Flags that will be passed to the underlying re function (default 0) - - func (typing.Callable): - Which underlying `re` function to call. Valid options are - `re.fullmatch`, `re.search`, and `re.match`; the default `None` - means `re.fullmatch`. For performance reasons, the pattern is - always precompiled using `re.compile`. - - .. versionadded:: 19.2.0 - .. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern. - """ - valid_funcs = (re.fullmatch, None, re.search, re.match) - if func not in valid_funcs: - msg = "'func' must be one of {}.".format( - ", ".join( - sorted((e and e.__name__) or "None" for e in set(valid_funcs)) - ) - ) - raise ValueError(msg) - - if isinstance(regex, Pattern): - if flags: - msg = "'flags' can only be used with a string pattern; pass flags to re.compile() instead" - raise TypeError(msg) - pattern = regex - else: - pattern = re.compile(regex, flags) - - if func is re.match: - match_func = pattern.match - elif func is re.search: - match_func = pattern.search - else: - match_func = pattern.fullmatch - - return _MatchesReValidator(pattern, match_func) - - -@attrs(repr=False, slots=True, unsafe_hash=True) -class _OptionalValidator: - validator = attrib() - - def __call__(self, inst, attr, value): - if value is None: - return - - self.validator(inst, attr, value) - - def __repr__(self): - return f"" - - -def optional(validator): - """ - A validator that makes an attribute optional. An optional attribute is one - which can be set to `None` in addition to satisfying the requirements of - the sub-validator. - - Args: - validator - (typing.Callable | tuple[typing.Callable] | list[typing.Callable]): - A validator (or validators) that is used for non-`None` values. - - .. versionadded:: 15.1.0 - .. versionchanged:: 17.1.0 *validator* can be a list of validators. - .. versionchanged:: 23.1.0 *validator* can also be a tuple of validators. - """ - if isinstance(validator, (list, tuple)): - return _OptionalValidator(_AndValidator(validator)) - - return _OptionalValidator(validator) - - -@attrs(repr=False, slots=True, unsafe_hash=True) -class _InValidator: - options = attrib() - _original_options = attrib(hash=False) - - def __call__(self, inst, attr, value): - try: - in_options = value in self.options - except TypeError: # e.g. `1 in "abc"` - in_options = False - - if not in_options: - msg = f"'{attr.name}' must be in {self._original_options!r} (got {value!r})" - raise ValueError( - msg, - attr, - self._original_options, - value, - ) - - def __repr__(self): - return f"" - - -def in_(options): - """ - A validator that raises a `ValueError` if the initializer is called with a - value that does not belong in the *options* provided. - - The check is performed using ``value in options``, so *options* has to - support that operation. - - To keep the validator hashable, dicts, lists, and sets are transparently - transformed into a `tuple`. - - Args: - options: Allowed options. - - Raises: - ValueError: - With a human readable error message, the attribute (of type - `attrs.Attribute`), the expected options, and the value it got. - - .. versionadded:: 17.1.0 - .. versionchanged:: 22.1.0 - The ValueError was incomplete until now and only contained the human - readable error message. Now it contains all the information that has - been promised since 17.1.0. - .. versionchanged:: 24.1.0 - *options* that are a list, dict, or a set are now transformed into a - tuple to keep the validator hashable. - """ - repr_options = options - if isinstance(options, (list, dict, set)): - options = tuple(options) - - return _InValidator(options, repr_options) - - -@attrs(repr=False, slots=False, unsafe_hash=True) -class _IsCallableValidator: - def __call__(self, inst, attr, value): - """ - We use a callable class to be able to change the ``__repr__``. - """ - if not callable(value): - message = ( - "'{name}' must be callable " - "(got {value!r} that is a {actual!r})." - ) - raise NotCallableError( - msg=message.format( - name=attr.name, value=value, actual=value.__class__ - ), - value=value, - ) - - def __repr__(self): - return "" - - -def is_callable(): - """ - A validator that raises a `attrs.exceptions.NotCallableError` if the - initializer is called with a value for this particular attribute that is - not callable. - - .. versionadded:: 19.1.0 - - Raises: - attrs.exceptions.NotCallableError: - With a human readable error message containing the attribute - (`attrs.Attribute`) name, and the value it got. - """ - return _IsCallableValidator() - - -@attrs(repr=False, slots=True, unsafe_hash=True) -class _DeepIterable: - member_validator = attrib(validator=is_callable()) - iterable_validator = attrib( - default=None, validator=optional(is_callable()) - ) - - def __call__(self, inst, attr, value): - """ - We use a callable class to be able to change the ``__repr__``. - """ - if self.iterable_validator is not None: - self.iterable_validator(inst, attr, value) - - for member in value: - self.member_validator(inst, attr, member) - - def __repr__(self): - iterable_identifier = ( - "" - if self.iterable_validator is None - else f" {self.iterable_validator!r}" - ) - return ( - f"" - ) - - -def deep_iterable(member_validator, iterable_validator=None): - """ - A validator that performs deep validation of an iterable. - - Args: - member_validator: Validator(s) to apply to iterable members. - - iterable_validator: - Validator(s) to apply to iterable itself (optional). - - Raises - TypeError: if any sub-validators fail - - .. versionadded:: 19.1.0 - - .. versionchanged:: 25.4.0 - *member_validator* and *iterable_validator* can now be a list or tuple - of validators. - """ - if isinstance(member_validator, (list, tuple)): - member_validator = and_(*member_validator) - if isinstance(iterable_validator, (list, tuple)): - iterable_validator = and_(*iterable_validator) - return _DeepIterable(member_validator, iterable_validator) - - -@attrs(repr=False, slots=True, unsafe_hash=True) -class _DeepMapping: - key_validator = attrib(validator=optional(is_callable())) - value_validator = attrib(validator=optional(is_callable())) - mapping_validator = attrib(validator=optional(is_callable())) - - def __call__(self, inst, attr, value): - """ - We use a callable class to be able to change the ``__repr__``. - """ - if self.mapping_validator is not None: - self.mapping_validator(inst, attr, value) - - for key in value: - if self.key_validator is not None: - self.key_validator(inst, attr, key) - if self.value_validator is not None: - self.value_validator(inst, attr, value[key]) - - def __repr__(self): - return f"" - - -def deep_mapping( - key_validator=None, value_validator=None, mapping_validator=None -): - """ - A validator that performs deep validation of a dictionary. - - All validators are optional, but at least one of *key_validator* or - *value_validator* must be provided. - - Args: - key_validator: Validator(s) to apply to dictionary keys. - - value_validator: Validator(s) to apply to dictionary values. - - mapping_validator: - Validator(s) to apply to top-level mapping attribute. - - .. versionadded:: 19.1.0 - - .. versionchanged:: 25.4.0 - *key_validator* and *value_validator* are now optional, but at least one - of them must be provided. - - .. versionchanged:: 25.4.0 - *key_validator*, *value_validator*, and *mapping_validator* can now be a - list or tuple of validators. - - Raises: - TypeError: If any sub-validator fails on validation. - - ValueError: - If neither *key_validator* nor *value_validator* is provided on - instantiation. - """ - if key_validator is None and value_validator is None: - msg = ( - "At least one of key_validator or value_validator must be provided" - ) - raise ValueError(msg) - - if isinstance(key_validator, (list, tuple)): - key_validator = and_(*key_validator) - if isinstance(value_validator, (list, tuple)): - value_validator = and_(*value_validator) - if isinstance(mapping_validator, (list, tuple)): - mapping_validator = and_(*mapping_validator) - - return _DeepMapping(key_validator, value_validator, mapping_validator) - - -@attrs(repr=False, frozen=True, slots=True) -class _NumberValidator: - bound = attrib() - compare_op = attrib() - compare_func = attrib() - - def __call__(self, inst, attr, value): - """ - We use a callable class to be able to change the ``__repr__``. - """ - if not self.compare_func(value, self.bound): - msg = f"'{attr.name}' must be {self.compare_op} {self.bound}: {value}" - raise ValueError(msg) - - def __repr__(self): - return f"" - - -def lt(val): - """ - A validator that raises `ValueError` if the initializer is called with a - number larger or equal to *val*. - - The validator uses `operator.lt` to compare the values. - - Args: - val: Exclusive upper bound for values. - - .. versionadded:: 21.3.0 - """ - return _NumberValidator(val, "<", operator.lt) - - -def le(val): - """ - A validator that raises `ValueError` if the initializer is called with a - number greater than *val*. - - The validator uses `operator.le` to compare the values. - - Args: - val: Inclusive upper bound for values. - - .. versionadded:: 21.3.0 - """ - return _NumberValidator(val, "<=", operator.le) - - -def ge(val): - """ - A validator that raises `ValueError` if the initializer is called with a - number smaller than *val*. - - The validator uses `operator.ge` to compare the values. - - Args: - val: Inclusive lower bound for values - - .. versionadded:: 21.3.0 - """ - return _NumberValidator(val, ">=", operator.ge) - - -def gt(val): - """ - A validator that raises `ValueError` if the initializer is called with a - number smaller or equal to *val*. - - The validator uses `operator.gt` to compare the values. - - Args: - val: Exclusive lower bound for values - - .. versionadded:: 21.3.0 - """ - return _NumberValidator(val, ">", operator.gt) - - -@attrs(repr=False, frozen=True, slots=True) -class _MaxLengthValidator: - max_length = attrib() - - def __call__(self, inst, attr, value): - """ - We use a callable class to be able to change the ``__repr__``. - """ - if len(value) > self.max_length: - msg = f"Length of '{attr.name}' must be <= {self.max_length}: {len(value)}" - raise ValueError(msg) - - def __repr__(self): - return f"" - - -def max_len(length): - """ - A validator that raises `ValueError` if the initializer is called - with a string or iterable that is longer than *length*. - - Args: - length (int): Maximum length of the string or iterable - - .. versionadded:: 21.3.0 - """ - return _MaxLengthValidator(length) - - -@attrs(repr=False, frozen=True, slots=True) -class _MinLengthValidator: - min_length = attrib() - - def __call__(self, inst, attr, value): - """ - We use a callable class to be able to change the ``__repr__``. - """ - if len(value) < self.min_length: - msg = f"Length of '{attr.name}' must be >= {self.min_length}: {len(value)}" - raise ValueError(msg) - - def __repr__(self): - return f"" - - -def min_len(length): - """ - A validator that raises `ValueError` if the initializer is called - with a string or iterable that is shorter than *length*. - - Args: - length (int): Minimum length of the string or iterable - - .. versionadded:: 22.1.0 - """ - return _MinLengthValidator(length) - - -@attrs(repr=False, slots=True, unsafe_hash=True) -class _SubclassOfValidator: - type = attrib() - - def __call__(self, inst, attr, value): - """ - We use a callable class to be able to change the ``__repr__``. - """ - if not issubclass(value, self.type): - msg = f"'{attr.name}' must be a subclass of {self.type!r} (got {value!r})." - raise TypeError( - msg, - attr, - self.type, - value, - ) - - def __repr__(self): - return f"" - - -def _subclass_of(type): - """ - A validator that raises a `TypeError` if the initializer is called with a - wrong type for this particular attribute (checks are performed using - `issubclass` therefore it's also valid to pass a tuple of types). - - Args: - type (type | tuple[type, ...]): The type(s) to check for. - - Raises: - TypeError: - With a human readable error message, the attribute (of type - `attrs.Attribute`), the expected type, and the value it got. - """ - return _SubclassOfValidator(type) - - -@attrs(repr=False, slots=True, unsafe_hash=True) -class _NotValidator: - validator = attrib() - msg = attrib( - converter=default_if_none( - "not_ validator child '{validator!r}' " - "did not raise a captured error" - ) - ) - exc_types = attrib( - validator=deep_iterable( - member_validator=_subclass_of(Exception), - iterable_validator=instance_of(tuple), - ), - ) - - def __call__(self, inst, attr, value): - try: - self.validator(inst, attr, value) - except self.exc_types: - pass # suppress error to invert validity - else: - raise ValueError( - self.msg.format( - validator=self.validator, - exc_types=self.exc_types, - ), - attr, - self.validator, - value, - self.exc_types, - ) - - def __repr__(self): - return f"" - - -def not_(validator, *, msg=None, exc_types=(ValueError, TypeError)): - """ - A validator that wraps and logically 'inverts' the validator passed to it. - It will raise a `ValueError` if the provided validator *doesn't* raise a - `ValueError` or `TypeError` (by default), and will suppress the exception - if the provided validator *does*. - - Intended to be used with existing validators to compose logic without - needing to create inverted variants, for example, ``not_(in_(...))``. - - Args: - validator: A validator to be logically inverted. - - msg (str): - Message to raise if validator fails. Formatted with keys - ``exc_types`` and ``validator``. - - exc_types (tuple[type, ...]): - Exception type(s) to capture. Other types raised by child - validators will not be intercepted and pass through. - - Raises: - ValueError: - With a human readable error message, the attribute (of type - `attrs.Attribute`), the validator that failed to raise an - exception, the value it got, and the expected exception types. - - .. versionadded:: 22.2.0 - """ - try: - exc_types = tuple(exc_types) - except TypeError: - exc_types = (exc_types,) - return _NotValidator(validator, msg, exc_types) - - -@attrs(repr=False, slots=True, unsafe_hash=True) -class _OrValidator: - validators = attrib() - - def __call__(self, inst, attr, value): - for v in self.validators: - try: - v(inst, attr, value) - except Exception: # noqa: BLE001, PERF203, S112 - continue - else: - return - - msg = f"None of {self.validators!r} satisfied for value {value!r}" - raise ValueError(msg) - - def __repr__(self): - return f"" - - -def or_(*validators): - """ - A validator that composes multiple validators into one. - - When called on a value, it runs all wrapped validators until one of them is - satisfied. - - Args: - validators (~collections.abc.Iterable[typing.Callable]): - Arbitrary number of validators. - - Raises: - ValueError: - If no validator is satisfied. Raised with a human-readable error - message listing all the wrapped validators and the value that - failed all of them. - - .. versionadded:: 24.1.0 - """ - vals = [] - for v in validators: - vals.extend(v.validators if isinstance(v, _OrValidator) else [v]) - - return _OrValidator(tuple(vals)) diff --git a/.venv/lib/python3.12/site-packages/attr/validators.pyi b/.venv/lib/python3.12/site-packages/attr/validators.pyi deleted file mode 100644 index 18fb112c..00000000 --- a/.venv/lib/python3.12/site-packages/attr/validators.pyi +++ /dev/null @@ -1,140 +0,0 @@ -from types import UnionType -from typing import ( - Any, - AnyStr, - Callable, - Container, - ContextManager, - Iterable, - Mapping, - Match, - Pattern, - TypeVar, - overload, -) - -from attrs import _ValidatorType -from attrs import _ValidatorArgType - -_T = TypeVar("_T") -_T1 = TypeVar("_T1") -_T2 = TypeVar("_T2") -_T3 = TypeVar("_T3") -_T4 = TypeVar("_T4") -_T5 = TypeVar("_T5") -_T6 = TypeVar("_T6") -_I = TypeVar("_I", bound=Iterable) -_K = TypeVar("_K") -_V = TypeVar("_V") -_M = TypeVar("_M", bound=Mapping) - -def set_disabled(run: bool) -> None: ... -def get_disabled() -> bool: ... -def disabled() -> ContextManager[None]: ... - -# To be more precise on instance_of use some overloads. -# If there are more than 3 items in the tuple then we fall back to Any -@overload -def instance_of(type: type[_T]) -> _ValidatorType[_T]: ... -@overload -def instance_of(type: tuple[type[_T]]) -> _ValidatorType[_T]: ... -@overload -def instance_of( - type: tuple[type[_T1], type[_T2]], -) -> _ValidatorType[_T1 | _T2]: ... -@overload -def instance_of( - type: tuple[type[_T1], type[_T2], type[_T3]], -) -> _ValidatorType[_T1 | _T2 | _T3]: ... -@overload -def instance_of(type: tuple[type, ...]) -> _ValidatorType[Any]: ... -@overload -def instance_of(type: UnionType) -> _ValidatorType[Any]: ... -def optional( - validator: ( - _ValidatorType[_T] - | list[_ValidatorType[_T]] - | tuple[_ValidatorType[_T], ...] - ), -) -> _ValidatorType[_T | None]: ... -def in_(options: Container[_T]) -> _ValidatorType[_T]: ... -def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ... -def matches_re( - regex: Pattern[AnyStr] | AnyStr, - flags: int = ..., - func: Callable[[AnyStr, AnyStr, int], Match[AnyStr] | None] | None = ..., -) -> _ValidatorType[AnyStr]: ... -def deep_iterable( - member_validator: _ValidatorArgType[_T], - iterable_validator: _ValidatorArgType[_I] | None = ..., -) -> _ValidatorType[_I]: ... -@overload -def deep_mapping( - key_validator: _ValidatorArgType[_K], - value_validator: _ValidatorArgType[_V] | None = ..., - mapping_validator: _ValidatorArgType[_M] | None = ..., -) -> _ValidatorType[_M]: ... -@overload -def deep_mapping( - key_validator: _ValidatorArgType[_K] | None = ..., - value_validator: _ValidatorArgType[_V] = ..., - mapping_validator: _ValidatorArgType[_M] | None = ..., -) -> _ValidatorType[_M]: ... -def is_callable() -> _ValidatorType[_T]: ... -def lt(val: _T) -> _ValidatorType[_T]: ... -def le(val: _T) -> _ValidatorType[_T]: ... -def ge(val: _T) -> _ValidatorType[_T]: ... -def gt(val: _T) -> _ValidatorType[_T]: ... -def max_len(length: int) -> _ValidatorType[_T]: ... -def min_len(length: int) -> _ValidatorType[_T]: ... -def not_( - validator: _ValidatorType[_T], - *, - msg: str | None = None, - exc_types: type[Exception] | Iterable[type[Exception]] = ..., -) -> _ValidatorType[_T]: ... -@overload -def or_( - __v1: _ValidatorType[_T1], - __v2: _ValidatorType[_T2], -) -> _ValidatorType[_T1 | _T2]: ... -@overload -def or_( - __v1: _ValidatorType[_T1], - __v2: _ValidatorType[_T2], - __v3: _ValidatorType[_T3], -) -> _ValidatorType[_T1 | _T2 | _T3]: ... -@overload -def or_( - __v1: _ValidatorType[_T1], - __v2: _ValidatorType[_T2], - __v3: _ValidatorType[_T3], - __v4: _ValidatorType[_T4], -) -> _ValidatorType[_T1 | _T2 | _T3 | _T4]: ... -@overload -def or_( - __v1: _ValidatorType[_T1], - __v2: _ValidatorType[_T2], - __v3: _ValidatorType[_T3], - __v4: _ValidatorType[_T4], - __v5: _ValidatorType[_T5], -) -> _ValidatorType[_T1 | _T2 | _T3 | _T4 | _T5]: ... -@overload -def or_( - __v1: _ValidatorType[_T1], - __v2: _ValidatorType[_T2], - __v3: _ValidatorType[_T3], - __v4: _ValidatorType[_T4], - __v5: _ValidatorType[_T5], - __v6: _ValidatorType[_T6], -) -> _ValidatorType[_T1 | _T2 | _T3 | _T4 | _T5 | _T6]: ... -@overload -def or_( - __v1: _ValidatorType[Any], - __v2: _ValidatorType[Any], - __v3: _ValidatorType[Any], - __v4: _ValidatorType[Any], - __v5: _ValidatorType[Any], - __v6: _ValidatorType[Any], - *validators: _ValidatorType[Any], -) -> _ValidatorType[Any]: ... diff --git a/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/METADATA deleted file mode 100644 index 5cf16a04..00000000 --- a/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/METADATA +++ /dev/null @@ -1,199 +0,0 @@ -Metadata-Version: 2.4 -Name: attrs -Version: 26.1.0 -Summary: Classes Without Boilerplate -Project-URL: Documentation, https://www.attrs.org/ -Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html -Project-URL: GitHub, https://github.com/python-attrs/attrs -Project-URL: Funding, https://github.com/sponsors/hynek -Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi -Author-email: Hynek Schlawack -License-Expression: MIT -License-File: LICENSE -Keywords: attribute,boilerplate,class -Classifier: Development Status :: 5 - Production/Stable -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Typing :: Typed -Requires-Python: >=3.9 -Description-Content-Type: text/markdown - -

- - attrs - -

- - -*attrs* is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka [dunder methods](https://www.attrs.org/en/latest/glossary.html#term-dunder-methods)). -Trusted by NASA for [Mars missions since 2020](https://github.com/readme/featured/nasa-ingenuity-helicopter)! - -Its main goal is to help you to write **concise** and **correct** software without slowing down your code. - - -## Sponsors - -*attrs* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek). -Especially those generously supporting us at the *The Organization* tier and higher: - - - -

- - - - - - - - - - - -

- - - -

- Please consider joining them to help make attrs’s maintenance more sustainable! -

- - - -## Example - -*attrs* gives you a class decorator and a way to declaratively define the attributes on that class: - - - -```pycon ->>> from attrs import asdict, define, make_class, Factory - ->>> @define -... class SomeClass: -... a_number: int = 42 -... list_of_numbers: list[int] = Factory(list) -... -... def hard_math(self, another_number): -... return self.a_number + sum(self.list_of_numbers) * another_number - - ->>> sc = SomeClass(1, [1, 2, 3]) ->>> sc -SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) - ->>> sc.hard_math(3) -19 ->>> sc == SomeClass(1, [1, 2, 3]) -True ->>> sc != SomeClass(2, [3, 2, 1]) -True - ->>> asdict(sc) -{'a_number': 1, 'list_of_numbers': [1, 2, 3]} - ->>> SomeClass() -SomeClass(a_number=42, list_of_numbers=[]) - ->>> C = make_class("C", ["a", "b"]) ->>> C("foo", "bar") -C(a='foo', b='bar') -``` - -After *declaring* your attributes, *attrs* gives you: - -- a concise and explicit overview of the class's attributes, -- a nice human-readable `__repr__`, -- equality-checking methods, -- an initializer, -- and much more, - -*without* writing dull boilerplate code again and again and *without* runtime performance penalties. - ---- - -This example uses *attrs*'s modern APIs that have been introduced in version 20.1.0, and the *attrs* package import name that has been added in version 21.3.0. -The classic APIs (`@attr.s`, `attr.ib`, plus their serious-business aliases) and the `attr` package import name will remain **indefinitely**. - -Check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for an in-depth explanation! - - -### Hate Type Annotations!? - -No problem! -Types are entirely **optional** with *attrs*. -Simply assign `attrs.field()` to the attributes instead of annotating them with types: - -```python -from attrs import define, field - -@define -class SomeClass: - a_number = field(default=42) - list_of_numbers = field(factory=list) -``` - - -## Data Classes - -On the tin, *attrs* might remind you of `dataclasses` (and indeed, `dataclasses` [are a descendant](https://hynek.me/articles/import-attrs/) of *attrs*). -In practice it does a lot more and is more flexible. -For instance, it allows you to define [special handling of NumPy arrays for equality checks](https://www.attrs.org/en/stable/comparison.html#customization), allows more ways to [plug into the initialization process](https://www.attrs.org/en/stable/init.html#hooking-yourself-into-initialization), has a replacement for `__init_subclass__`, and allows for stepping through the generated methods using a debugger. - -For more details, please refer to our [comparison page](https://www.attrs.org/en/stable/why.html#data-classes), but generally speaking, we are more likely to commit crimes against nature to make things work that one would expect to work, but that are quite complicated in practice. - - -## Project Information - -- [**Changelog**](https://www.attrs.org/en/stable/changelog.html) -- [**Documentation**](https://www.attrs.org/) -- [**PyPI**](https://pypi.org/project/attrs/) -- [**Source Code**](https://github.com/python-attrs/attrs) -- [**Contributing**](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md) -- [**Third-party Extensions**](https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs) -- **Get Help**: use the `python-attrs` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/python-attrs) - - -### *attrs* for Enterprise - -Available as part of the [Tidelift Subscription](https://tidelift.com/?utm_source=lifter&utm_medium=referral&utm_campaign=hynek). - -The maintainers of *attrs* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. -Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. - -## Release Information - -### Backwards-incompatible Changes - -- Field aliases are now resolved *before* calling `field_transformer`, so transformers receive fully populated `Attribute` objects with usable `alias` values instead of `None`. - The new `Attribute.alias_is_default` flag indicates whether the alias was auto-generated (`True`) or explicitly set by the user (`False`). - [#1509](https://github.com/python-attrs/attrs/issues/1509) - - -### Changes - -- Fix type annotations for `attrs.validators.optional()`, so it no longer rejects tuples with more than one validator. - [#1496](https://github.com/python-attrs/attrs/issues/1496) -- The `attrs.validators.disabled()` contextmanager can now be nested. - [#1513](https://github.com/python-attrs/attrs/issues/1513) -- Frozen classes can set `on_setattr=attrs.setters.NO_OP` in addition to `None`. - [#1515](https://github.com/python-attrs/attrs/issues/1515) -- It's now possible to pass *attrs* **instances** in addition to *attrs* **classes** to `attrs.fields()`. - [#1529](https://github.com/python-attrs/attrs/issues/1529) - - - ---- - -[Full changelog →](https://www.attrs.org/en/stable/changelog.html) diff --git a/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/RECORD deleted file mode 100644 index 2cf8b836..00000000 --- a/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/RECORD +++ /dev/null @@ -1,55 +0,0 @@ -attr/__init__.py,sha256=fOYIvt1eGSqQre4uCS3sJWKZ0mwAuC8UD6qba5OS9_U,2057 -attr/__init__.pyi,sha256=pVGImAUVovq2_TYl_r_HIYnGlyOaoCuEhxo-EvsnnSc,11325 -attr/__pycache__/__init__.cpython-312.pyc,, -attr/__pycache__/_cmp.cpython-312.pyc,, -attr/__pycache__/_compat.cpython-312.pyc,, -attr/__pycache__/_config.cpython-312.pyc,, -attr/__pycache__/_funcs.cpython-312.pyc,, -attr/__pycache__/_make.cpython-312.pyc,, -attr/__pycache__/_next_gen.cpython-312.pyc,, -attr/__pycache__/_version_info.cpython-312.pyc,, -attr/__pycache__/converters.cpython-312.pyc,, -attr/__pycache__/exceptions.cpython-312.pyc,, -attr/__pycache__/filters.cpython-312.pyc,, -attr/__pycache__/setters.cpython-312.pyc,, -attr/__pycache__/validators.cpython-312.pyc,, -attr/_cmp.py,sha256=3Nn1TjxllUYiX_nJoVnEkXoDk0hM1DYKj5DE7GZe4i0,4117 -attr/_cmp.pyi,sha256=U-_RU_UZOyPUEQzXE6RMYQQcjkZRY25wTH99sN0s7MM,368 -attr/_compat.py,sha256=x0g7iEUOnBVJC72zyFCgb1eKqyxS-7f2LGnNyZ_r95s,2829 -attr/_config.py,sha256=dGq3xR6fgZEF6UBt_L0T-eUHIB4i43kRmH0P28sJVw8,843 -attr/_funcs.py,sha256=Ix5IETTfz5F01F-12MF_CSFomIn2h8b67EVVz2gCtBE,16479 -attr/_make.py,sha256=H7OH2eWS5CnBzLUjNFE1WymfPrmF1r8fv2RPdt9MuYA,106129 -attr/_next_gen.py,sha256=BQtCUlzwg2gWHTYXBQvrEYBnzBUrDvO57u0Py6UCPhc,26274 -attr/_typing_compat.pyi,sha256=XDP54TUn-ZKhD62TOQebmzrwFyomhUCoGRpclb6alRA,469 -attr/_version_info.py,sha256=w4R-FYC3NK_kMkGUWJlYP4cVAlH9HRaC-um3fcjYkHM,2222 -attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209 -attr/converters.py,sha256=GlDeOzPeTFgeBBLbj9G57Ez5lAk68uhSALRYJ_exe84,3861 -attr/converters.pyi,sha256=orU2bff-VjQa2kMDyvnMQV73oJT2WRyQuw4ZR1ym1bE,643 -attr/exceptions.py,sha256=b4vMbnoQ3VpwWZhqrYi_ssXVCK8o2c4HQSS09cSUM9o,1990 -attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539 -attr/filters.py,sha256=ZBiKWLp3R0LfCZsq7X11pn9WX8NslS2wXM4jsnLOGc8,1795 -attr/filters.pyi,sha256=3J5BG-dTxltBk1_-RuNRUHrv2qu1v8v4aDNAQ7_mifA,208 -attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -attr/setters.py,sha256=5-dcT63GQK35ONEzSgfXCkbB7pPkaR-qv15mm4PVSzQ,1617 -attr/setters.pyi,sha256=NnVkaFU1BB4JB8E4JuXyrzTUgvtMpj8p3wBdJY7uix4,584 -attr/validators.py,sha256=m3QRzZTANr4f2C4eVdUoFg11NgXWak8Wat4qQTGhvcs,21553 -attr/validators.pyi,sha256=gM1ZmHaBckyYWI2EirpRNzqm3B19cw5Iq6B4Kno9YCM,4087 -attrs-26.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -attrs-26.1.0.dist-info/METADATA,sha256=TNQOaQ8jvzfLytNO_WdY4GLfHfB8hoM_fjzpW_H6OMw,8754 -attrs-26.1.0.dist-info/RECORD,, -attrs-26.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87 -attrs-26.1.0.dist-info/licenses/LICENSE,sha256=iCEVyV38KvHutnFPjsbVy8q_Znyv-HKfQkINpj9xTp8,1109 -attrs/__init__.py,sha256=RxaAZNwYiEh-fcvHLZNpQ_DWKni73M_jxEPEftiq1Zc,1183 -attrs/__init__.pyi,sha256=2gV79g9UxJppGSM48hAZJ6h_MHb70dZoJL31ZNJeZYI,9416 -attrs/__pycache__/__init__.cpython-312.pyc,, -attrs/__pycache__/converters.cpython-312.pyc,, -attrs/__pycache__/exceptions.cpython-312.pyc,, -attrs/__pycache__/filters.cpython-312.pyc,, -attrs/__pycache__/setters.cpython-312.pyc,, -attrs/__pycache__/validators.cpython-312.pyc,, -attrs/converters.py,sha256=8kQljrVwfSTRu8INwEk8SI0eGrzmWftsT7rM0EqyohM,76 -attrs/exceptions.py,sha256=ACCCmg19-vDFaDPY9vFl199SPXCQMN_bENs4DALjzms,76 -attrs/filters.py,sha256=VOUMZug9uEU6dUuA0dF1jInUK0PL3fLgP0VBS5d-CDE,73 -attrs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -attrs/setters.py,sha256=eL1YidYQV3T2h9_SYIZSZR1FAcHGb1TuCTy0E0Lv2SU,73 -attrs/validators.py,sha256=xcy6wD5TtTkdCG1f4XWbocPSO0faBjk5IfVJfP6SUj0,76 diff --git a/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/WHEEL deleted file mode 100644 index b1b94fd5..00000000 --- a/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.29.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/licenses/LICENSE deleted file mode 100644 index 2bd6453d..00000000 --- a/.venv/lib/python3.12/site-packages/attrs-26.1.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Hynek Schlawack and the attrs contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/attrs/__init__.py b/.venv/lib/python3.12/site-packages/attrs/__init__.py deleted file mode 100644 index dc1ce4b9..00000000 --- a/.venv/lib/python3.12/site-packages/attrs/__init__.py +++ /dev/null @@ -1,72 +0,0 @@ -# SPDX-License-Identifier: MIT - -from attr import ( - NOTHING, - Attribute, - AttrsInstance, - Converter, - Factory, - NothingType, - _make_getattr, - assoc, - cmp_using, - define, - evolve, - field, - fields, - fields_dict, - frozen, - has, - make_class, - mutable, - resolve_types, - validate, -) -from attr._make import ClassProps -from attr._next_gen import asdict, astuple, inspect - -from . import converters, exceptions, filters, setters, validators - - -__all__ = [ - "NOTHING", - "Attribute", - "AttrsInstance", - "ClassProps", - "Converter", - "Factory", - "NothingType", - "__author__", - "__copyright__", - "__description__", - "__doc__", - "__email__", - "__license__", - "__title__", - "__url__", - "__version__", - "__version_info__", - "asdict", - "assoc", - "astuple", - "cmp_using", - "converters", - "define", - "evolve", - "exceptions", - "field", - "fields", - "fields_dict", - "filters", - "frozen", - "has", - "inspect", - "make_class", - "mutable", - "resolve_types", - "setters", - "validate", - "validators", -] - -__getattr__ = _make_getattr(__name__) diff --git a/.venv/lib/python3.12/site-packages/attrs/__init__.pyi b/.venv/lib/python3.12/site-packages/attrs/__init__.pyi deleted file mode 100644 index 6364bac4..00000000 --- a/.venv/lib/python3.12/site-packages/attrs/__init__.pyi +++ /dev/null @@ -1,314 +0,0 @@ -import sys - -from typing import ( - Any, - Callable, - Mapping, - Sequence, - overload, - TypeVar, -) - -# Because we need to type our own stuff, we have to make everything from -# attr explicitly public too. -from attr import __author__ as __author__ -from attr import __copyright__ as __copyright__ -from attr import __description__ as __description__ -from attr import __email__ as __email__ -from attr import __license__ as __license__ -from attr import __title__ as __title__ -from attr import __url__ as __url__ -from attr import __version__ as __version__ -from attr import __version_info__ as __version_info__ -from attr import assoc as assoc -from attr import Attribute as Attribute -from attr import AttrsInstance as AttrsInstance -from attr import cmp_using as cmp_using -from attr import converters as converters -from attr import Converter as Converter -from attr import evolve as evolve -from attr import exceptions as exceptions -from attr import Factory as Factory -from attr import fields as fields -from attr import fields_dict as fields_dict -from attr import filters as filters -from attr import has as has -from attr import make_class as make_class -from attr import NOTHING as NOTHING -from attr import resolve_types as resolve_types -from attr import setters as setters -from attr import validate as validate -from attr import validators as validators -from attr import attrib, asdict as asdict, astuple as astuple -from attr import NothingType as NothingType - -if sys.version_info >= (3, 11): - from typing import dataclass_transform -else: - from typing_extensions import dataclass_transform - -_T = TypeVar("_T") -_C = TypeVar("_C", bound=type) - -_EqOrderType = bool | Callable[[Any], Any] -_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any] -_CallableConverterType = Callable[[Any], Any] -_ConverterType = _CallableConverterType | Converter[Any, Any] -_ReprType = Callable[[Any], str] -_ReprArgType = bool | _ReprType -_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any] -_OnSetAttrArgType = _OnSetAttrType | list[_OnSetAttrType] | setters._NoOpType -_FieldTransformer = Callable[ - [type, list["Attribute[Any]"]], list["Attribute[Any]"] -] -# FIXME: in reality, if multiple validators are passed they must be in a list -# or tuple, but those are invariant and so would prevent subtypes of -# _ValidatorType from working when passed in a list or tuple. -_ValidatorArgType = _ValidatorType[_T] | Sequence[_ValidatorType[_T]] - -@overload -def field( - *, - default: None = ..., - validator: None = ..., - repr: _ReprArgType = ..., - hash: bool | None = ..., - init: bool = ..., - metadata: Mapping[Any, Any] | None = ..., - converter: None = ..., - factory: None = ..., - kw_only: bool | None = ..., - eq: bool | None = ..., - order: bool | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - alias: str | None = ..., - type: type | None = ..., -) -> Any: ... - -# This form catches an explicit None or no default and infers the type from the -# other arguments. -@overload -def field( - *, - default: None = ..., - validator: _ValidatorArgType[_T] | None = ..., - repr: _ReprArgType = ..., - hash: bool | None = ..., - init: bool = ..., - metadata: Mapping[Any, Any] | None = ..., - converter: _ConverterType - | list[_ConverterType] - | tuple[_ConverterType, ...] - | None = ..., - factory: Callable[[], _T] | None = ..., - kw_only: bool | None = ..., - eq: _EqOrderType | None = ..., - order: _EqOrderType | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - alias: str | None = ..., - type: type | None = ..., -) -> _T: ... - -# This form catches an explicit default argument. -@overload -def field( - *, - default: _T, - validator: _ValidatorArgType[_T] | None = ..., - repr: _ReprArgType = ..., - hash: bool | None = ..., - init: bool = ..., - metadata: Mapping[Any, Any] | None = ..., - converter: _ConverterType - | list[_ConverterType] - | tuple[_ConverterType, ...] - | None = ..., - factory: Callable[[], _T] | None = ..., - kw_only: bool | None = ..., - eq: _EqOrderType | None = ..., - order: _EqOrderType | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - alias: str | None = ..., - type: type | None = ..., -) -> _T: ... - -# This form covers type=non-Type: e.g. forward references (str), Any -@overload -def field( - *, - default: _T | None = ..., - validator: _ValidatorArgType[_T] | None = ..., - repr: _ReprArgType = ..., - hash: bool | None = ..., - init: bool = ..., - metadata: Mapping[Any, Any] | None = ..., - converter: _ConverterType - | list[_ConverterType] - | tuple[_ConverterType, ...] - | None = ..., - factory: Callable[[], _T] | None = ..., - kw_only: bool | None = ..., - eq: _EqOrderType | None = ..., - order: _EqOrderType | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - alias: str | None = ..., - type: type | None = ..., -) -> Any: ... -@overload -@dataclass_transform(field_specifiers=(attrib, field)) -def define( - maybe_cls: _C, - *, - these: dict[str, Any] | None = ..., - repr: bool = ..., - unsafe_hash: bool | None = ..., - hash: bool | None = ..., - init: bool = ..., - slots: bool = ..., - frozen: bool = ..., - weakref_slot: bool = ..., - str: bool = ..., - auto_attribs: bool = ..., - kw_only: bool = ..., - cache_hash: bool = ..., - auto_exc: bool = ..., - eq: bool | None = ..., - order: bool | None = ..., - auto_detect: bool = ..., - getstate_setstate: bool | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - field_transformer: _FieldTransformer | None = ..., - match_args: bool = ..., -) -> _C: ... -@overload -@dataclass_transform(field_specifiers=(attrib, field)) -def define( - maybe_cls: None = ..., - *, - these: dict[str, Any] | None = ..., - repr: bool = ..., - unsafe_hash: bool | None = ..., - hash: bool | None = ..., - init: bool = ..., - slots: bool = ..., - frozen: bool = ..., - weakref_slot: bool = ..., - str: bool = ..., - auto_attribs: bool = ..., - kw_only: bool = ..., - cache_hash: bool = ..., - auto_exc: bool = ..., - eq: bool | None = ..., - order: bool | None = ..., - auto_detect: bool = ..., - getstate_setstate: bool | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - field_transformer: _FieldTransformer | None = ..., - match_args: bool = ..., -) -> Callable[[_C], _C]: ... - -mutable = define - -@overload -@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) -def frozen( - maybe_cls: _C, - *, - these: dict[str, Any] | None = ..., - repr: bool = ..., - unsafe_hash: bool | None = ..., - hash: bool | None = ..., - init: bool = ..., - slots: bool = ..., - frozen: bool = ..., - weakref_slot: bool = ..., - str: bool = ..., - auto_attribs: bool = ..., - kw_only: bool = ..., - cache_hash: bool = ..., - auto_exc: bool = ..., - eq: bool | None = ..., - order: bool | None = ..., - auto_detect: bool = ..., - getstate_setstate: bool | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - field_transformer: _FieldTransformer | None = ..., - match_args: bool = ..., -) -> _C: ... -@overload -@dataclass_transform(frozen_default=True, field_specifiers=(attrib, field)) -def frozen( - maybe_cls: None = ..., - *, - these: dict[str, Any] | None = ..., - repr: bool = ..., - unsafe_hash: bool | None = ..., - hash: bool | None = ..., - init: bool = ..., - slots: bool = ..., - frozen: bool = ..., - weakref_slot: bool = ..., - str: bool = ..., - auto_attribs: bool = ..., - kw_only: bool = ..., - cache_hash: bool = ..., - auto_exc: bool = ..., - eq: bool | None = ..., - order: bool | None = ..., - auto_detect: bool = ..., - getstate_setstate: bool | None = ..., - on_setattr: _OnSetAttrArgType | None = ..., - field_transformer: _FieldTransformer | None = ..., - match_args: bool = ..., -) -> Callable[[_C], _C]: ... - -class ClassProps: - # XXX: somehow when defining/using enums Mypy starts looking at our own - # (untyped) code and causes tons of errors. - Hashability: Any - KeywordOnly: Any - - is_exception: bool - is_slotted: bool - has_weakref_slot: bool - is_frozen: bool - # kw_only: ClassProps.KeywordOnly - kw_only: Any - collected_fields_by_mro: bool - added_init: bool - added_repr: bool - added_eq: bool - added_ordering: bool - # hashability: ClassProps.Hashability - hashability: Any - added_match_args: bool - added_str: bool - added_pickling: bool - on_setattr_hook: _OnSetAttrType | None - field_transformer: Callable[[Attribute[Any]], Attribute[Any]] | None - - def __init__( - self, - is_exception: bool, - is_slotted: bool, - has_weakref_slot: bool, - is_frozen: bool, - # kw_only: ClassProps.KeywordOnly - kw_only: Any, - collected_fields_by_mro: bool, - added_init: bool, - added_repr: bool, - added_eq: bool, - added_ordering: bool, - # hashability: ClassProps.Hashability - hashability: Any, - added_match_args: bool, - added_str: bool, - added_pickling: bool, - on_setattr_hook: _OnSetAttrType, - field_transformer: Callable[[Attribute[Any]], Attribute[Any]], - ) -> None: ... - @property - def is_hashable(self) -> bool: ... - -def inspect(cls: type) -> ClassProps: ... diff --git a/.venv/lib/python3.12/site-packages/attrs/converters.py b/.venv/lib/python3.12/site-packages/attrs/converters.py deleted file mode 100644 index 7821f6c0..00000000 --- a/.venv/lib/python3.12/site-packages/attrs/converters.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: MIT - -from attr.converters import * # noqa: F403 diff --git a/.venv/lib/python3.12/site-packages/attrs/exceptions.py b/.venv/lib/python3.12/site-packages/attrs/exceptions.py deleted file mode 100644 index 3323f9d2..00000000 --- a/.venv/lib/python3.12/site-packages/attrs/exceptions.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: MIT - -from attr.exceptions import * # noqa: F403 diff --git a/.venv/lib/python3.12/site-packages/attrs/filters.py b/.venv/lib/python3.12/site-packages/attrs/filters.py deleted file mode 100644 index 3080f483..00000000 --- a/.venv/lib/python3.12/site-packages/attrs/filters.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: MIT - -from attr.filters import * # noqa: F403 diff --git a/.venv/lib/python3.12/site-packages/attrs/py.typed b/.venv/lib/python3.12/site-packages/attrs/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/attrs/setters.py b/.venv/lib/python3.12/site-packages/attrs/setters.py deleted file mode 100644 index f3d73bb7..00000000 --- a/.venv/lib/python3.12/site-packages/attrs/setters.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: MIT - -from attr.setters import * # noqa: F403 diff --git a/.venv/lib/python3.12/site-packages/attrs/validators.py b/.venv/lib/python3.12/site-packages/attrs/validators.py deleted file mode 100644 index 037e124f..00000000 --- a/.venv/lib/python3.12/site-packages/attrs/validators.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: MIT - -from attr.validators import * # noqa: F403 diff --git a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/METADATA deleted file mode 100644 index 87936aca..00000000 --- a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/METADATA +++ /dev/null @@ -1,58 +0,0 @@ -Metadata-Version: 2.4 -Name: babel -Version: 2.18.0 -Summary: Internationalization utilities -Home-page: https://babel.pocoo.org/ -Author: Armin Ronacher -Author-email: armin.ronacher@active-4.com -Maintainer: Aarni Koskela -Maintainer-email: akx@iki.fi -License: BSD-3-Clause -Project-URL: Source, https://github.com/python-babel/babel -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Web Environment -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Internationalization -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Requires-Python: >=3.8 -License-File: LICENSE -Requires-Dist: pytz>=2015.7; python_version < "3.9" -Provides-Extra: dev -Requires-Dist: tzdata; sys_platform == "win32" and extra == "dev" -Requires-Dist: backports.zoneinfo; python_version < "3.9" and extra == "dev" -Requires-Dist: freezegun~=1.0; extra == "dev" -Requires-Dist: jinja2>=3.0; extra == "dev" -Requires-Dist: pytest-cov; extra == "dev" -Requires-Dist: pytest>=6.0; extra == "dev" -Requires-Dist: pytz; extra == "dev" -Requires-Dist: setuptools; extra == "dev" -Dynamic: author -Dynamic: author-email -Dynamic: classifier -Dynamic: description -Dynamic: home-page -Dynamic: license -Dynamic: license-file -Dynamic: maintainer -Dynamic: maintainer-email -Dynamic: project-url -Dynamic: provides-extra -Dynamic: requires-dist -Dynamic: requires-python -Dynamic: summary - -A collection of tools for internationalizing Python applications. diff --git a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/RECORD deleted file mode 100644 index 3b42d2a4..00000000 --- a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/RECORD +++ /dev/null @@ -1,1148 +0,0 @@ -../../../bin/pybabel,sha256=q-zqEA7-zA7yCEKC52e8ZudVaqhpnS3Q3UFcwZF7gEA,252 -babel-2.18.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -babel-2.18.0.dist-info/METADATA,sha256=ETmbwDprgf0g6LgoIzkgUqjblpqO8VA4Kwz1mWr89Bo,2222 -babel-2.18.0.dist-info/RECORD,, -babel-2.18.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92 -babel-2.18.0.dist-info/entry_points.txt,sha256=Y2Cr1P3E8Yt7kqzvVz4wnTvD1H3-BVD4FOkVqHIGBfc,750 -babel-2.18.0.dist-info/licenses/LICENSE,sha256=hrrVdYx4WboGJR-PEu7oheyjE2mSPfsrh6fBPDY02HA,1531 -babel-2.18.0.dist-info/top_level.txt,sha256=mQO3vNkqlcYs_xRaL5EpRIy1IRjMp4N9_vdwmiemPXo,6 -babel/__init__.py,sha256=iAtaegCgnkkqd3t4mTON700DPw6YvOeVn-zdWKrGhEM,838 -babel/__pycache__/__init__.cpython-312.pyc,, -babel/__pycache__/core.cpython-312.pyc,, -babel/__pycache__/dates.cpython-312.pyc,, -babel/__pycache__/languages.cpython-312.pyc,, -babel/__pycache__/lists.cpython-312.pyc,, -babel/__pycache__/localedata.cpython-312.pyc,, -babel/__pycache__/numbers.cpython-312.pyc,, -babel/__pycache__/plural.cpython-312.pyc,, -babel/__pycache__/support.cpython-312.pyc,, -babel/__pycache__/units.cpython-312.pyc,, -babel/__pycache__/util.cpython-312.pyc,, -babel/core.py,sha256=47mzcxFpj3j16ZV7VhvZOdJQWBo4rlutQBD0oqM6G7g,45775 -babel/dates.py,sha256=jRsuCS09RAsW8gqixQk23Ffn69FieZU85fDNTiAzL-8,76060 -babel/global.dat,sha256=o4ncdOHrpbYBw8e0F8jQe55B8Zk3DRDMLMcMdxdxFW4,461219 -babel/languages.py,sha256=vySUM88KPgjZIfp8F98rzg0eBWUxBp4VSv3daVZzQrI,2866 -babel/lists.py,sha256=AGtv7uAV6lz4bs4F9OBZ1zcMctNsC_w8_ClJJT7D4LA,4380 -babel/locale-data/LICENSE.unicode,sha256=tMCujvBPcFn5bOW74EZ_n-b22Bu-E1F3Ad_rlh-00LY,2033 -babel/locale-data/aa.dat,sha256=CI5kOVaXWX2CQoHT6AXhrOMwFPCiQyd7Wke2QBJ1UtE,2795 -babel/locale-data/aa_DJ.dat,sha256=VKjSgKVsftG91nj0-AQMYFuF_p4YAGfRkgAvycB2EYU,1112 -babel/locale-data/aa_ER.dat,sha256=6b6qROwoeOq0-batM1Z7Ji2gvGDogl3ODcftt1Jkm9g,637 -babel/locale-data/aa_ET.dat,sha256=piwFu94H7e-jPwd4ZaWiDc-j2nAH_GiC5UZ2IxMx7bs,635 -babel/locale-data/ab.dat,sha256=xeio1NLBNIAXcafnonWjO3w0zofwM-aKr0-RiaL7pRQ,95311 -babel/locale-data/ab_GE.dat,sha256=b5be2Uv1YtP-bkMasJMzoQPxJPNLFqifXnCiK8rCyLE,635 -babel/locale-data/af.dat,sha256=pO9_3BpJSQAJ3gZ_j6ZdUpGq3zqEv6CIZZ-CpJTXwr4,144732 -babel/locale-data/af_NA.dat,sha256=zif__TyNY44mSCYOCrrglsyr_H31JhBNPwSJftg8200,1450 -babel/locale-data/af_ZA.dat,sha256=6lT-mLg7LRtyAEhWhiF2xVtiPyzEa28U6hsFw_BMrZE,635 -babel/locale-data/agq.dat,sha256=JJbF6NFR9qwwQTrIyrLh04DFJniIqDuFsbmHDGu5ThM,16628 -babel/locale-data/agq_CM.dat,sha256=hYtg7pRw7J6TsYHvhlEEv7DqpyXoK3KS9DAqtYtgBX0,636 -babel/locale-data/ak.dat,sha256=L73y_ylhZy39SlEaAwA4jCqP_92qLvIlsagl-o2RlKE,116224 -babel/locale-data/ak_GH.dat,sha256=tM-OSpXWyBwZFfKPVStWBbFv3dwNj_7AqcBz4ONrOQs,616 -babel/locale-data/am.dat,sha256=TddfeRISFvzjTFpB-vH3gjSKs2r8iT3CYckrriibXZY,173260 -babel/locale-data/am_ET.dat,sha256=2VZDSh5bY9vLLuTqcKYG66p5OYlvPRPX58rpNTFWPok,635 -babel/locale-data/an.dat,sha256=a1gEztcqWoMLW3f5_jlDxcPsYY-ItHKVud7gjisrS4E,28050 -babel/locale-data/an_ES.dat,sha256=rQuq-zKj7-fCjY-5fGGoGJT1x60pAyCEAVEswXPnHrI,653 -babel/locale-data/ann.dat,sha256=fUenvOdPjqayLEiVDRNncK8SOBi9gAGAdxMuOATlsI0,737 -babel/locale-data/ann_NG.dat,sha256=0i2K6cv6eCurgFgwtiCr09hYwZCA1kHkQaKnDyCD86M,617 -babel/locale-data/apc.dat,sha256=G96W-ebLqQlTuPN3-VqSfAY-GF0K99c6uXHza5f4e2g,1564 -babel/locale-data/apc_SY.dat,sha256=tagFeQz8BA9Pc7RMqkce1ZalfKIBjqg0SR4NlpGQaaM,679 -babel/locale-data/ar.dat,sha256=yAijlkkaH-RgRQidvCyNBkpi9Z7OTC9cNfIbKo4n4gk,288582 -babel/locale-data/ar_001.dat,sha256=hRKDI3wzxO5T191MfSdCn_MZ6ckM2Rz_r__0JZqS00s,1707 -babel/locale-data/ar_AE.dat,sha256=hEbIYUoNE5mRLOVXhmkGsvT0NAcq-rxs51VIMMzG9LY,982 -babel/locale-data/ar_BH.dat,sha256=FoQ2c3E3TDqKcioarWBvLi_MllJmSd_56r0U45_gHWw,720 -babel/locale-data/ar_DJ.dat,sha256=qbNam_QAmhobsIUfJLCL9pe146SvcmNIE_L3cWpmkBs,698 -babel/locale-data/ar_DZ.dat,sha256=_Y-JLC5hincnxze7sGmEpb7N4JnBh7LdFdaVPcSMDvM,1263 -babel/locale-data/ar_EG.dat,sha256=qE3gLQ6BzLaBOPNJVgaBhPQiIL2kQ4AiD_jLl7o52IM,720 -babel/locale-data/ar_EH.dat,sha256=a0xJdGJYzGFzfvwxZVDP7yURlTm93FQSTQ5qp3XDMYo,658 -babel/locale-data/ar_ER.dat,sha256=l-WjJfxm7kP05-Hnr5PingHaKWJ46ScfW4KEBcwD5HM,679 -babel/locale-data/ar_IL.dat,sha256=O3UYAjf4BB3NdcSQTn-RzNjyj4kzTBhX3q7E8-9Aihw,1264 -babel/locale-data/ar_IQ.dat,sha256=7-DnoHqsoBDe-ZXe0rIcEotb-F_CFyChc73dwnXuUoE,1975 -babel/locale-data/ar_JO.dat,sha256=hT1z-Me5SOjul3BHFubcqlyEk9H57fwD4EFENddiVt8,1398 -babel/locale-data/ar_KM.dat,sha256=Zx5XtfTuRPiW6iViOjeal_Fq-18EA7GuERViMP1_mc4,1230 -babel/locale-data/ar_KW.dat,sha256=1wSXHTcgFKwhNciVS8IvGKvkzzqOIMBFykEJEQXdhEE,720 -babel/locale-data/ar_LB.dat,sha256=-SuXO6LdhXNi4f_PHNrlxl8iXX47perQ_oS19bwRGpI,1414 -babel/locale-data/ar_LY.dat,sha256=2OxSGD1gkiWPcaRHd1qQv9ifG82WEjGahKqhsoFXfnY,1249 -babel/locale-data/ar_MA.dat,sha256=hTOwsXC8tsJxBVlksxmAMlIkn4kK50juJa6TuAPFkp0,1559 -babel/locale-data/ar_MR.dat,sha256=Kq7uT5LxvYXnGWz-cVcI6yDNximu9hZIpoKydmVse78,1359 -babel/locale-data/ar_OM.dat,sha256=HVOqTfoH3JVTHJL-yDBiIafNfckU7CIcO_sIzXBffKw,720 -babel/locale-data/ar_PS.dat,sha256=F-HZRPcMe_tJ8r9V01h-7mQ0UKQxQmuWuXNSi94MVMM,1336 -babel/locale-data/ar_QA.dat,sha256=UKfLpyX856fNgeifdr22wXpDncirWJi9NxqfSnS-7fw,720 -babel/locale-data/ar_SA.dat,sha256=8WE8HwCwO-OT_G2JBSgmPWSzyHOdIA3zajXPbBZr2Tg,24787 -babel/locale-data/ar_SD.dat,sha256=f7aUKlrKmmC7ijTjVwn5r9m3mfZfEob1a7GHVCj4Qxk,720 -babel/locale-data/ar_SO.dat,sha256=4LD_gds2VsvxdFHpFo56CxTFenzNdvEkMofc4oTGBTU,677 -babel/locale-data/ar_SS.dat,sha256=T6eeWPeFejmBC7InKqg-6qmWoer1vjl6srHe_5ybkDU,700 -babel/locale-data/ar_SY.dat,sha256=u42uhje_zt806WlUMpp5sIdi1yjw3Yv7vYbl6aELsyc,1398 -babel/locale-data/ar_TD.dat,sha256=OG7NYtRrJiDeRt7HP-B4ta5ri-6fdSnYomppfy2A4ec,658 -babel/locale-data/ar_TN.dat,sha256=xO974NyO5T-pRs-5b55sfQFGIwU2Y4q3VQUeqtqWf80,1201 -babel/locale-data/ar_YE.dat,sha256=vu8wYJgzcJ1douKfKLx3-hAg1sjndD5_PTHWG_bzRL0,720 -babel/locale-data/arn.dat,sha256=JB-QfwCOVQbVrLRlq2LMxU6SHO-V4ZaU2_0W15-0Rfs,721 -babel/locale-data/arn_CL.dat,sha256=cf_JrdIAxFwbltVwtloNC9527Du7zaoBDM-_KmctSvE,636 -babel/locale-data/as.dat,sha256=xoTmhieoAVjw59h2-_pLp0wjfZPVvYMV6qDSJjLrjq8,209876 -babel/locale-data/as_IN.dat,sha256=bE-OZ0Y3d2-QtD23ztjluSsFk1_RFNYMFzKgBC3waw4,658 -babel/locale-data/asa.dat,sha256=zzPu7L8RV8SVEKqeRiGIoWLBCJkeS0acDh0pYq87qMI,15492 -babel/locale-data/asa_TZ.dat,sha256=U0GU10aP58LxCeqyjl69RQCZi_6tTQxm2vni6XVkoqY,617 -babel/locale-data/ast.dat,sha256=_Qeoc0gz1ag9MkmNIuv4ufVjk0fH5xtW4M7vp8ST4hY,168961 -babel/locale-data/ast_ES.dat,sha256=51kFxigV5COuiPiNisp-OBFYMith5rUgBmU48rGk7Hc,654 -babel/locale-data/az.dat,sha256=2EcwMc8qIqCW_F0Ic2XjFQthkCGyfLNzJzJZU99ZcaA,173802 -babel/locale-data/az_Arab.dat,sha256=IGBla73oKavlLHhtIW37idHxTkmQWZ-S8NE54b65d4k,7499 -babel/locale-data/az_Arab_IQ.dat,sha256=VsM0-KQPCP4MUw-2wMsBv-F467cr2Bup3jfxx8Fc3ZE,678 -babel/locale-data/az_Arab_IR.dat,sha256=ROb0gFTPwK5CcjpQMB7CJ662eld9sZj7IA6QMrvxgto,678 -babel/locale-data/az_Arab_TR.dat,sha256=EV6cNf3keBNREHkfF98GGZGOZz4L5dxf8pFbujInaQY,635 -babel/locale-data/az_Cyrl.dat,sha256=ru4QYee174toLQsGPJn-d6xe65FsLQ87vBV9AW0o4D8,35540 -babel/locale-data/az_Cyrl_AZ.dat,sha256=GRZjR-gp6JYeLNxoiAKNdv9bkTmS1bpYnRJ0c9YIGws,635 -babel/locale-data/az_Latn.dat,sha256=aK2ziytg5Ig6GYk9tVCq4BEyV6UYy9Ip7F1_i4Pq1jw,2258 -babel/locale-data/az_Latn_AZ.dat,sha256=GRZjR-gp6JYeLNxoiAKNdv9bkTmS1bpYnRJ0c9YIGws,635 -babel/locale-data/ba.dat,sha256=Ly1V4wBNM4KCDGtuy9oCug4fZzVNEFqXOI8XKhkWFOQ,732 -babel/locale-data/ba_RU.dat,sha256=8YcwUOx3h4BcSfUIumKJ4wcFyiSgBM74SqenVIQUKi8,653 -babel/locale-data/bal.dat,sha256=mYMTNzej_lac3GimNq9dYgLY3Q4RtXpEvUkQ-zfzVqw,12849 -babel/locale-data/bal_Arab.dat,sha256=25CVl5rF7FqKEeHzAmt0BccVI_ODqmMP86KqQ4i6PL8,934 -babel/locale-data/bal_Arab_PK.dat,sha256=sy4tpUtN1St5yFoTp8C6-vi2bFGPjSaG_zDbY16Q988,636 -babel/locale-data/bal_Latn.dat,sha256=X9sEvC7SU0VUFnapzFKUq0nvc1az1c9lHmrCPHPCg4U,116909 -babel/locale-data/bal_Latn_PK.dat,sha256=sy4tpUtN1St5yFoTp8C6-vi2bFGPjSaG_zDbY16Q988,636 -babel/locale-data/bas.dat,sha256=hts2-gooZheGxndoyiVM9NwFrAitthfb4IPr7TtYb2U,16673 -babel/locale-data/bas_CM.dat,sha256=WBq-_rOvkBnT5pBaCyq844LLSRReQzg2HM0u7GmhKKM,636 -babel/locale-data/be.dat,sha256=8L6Q0C4wjbLMC9I7ltFoY1prATuRd-8nn0udprYBI6A,272322 -babel/locale-data/be_BY.dat,sha256=yN4Rf2-HqinMscTfe4jKEYpsCEaddHce6K25ol595Ag,635 -babel/locale-data/be_TARASK.dat,sha256=93xDxO06nuAugpWjrnx6C5k8Fs26HIAolUcUPuoHhbg,104118 -babel/locale-data/bem.dat,sha256=zBh5HxY-hRgaQd95anmqMAPP0UVzSV7JdcuqtJ78t1c,5782 -babel/locale-data/bem_ZM.dat,sha256=nQvsLK0FOSlgf6wspQdbj00IveO62c2Mvxjzn4eHGSs,617 -babel/locale-data/bew.dat,sha256=mfKR23PSPyS0AHxbaSuMvufZZQlMICxDGuPv78MtDbU,122095 -babel/locale-data/bew_ID.dat,sha256=JWKbnUQc4ZsJYTcBFypK3gSyFRK6Kp8UFEqdytnPMnM,636 -babel/locale-data/bez.dat,sha256=_saESrmbxsbQnGLACykFIs-aLUC-N7IigpyPWGLBebI,16300 -babel/locale-data/bez_TZ.dat,sha256=OwcKelhUxQWu-8Jm9nf2vCay6-8LQvg-ueZSN_mrGz0,617 -babel/locale-data/bg.dat,sha256=ofd_Tv4onIP1cE1pVO-171ySR9JJXuHKzpWqc-vtLP8,227940 -babel/locale-data/bg_BG.dat,sha256=CERFyuC4Dw9BfAZapqaq69iKAp7rBXWNCkLWAL5tAhQ,653 -babel/locale-data/bgc.dat,sha256=gW0lkQNX_h1SATbqzIqe0twmGqqfigkbqo7gvBCG1AI,2493 -babel/locale-data/bgc_IN.dat,sha256=hO39nPiyUx1kfCdagpCQ8lzPLycjdfk59jFQgsd3Qec,659 -babel/locale-data/bgn.dat,sha256=8RKzmGYGYkn5IF7NHRgPtaI9ozwCeEKoUE4aXQpItKY,28964 -babel/locale-data/bgn_AE.dat,sha256=5jo7AWXMNdiiEf-RqzB5xRVj0R89NFdwq5RAIEAeeRE,636 -babel/locale-data/bgn_AF.dat,sha256=UXCb78n57adZBdKHEGnhbmeVYppzlSscVOWnQNxfJOQ,679 -babel/locale-data/bgn_IR.dat,sha256=iXxyPD5Z-ouDWFpOE2atoi6-HHGoD6PNaT9c-TH6x7o,679 -babel/locale-data/bgn_OM.dat,sha256=3O0CzFrjWxOQFdN6-lDhCxRngoXfadd_dwYoVyjK9jA,679 -babel/locale-data/bgn_PK.dat,sha256=gdVUcc-isHd9lf0S-AmFoPIMwDZ8ay6NY4o1udWI6Yo,636 -babel/locale-data/bho.dat,sha256=JQAEhmajFwUOKB7ACN1-ZPcoKcXJaq0-FhsFCVcvn_8,2875 -babel/locale-data/bho_IN.dat,sha256=UcL_aZq35dOHME0Fux9zyt9B6BOTxyR5UvaGWKLs1jE,659 -babel/locale-data/blo.dat,sha256=I0gfsGv80xf7do-oxzjkh2or934YxYgDMkM9RHf7UXk,168499 -babel/locale-data/blo_BJ.dat,sha256=LM79oUzVI4JtLYEUklfbQxoweFAslN9jS-Ex-dya0mE,617 -babel/locale-data/blt.dat,sha256=6G2E9osBy9OrG1iUKLD9cwcRk3IV7fT11iI8zFkLaeo,723 -babel/locale-data/blt_VN.dat,sha256=vpTQEJYssmuBUn_pYqx7ZtYONNNz02_oBYlDMdWqKAc,636 -babel/locale-data/bm.dat,sha256=K0dLlxoDfJb4QLwa_D0SXfrbD53Xed_EJ21843fY9hY,15798 -babel/locale-data/bm_ML.dat,sha256=PAGrUwc1S6q32DDHTu5LkePFXe_M_i_WxIY4oLuUnzw,616 -babel/locale-data/bm_Nkoo.dat,sha256=UiQ-oKSXYmbnUj9nNXi-9Afx2taBc5EiN-JM1huGOno,2805 -babel/locale-data/bm_Nkoo_ML.dat,sha256=PAGrUwc1S6q32DDHTu5LkePFXe_M_i_WxIY4oLuUnzw,616 -babel/locale-data/bn.dat,sha256=00cRKYrwyk5snhrekLj0TQBVzZlV_PjQ0aLlsApgwxw,219185 -babel/locale-data/bn_BD.dat,sha256=mVW90kQmhDcwzKmkmgNAt5iMq1pLWmNndNID9fspVjY,635 -babel/locale-data/bn_IN.dat,sha256=Y4d5tMv6rG2A1fTcfN7-cMeZ1iH8XuqdZgpEBUy_FTg,4035 -babel/locale-data/bo.dat,sha256=2eVqnOFPv04Ix-1FVPHlDQ9UVTQC1JtC7-5lQ89aX7A,20102 -babel/locale-data/bo_CN.dat,sha256=xOnyre81Z1IHnWV5mJH0x313h-ZWu84hIXcMuB50IT0,635 -babel/locale-data/bo_IN.dat,sha256=GvYCgtrOD5zYYsfMtVmkCv8-y6His8Lg7iuytEGjnMI,1307 -babel/locale-data/br.dat,sha256=rBs1Bp9H6Q4ECO2mouoJmnCW0ux5CLPx1zy32GvpkCo,272612 -babel/locale-data/br_FR.dat,sha256=3oVDdZd7Xf4Q-hwmYraF42SM_3NtvSoiZlmlh6gSNOs,653 -babel/locale-data/brx.dat,sha256=0L8rfQwjwtj3y5M5zzdEB10way4I9P2m8A2E0mTIcac,160164 -babel/locale-data/brx_IN.dat,sha256=NKuJh0cEhRQasy6JAGUi5HbqdnhhEFTQ_7osgtdG5Iw,659 -babel/locale-data/bs.dat,sha256=YVLbXGLHf6QZ9vYnCjxjO3uBImuYnoxR-cEuBPVbP_w,213968 -babel/locale-data/bs_Cyrl.dat,sha256=oqFwEFFfYqyzkVvswj8CR2B_RCb51Myv8jQaDDjz0u8,219235 -babel/locale-data/bs_Cyrl_BA.dat,sha256=oA7rsQckE_IxCpXXB3F3KWQwM3WZTR7WAdYxJN98oeM,635 -babel/locale-data/bs_Latn.dat,sha256=6L9SY85hONRHO4Jz389C2Ziy7llKcDoyVNiE96ozCJ8,1990 -babel/locale-data/bs_Latn_BA.dat,sha256=oA7rsQckE_IxCpXXB3F3KWQwM3WZTR7WAdYxJN98oeM,635 -babel/locale-data/bss.dat,sha256=n9LaBwBIJ7adjJ63G7Q9o00llhPJtkgmeEp08nAa4D4,978 -babel/locale-data/bss_CM.dat,sha256=TD7ixCHREfOLDsDJn8FY5YqGYH9czdEzdULCIl_0GhM,636 -babel/locale-data/byn.dat,sha256=qT32E4jh6H_qm7HYSsAbKmaKluaLrGNXNl3F3FivncE,13403 -babel/locale-data/byn_ER.dat,sha256=oAaZEqMJuTbMITO-ZO6gg8qjwROukzh5VgjtLp6rqD4,617 -babel/locale-data/ca.dat,sha256=CIT0-7X7_e8RKWRrhLSmG-aBC2D51-hvRrTV-7iBav8,184038 -babel/locale-data/ca_AD.dat,sha256=tsLumbhbbeXw627WKi-sQdSTFwNpcSRvJIEYcrOMqOM,653 -babel/locale-data/ca_ES.dat,sha256=T-zv3FfnCJA2WUQTeBADKV82ghY-r60wk8mXtD9wbnI,653 -babel/locale-data/ca_ES_VALENCIA.dat,sha256=7Q59_PX0VWyLsDkC-qscZ-dKJ9AFzz7kCzJz4fTdBWY,2976 -babel/locale-data/ca_FR.dat,sha256=5HUFYUl5QRaUMwJxGLIqUhDO4_wF_crrr346wJmEOZo,672 -babel/locale-data/ca_IT.dat,sha256=Zh2rtOq8MIDC2CQXWnhp5HOvPfXNxhRGnnZViOgc1Mg,653 -babel/locale-data/cad.dat,sha256=XxlMk1b9CWE3wERhhQQN0wxW7iq8Mrngb05RXenmaSI,2476 -babel/locale-data/cad_US.dat,sha256=FWV1sofgr9no5P9yBxNDLeKWoQd80XEYEilmLwamkSc,654 -babel/locale-data/cch.dat,sha256=J4S73psT7C_cXs1VNNYow47plBodQ4PKINC59kzvfAw,2430 -babel/locale-data/cch_NG.dat,sha256=0adhw9xNMlVEqDVKvHe4HCPtbjcw5NSo4ny_pOjl0-4,617 -babel/locale-data/ccp.dat,sha256=Jn_O5MwN6DMGxNrXUpdrhT_z5ZOYh3-Sm5yGUcV8bII,207292 -babel/locale-data/ccp_BD.dat,sha256=f00776fnI484B-mzW0rY22KNkzV6wtmk7uxrSK_x9bU,636 -babel/locale-data/ccp_IN.dat,sha256=jMJimrQrx-b96hBSLi8sOyHtYRwNZryt-he0rhwEZ2s,659 -babel/locale-data/ce.dat,sha256=yuopW5QCq1u8E1Rs0hEOv_y1uvbem_P3lK-_AvU5zLE,135403 -babel/locale-data/ce_RU.dat,sha256=NgNmp1uFnm2MT-s_yx_V8KuW1dSR6_SEWf4NcqrAL9o,653 -babel/locale-data/ceb.dat,sha256=fwgJPAV6TfvyavZUW-9pKeLUacMuH03hCkSkHdIGm8I,100779 -babel/locale-data/ceb_PH.dat,sha256=Z2zXPyNI3Yu3QcGL6lT1nIsl04vDDLFvyd9GeucDtqA,636 -babel/locale-data/cgg.dat,sha256=vgLxp4dDUjKyHy-36IvfaPEy6IiXbDiMKShuEKJuXHU,15621 -babel/locale-data/cgg_UG.dat,sha256=HZBma4MFuX2Q7sk-SOYk5OUkbMXWWr_tEInOqPboNYs,640 -babel/locale-data/cho.dat,sha256=hWEL5XrIWAnqjYSb78iPaj2xh34KDcPYAMc46S7ZLuU,795 -babel/locale-data/cho_US.dat,sha256=T3CacgkE-uKPi0CV7fbcCLTNfdjEco_A50vYciuXGGI,654 -babel/locale-data/chr.dat,sha256=4a6eJjX1JZ-inCk-J8gHJq1QtoK3FpOXhOlQMDbHjwM,187187 -babel/locale-data/chr_US.dat,sha256=wZE2RrJ-5jjUQa1RCJgL7hkXdsU3t91orD-ULDYMXSM,654 -babel/locale-data/cic.dat,sha256=KgrmFl1fudc1tdR_g39YkGR6bcIedQMFnUjh831VIhM,2732 -babel/locale-data/cic_US.dat,sha256=DvW1XJS1hEp_W_5ol0pIECeUb3RqXsyieKVS8J6caJ4,654 -babel/locale-data/ckb.dat,sha256=s6wh2TRjabPo4G64RMoQD_J7FK77RBwsK8R0TH1xytI,35553 -babel/locale-data/ckb_IQ.dat,sha256=Osgu1RAUf2yQpZ-nNBKMxTFJnH32ItLg0kVVzjNfmeo,679 -babel/locale-data/ckb_IR.dat,sha256=6wWzlZWn7oDmGIdnF7UtCfIcUgQ1zvMxtAcEw4bbM-g,1231 -babel/locale-data/co.dat,sha256=NbKZ99wTpG7Y64LnyxTQJIo5SYQaUAOybc68DN6IbUY,12637 -babel/locale-data/co_FR.dat,sha256=DAmwXn4n1mRSsT17-DcU0vdVSI_JfVR8oGOnj7hTFHk,653 -babel/locale-data/cop.dat,sha256=MAhXKI-x0mI10Vlgo35ZvUTsKkyx3yrzVIDMfya68rA,693 -babel/locale-data/cop_EG.dat,sha256=ggMojdy-Ua0lFBG-khgVqDiiyDqP6v8JKJzdp4hv-kU,679 -babel/locale-data/cs.dat,sha256=o7qnhuIqxpPIfD-oyVLsvpmhtRiIOzgJBoMaZvyvPL4,247800 -babel/locale-data/cs_CZ.dat,sha256=keaiqSsGtB_axj8FvdYDOCDmlg2PbQMI6qnW-2mFUTY,653 -babel/locale-data/csw.dat,sha256=a31wJpIkcegvNW9_O8LfIY4-HK0zpLn4MBUEBb9C3PQ,20303 -babel/locale-data/csw_CA.dat,sha256=aWccZHGNU7G4JJdlGRnqyqm0ROubX0rwI9SnH_EhAxU,636 -babel/locale-data/cu.dat,sha256=mlGQkibxBcukKPAJawHruBMuw21x1j42ebTnraxW55w,16865 -babel/locale-data/cu_RU.dat,sha256=GcbqgE8Mlglk5EGGSVcCR-Zb8AmCqHGE8cbmAUSbpdY,653 -babel/locale-data/cv.dat,sha256=BUqS-4RvAuycOlAm_k33eV98VCp5bIUmrWUqjhmsqls,198348 -babel/locale-data/cv_RU.dat,sha256=oVPRtcHzmRmkAy26Bv1jLiWmm0P18ruvHlCQ5F3IFY4,653 -babel/locale-data/cy.dat,sha256=-bN00WqRhdnCL9VdnGgtUtwbmZ7n2r96oZdLuxSP8KM,276313 -babel/locale-data/cy_GB.dat,sha256=dyPULIteKk9gP7lGQiDtsiH5QDqNdlxMdK__2dq90dg,653 -babel/locale-data/da.dat,sha256=Aw20nDcBvnWWpy2IYvg9S8hJVoT-gavJ_LokmwppLL4,171762 -babel/locale-data/da_DK.dat,sha256=qNTw1H8WXsV8qGBHxu6noCUOV3BcoR63ERJF8Xc86Ls,653 -babel/locale-data/da_GL.dat,sha256=9BNeKX1-U4NDr3pBU1ZnCGAasdYZjBMsvpdS5uTf9qs,616 -babel/locale-data/dav.dat,sha256=LBf1uH_Q7632sBVhZgQSrdn5LU2pyIU389h4LircJM4,15690 -babel/locale-data/dav_KE.dat,sha256=MfFFEDT6fib5be-EmziiEF0NAnEV6lXD7svYtHUtmn0,636 -babel/locale-data/de.dat,sha256=l6ABVg2zXPX9Ws9g2-Weve05S46ii0hnbuCViS6mtmA,178630 -babel/locale-data/de_AT.dat,sha256=OhoRvklFcyYUJXplBoK2rAITKRi6r81lwOaixIuffcA,2010 -babel/locale-data/de_BE.dat,sha256=wvdWykhix9Slofcjm2tx2aEhMjffmMLsNrd8709NIR0,653 -babel/locale-data/de_CH.dat,sha256=PH2rDxHA3lpTBkPMCwWjapEDJtgrkHhq2_pAdSKSiXE,4091 -babel/locale-data/de_DE.dat,sha256=SM44YjGlqgh7bKlXvUdt53t9hnCX5o46ujI4IrGPpeI,653 -babel/locale-data/de_IT.dat,sha256=ALPY2QGlR9-kphEk4KggHVS51fQwEDVRd7aHLIiP9fw,1448 -babel/locale-data/de_LI.dat,sha256=FnGRiippDTTFRFswU_4PblQruAuWuPEpa8cGbXPJpbE,1413 -babel/locale-data/de_LU.dat,sha256=QAkvLfyKmZdQVfkk2BbE2AMisK1i7yhRetcApqn4dxA,1067 -babel/locale-data/dje.dat,sha256=6NxQgBas9S5kmjbRcbcuuIIxKaI5_MlfmSUltxJuraY,15530 -babel/locale-data/dje_NE.dat,sha256=lU1vag_HJ4OrbYNYWFXuSdtqLwSlwBbvD6Tg-hl36e4,617 -babel/locale-data/doi.dat,sha256=JrHDjsVlGntLL6qYf9NH52BWAGCSGeGVaHg_rgUiGkM,47442 -babel/locale-data/doi_IN.dat,sha256=-q_oAqvdTizFG1DB_1p8sZVyjbaFFQ_FFPtNCyVn9ZA,659 -babel/locale-data/dsb.dat,sha256=KvZxymKZ5ZbxQ-4gS_uvnNkrYMYn3D-BKz0MGAn5488,203926 -babel/locale-data/dsb_DE.dat,sha256=Z2Dzjx7eXscEBEAb-ymvb1GKP4GVNCt5L4i-BYC_vrE,654 -babel/locale-data/dua.dat,sha256=lmO8ssPF9bIgTU2SM_SzrGWodmsGsmbS485uZNDasq4,4827 -babel/locale-data/dua_CM.dat,sha256=Z3vD8WEr2Wlqqt39anvKXVhz4_yBSm4t5BoAaEz9PgE,636 -babel/locale-data/dv.dat,sha256=-1zCv4GXiRHdWjQk0qXWNPSf4FX4Zp-Ij2jwgz5BsGk,2225 -babel/locale-data/dv_MV.dat,sha256=lvuU5gj3vDykU2ftU1_4Lza-130DB6_ZwylDrdb_GlY,635 -babel/locale-data/dyo.dat,sha256=w71HDmFfCuKN9nUJnuqpHR9DDAmPLkwIu9E0abhYjWM,9854 -babel/locale-data/dyo_SN.dat,sha256=Z6N-NIVcKkx6sFUm8LfN74usyZfgeI6swJL_A_XbBcc,617 -babel/locale-data/dz.dat,sha256=dbPhCcTpoQh6szaBA4TN5eCi3sbG1UBvlQQxtMGHLEU,85026 -babel/locale-data/dz_BT.dat,sha256=KWd6cypWse4XfXbY4BmTlxGjlnRksYNcQYi_iyXluKw,635 -babel/locale-data/ebu.dat,sha256=S8bggVknCbVHw5x9aJQGB9kUBFNcwj9EONHRNtbaWCA,15583 -babel/locale-data/ebu_KE.dat,sha256=GEcGqVmH0WwgP1BCsd9vnSGxGXl8ON4UPTRbyeaNDfw,636 -babel/locale-data/ee.dat,sha256=xJ5vpKJa546rTvKKQI75-HvYmu6VjEedXENlPSeDgCg,88730 -babel/locale-data/ee_GH.dat,sha256=SkQi-aVmtqi3S99DUTLQzZqUE4LrAIWuy25QuZqYAfI,616 -babel/locale-data/ee_TG.dat,sha256=7y8-rDvag7_EQtEObq3fpHaRedVmuEREnQwjktmLwgk,1168 -babel/locale-data/el.dat,sha256=pMPkYRvK3y4IF93Z-127ZF_-_P_QV-_460LgZAHCw9o,247970 -babel/locale-data/el_CY.dat,sha256=POPjzTYMuP7Ijf1S88cAFR0ge9Id_sSRNembKdhk2aE,635 -babel/locale-data/el_GR.dat,sha256=S32Cm-1ENgLL_lDecCDI4qDfyd3wlQ1JbUwOnW1whm8,653 -babel/locale-data/el_POLYTON.dat,sha256=lkcs2uXW6mWXztmsAXnapnSuQ6gkns1WwsVgrg-24Uo,14964 -babel/locale-data/en.dat,sha256=iUuuvOPD3NkEnkY0K6ipYXaJlU_dggEeerU5c1EUQZY,220701 -babel/locale-data/en_001.dat,sha256=1h_YIMaDGBLRbF4L34EAmSTm3ihmKctA2GmzG4loU7E,31616 -babel/locale-data/en_150.dat,sha256=cjKCV1ZLhNY4TvqJF8BXvQR1jVUBFrdjL1RKSnBa0vc,1851 -babel/locale-data/en_AE.dat,sha256=T2jBGVJpPTUgTICdaQnAVjSsYCVkQLfjabyStaPl2CU,4164 -babel/locale-data/en_AG.dat,sha256=POWSM98pRq9k7lqCsxr-MS9PA-zLoOVUKUcI9iXtKq4,654 -babel/locale-data/en_AI.dat,sha256=7iwWKftqPdOdWJ7v94jsre2ZIq2cDpbVGAs_ScBztbs,1206 -babel/locale-data/en_AS.dat,sha256=M-N7v6BuP47wkTFmv5by1w42IQCUiSqKbaVTXtnwF8Q,635 -babel/locale-data/en_AT.dat,sha256=05-bVy1aMFWZlHMBC86A7OVy1zlhJw7hsjyvIOjxE70,1309 -babel/locale-data/en_AU.dat,sha256=4Of5kXCe3oX5qViTn-q1ZD-50JSQ-Ly7xX_b6SFWSbY,19022 -babel/locale-data/en_BB.dat,sha256=J8vppKlBp03jJvVuVp6Z4GMc_N0BukeQ8f8q14y8fwU,635 -babel/locale-data/en_BE.dat,sha256=0JXHgqUIWZYe88FYC97spm2TqyIaNOwDJBt3LBmUF7A,1547 -babel/locale-data/en_BI.dat,sha256=AbdI5FjEqQLOMbBtD3wLW_7AccuOwY9MZUFpMF1uEco,1189 -babel/locale-data/en_BM.dat,sha256=cZE8sxf44ETY6nCxOgbvJN2RhABZrRaP8SjwsbSepuE,654 -babel/locale-data/en_BS.dat,sha256=sGvlF2LPwG5eVA3IuH8-4S5X7BrvQ5LcgZ8gfAkkwwc,752 -babel/locale-data/en_BW.dat,sha256=k5bc9gYBdl_8ZyCelrlS9ZenMzs8cdfl2MxeCnOAr88,2850 -babel/locale-data/en_BZ.dat,sha256=LJsn2M0RWMbxDmGRxs2lk2ZvkgoguwPf7oLBYQydU4A,2979 -babel/locale-data/en_CA.dat,sha256=u9gSGF_MnHeklexjpxBWfDQI-5LREnNkP7hAod_hNoY,37942 -babel/locale-data/en_CC.dat,sha256=34a3Vb57hT2reRvwn0Ud1nXkoYQJoPmaqh1n5qflFdI,1187 -babel/locale-data/en_CH.dat,sha256=Nfkwk2HNOccSH8Co5SWYnFdHWEDEpKNPQ2o9a67vt_k,1866 -babel/locale-data/en_CK.dat,sha256=gvU21BSAugpjE7CT3xnpHML6oRgHPaBoq7W11YbG-1U,1187 -babel/locale-data/en_CM.dat,sha256=r_7Yzep6S0ZOFvk_TORm5Qhi55yjehVQAotaLnn7igQ,1437 -babel/locale-data/en_CX.dat,sha256=lKYbIRS6r-WMUd1ewX7Q4TpX0PBVcsYN7Cs263_xosM,1187 -babel/locale-data/en_CY.dat,sha256=kQZfhQdPfdBeePOngn-4CpA3ZJ7Th-lmZ-jOrBe115o,635 -babel/locale-data/en_CZ.dat,sha256=PNwk-yPyyA7DZbmrwm_Bcrhw1jNmFF8WNWtHMXwdVsk,713 -babel/locale-data/en_DE.dat,sha256=oI3TmMDoQZsGddvDb0fbbwd_LdTeFbFPdFkZhst2q7Y,1027 -babel/locale-data/en_DG.dat,sha256=xtjjxkuFB7I4p3n0f4Evz631RtMQhIzpoYwUzOzICqA,1168 -babel/locale-data/en_DK.dat,sha256=pE7x4MDJvZGFnqQqXMCDWoMV3Ovu2qH-QO2oYNvgL4Y,2425 -babel/locale-data/en_DM.dat,sha256=gbriVhXcdXi5GWmpgWdGB1LUwI2yquuLk1khkzpo1UU,654 -babel/locale-data/en_Dsrt.dat,sha256=16L4-KZ6QNunAmMFJ8bSRppOC16aaHl1rtQN8zOCbbQ,38658 -babel/locale-data/en_Dsrt_US.dat,sha256=p8c_L5tEvzYmacz0tmq58Wsbfp9wMV_PBgz5R_v7I-k,653 -babel/locale-data/en_ER.dat,sha256=U1vYaQVwnVgfmO9l2_GjBnAhHrShbiHU_E-hAclDvck,887 -babel/locale-data/en_ES.dat,sha256=UzffO5tH8bSTRY69w0_Smf0vvOrwID6yIjLHdVUphFo,712 -babel/locale-data/en_FI.dat,sha256=i4gKdljmc9cXO-E1IT6JGmq2JsRRH71sKe3yUmkhIa8,2357 -babel/locale-data/en_FJ.dat,sha256=k-hXekogcQzt00qXdcWODsHbg7Vh9U8Vx84bERd8CHM,672 -babel/locale-data/en_FK.dat,sha256=r6OGl7mX4ep5aeFMlhog6Xr--kDKFEVhDcepSJBYZWw,1210 -babel/locale-data/en_FM.dat,sha256=88mCYM5_VDSYpJ_mV4AStjiDpKAo_1W7GbmKcI9f_sk,616 -babel/locale-data/en_FR.dat,sha256=BTiA_ltFmx1GIjAZJ6bCZ2cuucYc--keQ26NCyLcibI,714 -babel/locale-data/en_GB.dat,sha256=2qd2bKhxJeiwIneKwRmH89UCmnYfBdpOFvL02en-YKU,3440 -babel/locale-data/en_GD.dat,sha256=c2ap41e5x-OY5dEYZ5wD4ByRbEzlx0845R1lPBLnJus,635 -babel/locale-data/en_GG.dat,sha256=qhfvbRdldEA4467X0_8N09HwT1ZRPJJtb7Evpvbbarg,1273 -babel/locale-data/en_GH.dat,sha256=bBpzsHl9QhNAQ7hrUtKb6TwGvZYibXQwXfuJVgLMlZ0,889 -babel/locale-data/en_GI.dat,sha256=veSOVDagnkTwyqxP4u5mOkRG8_W4MCZ5ptMBoGOg9SE,1228 -babel/locale-data/en_GM.dat,sha256=jfHQeiMTDNgnIpx3WZ9hOpCR_LXCYLLrVN4SUakudi8,885 -babel/locale-data/en_GS.dat,sha256=4qjeVJPA9Crph3uHpcywYtsNR8S8ok5l38qtEHsIk10,1168 -babel/locale-data/en_GU.dat,sha256=WDR_uybAkUii6RHT2x62pzCLvX4bMstibEztBvLSuxI,715 -babel/locale-data/en_GY.dat,sha256=TDiVa4YSapsK9rjQN3ATnrj3m267l35pqRLI7r5bLFc,694 -babel/locale-data/en_HK.dat,sha256=meLPc5_3zahna_xycXEf75TrW5_4BHuqMQcMyoo1eYQ,2315 -babel/locale-data/en_HU.dat,sha256=6D4ZyheEpHTHE64XVpU6t8ddSWyYT4Hhfo1HEHzimZ8,713 -babel/locale-data/en_ID.dat,sha256=b8R7owdV-fl30WrUhFz-1YGHiFweaoYs_bBFr9pGhD0,3172 -babel/locale-data/en_IE.dat,sha256=ZNe5XFbdM4UnYu6KaGYHggQqzIVCWwWof5C5ktTsHn4,2094 -babel/locale-data/en_IL.dat,sha256=O4-JPTBiiLezF-iUSDu73id-0VWlSiE0ie8z9qRaxq8,1424 -babel/locale-data/en_IM.dat,sha256=u3JX9jrrsW_G5RQXHsQIGl6ikVduckeldeS_FjKG5Y0,1273 -babel/locale-data/en_IN.dat,sha256=2d4aWIy6Q72ExhZHnku5HKbKmSSavvB7zkflqlBsRQA,14809 -babel/locale-data/en_IO.dat,sha256=TOM0TxdcWcu1wyBXJxsrE-uCIZsbYTCmUkTdfCaOO-E,1168 -babel/locale-data/en_IT.dat,sha256=zp8h2lM7v7DPs_9F1CphO3UVNIYsdf1_8Kj9PPiafoo,712 -babel/locale-data/en_JE.dat,sha256=IxL3Ry70pMOXDbCwU6LZllHXZmg8XHbjocavEWvb9jY,1273 -babel/locale-data/en_JM.dat,sha256=Z9GFZ6aFGEvIEzlbOlA5bBBvxgcxXx6s_7AxPMp2Yg4,1666 -babel/locale-data/en_KE.dat,sha256=oJa3lUdjavkdhkMK_bcnJ08waK9lfIRs8mFkG0JYf-4,1458 -babel/locale-data/en_KI.dat,sha256=3yfkrwv7Yd9yxDNnXz9Sv8ghbDvqczZTAELr3uGNYn0,635 -babel/locale-data/en_KN.dat,sha256=cFebccXOrQo2X1zmHoHYB2vzXynoroYImJTMnK9j3h0,635 -babel/locale-data/en_KY.dat,sha256=slmSWk0IEz_UK7PfHX4KyHlf_iBRfuoOWVuEB0aKaYI,733 -babel/locale-data/en_LC.dat,sha256=jUYdKd248Jkj5XVxXXbvXnfxu4BH_3s4Ld-O67qbPkE,635 -babel/locale-data/en_LR.dat,sha256=ribT8_azFjDC8uzIsOwkGTjfgEiFlSG_wAb_wG1Fmnw,885 -babel/locale-data/en_LS.dat,sha256=2HtV3gUrynDl2mVvqywMfff8VnVjz2ryoNS7D2n5T9c,885 -babel/locale-data/en_MG.dat,sha256=dtMrsBqOpwQExs7dVukbUzWojz_6swx5Ng6OXdKxsSs,1438 -babel/locale-data/en_MH.dat,sha256=NZjQknLyokLgJRNPicAZ55_VoRCATz_WvAxMo_I-HOg,1368 -babel/locale-data/en_MO.dat,sha256=UJVR7td-vlRY_a36ZWbxr_PcK6egIUQSKoky3DeAmEA,830 -babel/locale-data/en_MP.dat,sha256=Sd2vA3CdR_GBw1vQQQBfKIY0asJGsVUwZ6gD-h7Or8o,1349 -babel/locale-data/en_MS.dat,sha256=BXrA3odGnWSeR6404L85ErqMKxTnRCZ16UWL2RpD7TY,1187 -babel/locale-data/en_MT.dat,sha256=sODJaESHndxdT9SYZcnESDA6G6oln6n9NCojeHhKvJg,1990 -babel/locale-data/en_MU.dat,sha256=SefoZho0XzSzzU_Y_-udWPCuRgz8ujdUVkL7hhzZO8Q,1438 -babel/locale-data/en_MV.dat,sha256=Spp5zsOCHzCeZOyUdYqXoqmSoIJ0pxRR3b3nI27jvTs,2034 -babel/locale-data/en_MW.dat,sha256=5d20Ih8j6iwBY545I4nObBWdv9FL37nE-ggfpGeXSVs,886 -babel/locale-data/en_MY.dat,sha256=nUwwM17W9W78nnbXGmrx2zComZQ9Fg_pu3va9wPZzvY,716 -babel/locale-data/en_NA.dat,sha256=-yNAAmx38RoVJt3YRH7Tp1xO9o3GsSVDU6uF_GKhGb0,885 -babel/locale-data/en_NF.dat,sha256=6LqhwHyOnkQA4TNvseuiE1a5Ov7DV3y46CZ0ENcibXE,1187 -babel/locale-data/en_NG.dat,sha256=dvbDZYdlkmhF-IU8M6s2p_kHswy8HhdmR3LLc4EhePI,1439 -babel/locale-data/en_NL.dat,sha256=dDCtXReh9uMfUOVqGWaWjfV1-dLe5_yj-o_HrKZGMeM,1192 -babel/locale-data/en_NO.dat,sha256=7PJnnryBa2El81rK1pzjj1XT6PADpW-P9HnL7bRyKzs,1010 -babel/locale-data/en_NR.dat,sha256=ZiUNBY0c1T3LQP-qGmtuxLW2Jfc-lcVTuWpB2jxfAGI,1187 -babel/locale-data/en_NU.dat,sha256=jU4HXz_3l5k-luPPsjZmZBsdOVB5ZfkN1Xuk5i2pduc,1187 -babel/locale-data/en_NZ.dat,sha256=z_qul-H3zQn0Q_lrrHceySj1m2TQaaJZd3QMXE31CcA,2340 -babel/locale-data/en_PG.dat,sha256=WV6D2yT-4GX6ftbnUkK-ihfNg8Y-ImX1Af56okx8LTA,635 -babel/locale-data/en_PH.dat,sha256=oPz_wDvxfCHBpkBUaZ0RiwtPrLtx-9IigR0TIK1b-IM,635 -babel/locale-data/en_PK.dat,sha256=KS_KXItkhkUULbKd9mHdWD9ok4WjZbVSXhU8e5u8blo,2074 -babel/locale-data/en_PL.dat,sha256=us143U1tPhG6MUHpeixIX-yOMwEilDV-tUaQ1XgsjMQ,1038 -babel/locale-data/en_PN.dat,sha256=qpEWWX0fvhzV6gcX_SHNbJW7E-5FikKwXrQAP9BWmXc,1187 -babel/locale-data/en_PR.dat,sha256=bpAu9yoeKxzQTF5Be7AfXtyE3wax0eP0m-Eu-Cr0jKE,635 -babel/locale-data/en_PT.dat,sha256=D1S2jptkyX0igfFWctmR1yCVruIR7IZaJKevAIrPRS8,1039 -babel/locale-data/en_PW.dat,sha256=--3_iiNqupR9R3ccq5foj9HzkADBEwBYdKE_AxLZD3w,714 -babel/locale-data/en_RO.dat,sha256=5_EIBj1QkBAnG-yAK1C2PVjeJh2a33SjlAaTglysxEc,1020 -babel/locale-data/en_RW.dat,sha256=Et3hOZwaSDECrCzypgYq1QxKxxnB3XYziIeEe_WiY7c,1438 -babel/locale-data/en_SB.dat,sha256=h9TJ69YThzzLGTh49BBEmRBxSiZhZpQrVJif0QglLVs,635 -babel/locale-data/en_SC.dat,sha256=omYtCZBrLl8fHuT6XInezdetV7WqWZVYriJRnlWKbTg,1188 -babel/locale-data/en_SD.dat,sha256=ozd3rV_icr8p2H9V6StOSMkoHBPNBsXsay1GMRWZ1zs,928 -babel/locale-data/en_SE.dat,sha256=96guhEOzGr2Lcbd3xZ4j7HTj-0H0oaGMQIbI9a_5g48,1502 -babel/locale-data/en_SG.dat,sha256=u9U5CLnqs0iJmSnMsPBHF71HQLpFM3siADzZeC7iaJ0,2096 -babel/locale-data/en_SH.dat,sha256=rBm_0zxjHnEVzs_CPCyuz5LpI9isA2S6copUuNbxCcM,1210 -babel/locale-data/en_SI.dat,sha256=kkeEkPnW48iNoiwNZb0KV29DPU0oVNuENTi_Wmp2ALc,1046 -babel/locale-data/en_SK.dat,sha256=r6hAr3NnR-tbXF7eA4QVgwe493o_wH-QzcA4xdlYoRU,1065 -babel/locale-data/en_SL.dat,sha256=4hJLAAnyuEVVQZ13NC7wZ0JYJQ3rvX0SsOAp0OoW7_U,886 -babel/locale-data/en_SS.dat,sha256=C4kpyEFjyKT9-6wt2pGl4125aN0Bp0wpcYBrr62nPOc,908 -babel/locale-data/en_SX.dat,sha256=7vESruRIiaBqWxuSRpIE2U6PgWwJ9mxgbjjH_XAraa0,1190 -babel/locale-data/en_SZ.dat,sha256=A9wkzZ5cFffcEf1FobYRPxR1EAFwqQvqELsqRpNleOg,885 -babel/locale-data/en_Shaw.dat,sha256=wU9fC6w85YCi9jeUFo-zofJuR14QaSehUJxcCOGfQik,4776 -babel/locale-data/en_Shaw_GB.dat,sha256=uAIWlJ5gr_NR6lncmMaBrjXUPhZ33rGsXoHz8hrd_E0,653 -babel/locale-data/en_TC.dat,sha256=UWqEc9zpN3NdS1dymbUv2iGKhLkH65Kk9uNFxdXGnOw,616 -babel/locale-data/en_TK.dat,sha256=qz4RHtHQ6cNjB8xW8_BIHdzW1cEcQuyJfdqFIybXAs0,1187 -babel/locale-data/en_TO.dat,sha256=OehJQf8kvWi7A-Rwo2hPjs1Z9pTdg7_Hyx_ewWxbLQ8,636 -babel/locale-data/en_TT.dat,sha256=L23iDn_zvh16iPE5uBlLCRvECSozqyzU9W2OcSjYogE,654 -babel/locale-data/en_TV.dat,sha256=3sj3NCjQg-9UOdTz7wDJRpjx57bI0bntpmx0T50OK9E,1187 -babel/locale-data/en_TZ.dat,sha256=5Q87mjSEaCMmWeWwtesb9lJw2tIWOsvyScG1CqSpWvs,1439 -babel/locale-data/en_UG.dat,sha256=aQLfdbfozsYhK3EG9JXVxsmT4B7UvfSzNBo7kO2WSsc,1462 -babel/locale-data/en_UM.dat,sha256=qd26Sl5bJTEgHU25hvskp4LMxvRJByLOSvUikqz0J4I,653 -babel/locale-data/en_US.dat,sha256=p8c_L5tEvzYmacz0tmq58Wsbfp9wMV_PBgz5R_v7I-k,653 -babel/locale-data/en_US_POSIX.dat,sha256=U8r77o6qQID3Sd5FiZqukSpQnq4LV1BDwHUeuNZjW10,1321 -babel/locale-data/en_VC.dat,sha256=ES4c4Xt6OvJDhxoAZpWR2HFbtyENOl9CrV7XFCBs-MY,635 -babel/locale-data/en_VG.dat,sha256=sqiVUOEtDV2pAc5DzGl76qV1mL5K-UWhMRzG-Ff6r30,616 -babel/locale-data/en_VI.dat,sha256=AGffygfBAafQMxKAAoxt6kwmbES4R_ybbXRV2tHpy04,653 -babel/locale-data/en_VU.dat,sha256=ji2cRB8B-c8uJR7L56HzbkiTR8eJZziVTMcEzuiK2g0,636 -babel/locale-data/en_WS.dat,sha256=gowKsveED3UIWyLgnSWxyE6qq87lH2dMgOVLDGCEcBk,656 -babel/locale-data/en_ZA.dat,sha256=uoSLsVWiMu6x-DYwISGhEwveg0kVsDwgnIhq2Jn0hXo,3411 -babel/locale-data/en_ZM.dat,sha256=8y814a5GtYcC5gvh3YBlcYf5Imr0yNEIV_v8jH3SXqk,885 -babel/locale-data/en_ZW.dat,sha256=GUDDEesQRkUuLyyDlnJ3MGlGzG-kDPxt8OTIqVRKDyw,3320 -babel/locale-data/eo.dat,sha256=kSIbuCrTJF0ZuNV8FzhyheJT33iKwteIHLaYgdfPO4I,101326 -babel/locale-data/eo_001.dat,sha256=3-tP9zaTDfbBBjpd2qKVIhS7WWbmtVKGGW_BmGIhrHQ,850 -babel/locale-data/es.dat,sha256=c62rMW3rHT7taFSTKPt8je50jtFex4sL8nvuVey9224,191377 -babel/locale-data/es_419.dat,sha256=s-JLmJhGKoIbnsjZm-6UXnDsAsgFhIKk9OJk7SPR_oI,27973 -babel/locale-data/es_AR.dat,sha256=rFeyqcUDLb3yYYWMuCP8R4O1n4rT1XCeayaIaoPaOPY,7087 -babel/locale-data/es_BO.dat,sha256=9zgEUshMb69M3LZS_3TOk4kGOt73fnB2UWWR7HPFAgk,1477 -babel/locale-data/es_BR.dat,sha256=oCD_7IoQ5w-C-YXpQ_wBcS0wUkcY268QDcg5y7Vo7KU,1207 -babel/locale-data/es_BZ.dat,sha256=Z3ZskA42UBcpOPDI66F6YBTvzt-g61PmxhiEpdGXpwA,1206 -babel/locale-data/es_CL.dat,sha256=tpiiCMc68Ab3YYDqi_zh0dw_IfsZmLciylujbf72XqQ,4848 -babel/locale-data/es_CO.dat,sha256=_ZNmqQnEj8EoNse4lJuK0Qrg1ZnPSgUVCPCNo7K6FE4,7436 -babel/locale-data/es_CR.dat,sha256=GknsR2O3LbFhHagGvUvgNgBVCVbXZxk-yzivTPR-Q_0,1308 -babel/locale-data/es_CU.dat,sha256=N-TqpCs34sjlEMeJgvrfOSpmnnlGxuuGtSBSaUKDbvg,656 -babel/locale-data/es_DO.dat,sha256=HMY1U9RiyiBlQKc_kUERQ-GHqWCgjaqrBY0mKUE9Gp4,2700 -babel/locale-data/es_EA.dat,sha256=V4UU6BM3pjbuEleLA_MaJtWl8IejNcHzUM660pfKa80,616 -babel/locale-data/es_EC.dat,sha256=i6oUe_5GL6b8ZnbnORW1csbq1-qgo6VN0Z6x9G2xIWk,2941 -babel/locale-data/es_ES.dat,sha256=fd1TSjdlf2lSMxwi0NhYGR2eyiABBaSJvhLlwLO46j0,653 -babel/locale-data/es_GQ.dat,sha256=a7A-hqM9wNt51_CdpjzU8aT9KEeMz165-MyhkWyqJOM,638 -babel/locale-data/es_GT.dat,sha256=zk_WqHmzYsoIAiErSYC9G62MlbgAuySSTAPZLtOX__c,4132 -babel/locale-data/es_HN.dat,sha256=71FLaDfVW5pgllY-BoQoPaDjJ4lvhs_Fi9EBmJQjZj0,3051 -babel/locale-data/es_IC.dat,sha256=hwey8h4GM8i25i-YZR_K-jvCij95wZqr0zdTIKauP58,616 -babel/locale-data/es_MX.dat,sha256=6jxhdWleuQwI_O1FZOl2I5DEwUs3oXjAhJhyBmBCMGM,25204 -babel/locale-data/es_NI.dat,sha256=cF62A52GuQD-QU9-jBmK_8AJ5pZ1W_nJ7NwraSAhMvA,1227 -babel/locale-data/es_PA.dat,sha256=fBNNQGv937Iu4OPHxSuYtPu8M-u6xhUmG0LtMbxDC0E,2775 -babel/locale-data/es_PE.dat,sha256=G6ZcerCUvlSGQmeysFNtMNJEZwPAerVqwOG2hHUpZ_U,7244 -babel/locale-data/es_PH.dat,sha256=fEMZmVPQb97bBjEtCVMLn9JUtosTePmRuQqhpfNYkZQ,1248 -babel/locale-data/es_PR.dat,sha256=wPOPeV-O9rnLzNHObqs9JEUo2ouyqmaiXqtsxHtKpWM,3307 -babel/locale-data/es_PY.dat,sha256=KXiw-rkQCdcoeTv0t0SnA8ioZrHQYfPxen8491aSq80,4442 -babel/locale-data/es_SV.dat,sha256=JM9uw_EVtB6J8bH_Nz0GL_yr6ehay4uAPm4OJIiUmfw,1260 -babel/locale-data/es_US.dat,sha256=ifZ5PL9mZhnqv3E6uRoRS2Sqlkgyz53m2RJs9wDcpMY,22533 -babel/locale-data/es_UY.dat,sha256=31KdHVEWnE6fWuHa7KOdTLcmlWXn8M5liZZMDWlaVrk,2703 -babel/locale-data/es_VE.dat,sha256=kyxdQ51DhLNf6dQauAPw4vOlpjftw9CQgc3yH_uAoKc,2509 -babel/locale-data/et.dat,sha256=eTq6nG039wKW81vnFtx-qzaWVo0wiUey2Y2ZUQLl49k,175652 -babel/locale-data/et_EE.dat,sha256=JpcyzIDpGwHlLSiInuYO3HfxkMEFsDPBeMc-B8Rj3MM,653 -babel/locale-data/eu.dat,sha256=lEQD4nzng6_Fmu8zQtkrvoFWV5AvUaCpOaAlv7vknt4,178023 -babel/locale-data/eu_ES.dat,sha256=ABS9lrwjRkWenVB4fNrwKpr0mOuMF_eOlOrSrobcDl4,653 -babel/locale-data/ewo.dat,sha256=tHyC9Uj5Gkz_nGEiTCFjXn3veej5z1GUhr-j-Dl2vgE,16881 -babel/locale-data/ewo_CM.dat,sha256=likxlZJqzYOVxcG4oSUQLTiH5DjGjdAFsJPIjnnj2Iw,636 -babel/locale-data/fa.dat,sha256=ci_F95dki3wTXhl10yqZP7IomDg1FHIOArxqTEMUEyk,192269 -babel/locale-data/fa_AF.dat,sha256=3lXNqz0r7fjXSYzqZBKGD5gtS0cPs-lx1ExY_s1ucfk,8639 -babel/locale-data/fa_IR.dat,sha256=nPuexwzRHDb9TFMjILK_V43sVIzbcn_fgp0msY3eFQ8,678 -babel/locale-data/ff.dat,sha256=gT8MoHYFJcGDoVjBCYPzsrkMTKylOjXkE64Sc0L-rvM,15911 -babel/locale-data/ff_Adlm.dat,sha256=GbwYyVBbA8SAJP-1BYMW4b3HXivgi8HvRDzIaKuJtvg,326252 -babel/locale-data/ff_Adlm_BF.dat,sha256=eYQLbHvxtEqcJCMbXSkqN2dRSuYHVc6Tg9kOUKayJQs,637 -babel/locale-data/ff_Adlm_CM.dat,sha256=b3VqkcWqXc7s1hCxN60dd-yBRXkcXfMMQ0uAJKmSsg8,656 -babel/locale-data/ff_Adlm_GH.dat,sha256=r1CRe9Z8kZ3B3PDxmZQbvgpsEcY3OjJek9Z_S8FBb5Y,1236 -babel/locale-data/ff_Adlm_GM.dat,sha256=ZM17aBc8xznEjoP4f7HRCHaKn-DIxCSDGkL6GvDYvDw,1232 -babel/locale-data/ff_Adlm_GN.dat,sha256=HWkLsTj8Sd0fWYuYysHhShl08hg0iAvSXhPjy-rUjB0,616 -babel/locale-data/ff_Adlm_GW.dat,sha256=vIUl_C4PZSWmypeEmnsAUbGN2dSquM3nEOdBs22_qNE,637 -babel/locale-data/ff_Adlm_LR.dat,sha256=277rY6zAWlxetFe0D0JpVEY9MNcQiJU9kt1jRoiXE7g,1232 -babel/locale-data/ff_Adlm_MR.dat,sha256=qz618w1XY_zwZ27nB6LBgbzsjsh4UinfnuOVx2bqLgk,1233 -babel/locale-data/ff_Adlm_NE.dat,sha256=waY1EIQKuOkVzrtJ-t-5VY7cK2uKL0SIBO0PQbx4x9Y,637 -babel/locale-data/ff_Adlm_NG.dat,sha256=9UdHELj5G90YopBKktxDyWVQyLxl5f7P3ToBElwlvCo,658 -babel/locale-data/ff_Adlm_SL.dat,sha256=7jLKofwQBRggP61keprrbfFGXvrcJPIOvwEVmUNjcTI,1233 -babel/locale-data/ff_Adlm_SN.dat,sha256=nGazz6Tun3D-XsO6SljxQeeBuzvAb1sBQw21IbkDQKo,637 -babel/locale-data/ff_Latn.dat,sha256=APyNmlFYRwCQOlboY58qsqiF57rrI3F4fHu-uQTdt4g,866 -babel/locale-data/ff_Latn_BF.dat,sha256=Nym5keR3dDPMJBFr7ZyW2cuuY-qTMkdHzXnIHSmfD4Y,616 -babel/locale-data/ff_Latn_CM.dat,sha256=rWaVAF6D5gv_hDhNmSGbcWGpPV7X4gp53FBMXyMjT-8,635 -babel/locale-data/ff_Latn_GH.dat,sha256=-kMvFNIQyikTD2dFakV81ghSR2H584AigLgtmrzlhN8,1231 -babel/locale-data/ff_Latn_GM.dat,sha256=1Air8sk0wb0McqLZXDWIYQeiKgPusBaVjr4zk54IaoU,1227 -babel/locale-data/ff_Latn_GN.dat,sha256=7R-6dXYMBdveo2-9Vc7CjIA2H6y_hGJVbfVGIaJUgbI,636 -babel/locale-data/ff_Latn_GW.dat,sha256=2kbqhzcDeSPQxefHxynmrv2tmFVnWdRNjO7I3AZ6lxk,616 -babel/locale-data/ff_Latn_LR.dat,sha256=MQxcDQnMZC5hN52abzqCjVIL9Z5mA9wOyTqGPP4Oj0g,1227 -babel/locale-data/ff_Latn_MR.dat,sha256=CDUb5owq0Ntsd2lIZJejvYxOjl-NOUPP-v3tgVj1_qU,1228 -babel/locale-data/ff_Latn_NE.dat,sha256=R8C0fI8ql5XpnT29CrwBG8rgdHB1AASDBSdvqIqIQ1o,616 -babel/locale-data/ff_Latn_NG.dat,sha256=00rtiYbJD_ci8-0M4ye95rzGpbyX8qQ-dmFFIajAtRQ,637 -babel/locale-data/ff_Latn_SL.dat,sha256=UaWOeokdbSoxG0JUyyGWkDyz70sXy-cZbc3lXZxmQK0,1228 -babel/locale-data/ff_Latn_SN.dat,sha256=mPVac9iCnwNay8c7Ko3uUvg48lfyfEZrvhpyEFFGmFk,616 -babel/locale-data/fi.dat,sha256=yr9FhmvZuEh0-H8Kx-8XWi-7Suo0K-esRZVVhXdMPxQ,204853 -babel/locale-data/fi_FI.dat,sha256=cpbJU4KKG8OWbmLnc9TKTdzmpoi2AJh_Pu1cHhDwVSM,653 -babel/locale-data/fil.dat,sha256=g0INTCXppDgjTGgBHy9hqQ8Mcw7G9K3XLTLyVshWuuA,144724 -babel/locale-data/fil_PH.dat,sha256=JjoZt3zNxy1X6RnHoxUKOMzT-xMh3tGZBRq8cvrBwqA,636 -babel/locale-data/fo.dat,sha256=4P7HQxbB7uhYUyAt1rFID1mHXUe9zlQBvWCt8LDRhNM,148411 -babel/locale-data/fo_DK.dat,sha256=HUuarLNFSsV_gN2fg-OqrPr-6Ry6KFgitCp173jxAOM,674 -babel/locale-data/fo_FO.dat,sha256=SLeNQ33QTZ92kXCzQcFfo-OgwZVFPJKIeRivuvz0zo8,653 -babel/locale-data/fr.dat,sha256=HvPCR3ft-_VxUpJsVp1wKU8aiJ7eCKZEz9XZVlM5jek,213357 -babel/locale-data/fr_BE.dat,sha256=ODfkJC5kWWEEkjr1UV8AaLtKKc4sPCUxlIGkEDkTr6s,1085 -babel/locale-data/fr_BF.dat,sha256=7piWVeMMrZ-YbO9MNqJAu7ZeIdueVnhg07eeMwJHy94,616 -babel/locale-data/fr_BI.dat,sha256=R4kaU1OtMcNIcibkw8OFGP6FaYMxt2uKMRyidkEqbiA,637 -babel/locale-data/fr_BJ.dat,sha256=sel3fdwegY5W4NRg32t4MuXoqpGqTAGO_ffCF2NPggE,616 -babel/locale-data/fr_BL.dat,sha256=n-MRZjeHNkRMwCtGgPZq52Lj0ijntBsxfskC324i2k4,616 -babel/locale-data/fr_CA.dat,sha256=Nu60QdYyJaiQnz1EbBLSWIO86Rf57q3FWoU7ywZMKQ4,72420 -babel/locale-data/fr_CD.dat,sha256=HWaY-wrLseHcf0fGlRkAWt118iQjRZaTGlqs7G23ZrU,1138 -babel/locale-data/fr_CF.dat,sha256=wxzk0Nf6_pKO2VDivQprUY9WvaxRSkrfQBoUE1UVrAA,616 -babel/locale-data/fr_CG.dat,sha256=zP3HTP-nqZjG-OD_DLrDEPPbB3t9g4T2kHVzI-HxhaU,616 -babel/locale-data/fr_CH.dat,sha256=DiO5jczUyCwTWpWSoxVHE6kLMXQPru-XotJQLDInjKw,2876 -babel/locale-data/fr_CI.dat,sha256=TrHL8hQRuJI3Auxe3aTHYa_rljyUvT_s0q-jdSJflJM,616 -babel/locale-data/fr_CM.dat,sha256=31oDAmeRb3F8a4fvVn1QJyIbrla04w8kfC4oQO0vwkk,1970 -babel/locale-data/fr_DJ.dat,sha256=Jqu2FOJQD41B6fLSBEc_Wf-dIQ-B6MWlT0m1buQnmxo,1248 -babel/locale-data/fr_DZ.dat,sha256=uOe9lbjO51q30G0UMs5nKrMLta5CtLW2Fg4dvsfRX3M,1290 -babel/locale-data/fr_FR.dat,sha256=bwtjtv0NG-lxlXO7fJd6r--l38WKaQmqlmn7PpXJdJs,653 -babel/locale-data/fr_GA.dat,sha256=rMwS-msXjMO5T5k1BP2B2S9B_dYvDHlP8HMS-Lq8XU0,616 -babel/locale-data/fr_GF.dat,sha256=x97-H7H0h4_MUTpjMdrkYB790vBvPKKxmnUTu-FOwLg,719 -babel/locale-data/fr_GN.dat,sha256=6KVlBWrF76ZhgyYg47F1TcR8_fbE5Ii2pJzPhmbr3Pk,636 -babel/locale-data/fr_GP.dat,sha256=W99xHjqPQj_NJ1X34ismj8A4xeZcHu_vyqH8tIwXgeI,653 -babel/locale-data/fr_GQ.dat,sha256=Ogf5NI4zMvbVROeIb8F6h1NxJNKOqY4fKU6G2SPJXDk,616 -babel/locale-data/fr_HT.dat,sha256=S2Vvt2b9Wu4dY5Tsd9xxFAx_RLcWXCGf_XeSSQFYbKQ,1836 -babel/locale-data/fr_KM.dat,sha256=ChLpzCyulxYg8Qmm5HK8YkK203GRSUEBNPSl8war7lA,636 -babel/locale-data/fr_LU.dat,sha256=0sYBhn8-9lvfOrU-ZEIbnO5v03qQOQq4wXCKp70b2sw,729 -babel/locale-data/fr_MA.dat,sha256=Um7dK7MvHkPB_h_r0pk47rMFCng1LWFgIekS7cDX9ZE,1098 -babel/locale-data/fr_MC.dat,sha256=IfTCZ89rqHzUxdKyIz86Zs_VUH2zc5JAvntdhyKsNR0,653 -babel/locale-data/fr_MF.dat,sha256=xU0grNJKWgpfArwpLt7vuOpSTSkyPx3o6gFkmZLCxd4,616 -babel/locale-data/fr_MG.dat,sha256=A9zYBbE6gIWuGDPxv92ReIcR49gtIEYaEkCj9ZONHHI,636 -babel/locale-data/fr_ML.dat,sha256=-PoWsHBtnaZisIOqZyaEFmwooRXb2R2xEMe6DYad_sk,1153 -babel/locale-data/fr_MQ.dat,sha256=45qWAev4p2ZaFVky1Qlrw5q907PDfbRJq9q2UACahm8,653 -babel/locale-data/fr_MR.dat,sha256=mVSOABz3cRa1-HEeUKW13sLw1ZR6PmB9h_DPfZsK4Uc,1228 -babel/locale-data/fr_MU.dat,sha256=c4ERuS1GI8h7waxcW6BRtloW7BThLlgqdYTf31Ioh_U,636 -babel/locale-data/fr_NC.dat,sha256=Su2DaGZPnLCaONmgosMYke9kH6dP8xJ8wJF6P5xrJq4,616 -babel/locale-data/fr_NE.dat,sha256=D6ghYGdDXlhaVQ5gAr8wWHxEHYR7mBs0QaiNL5nfxWM,616 -babel/locale-data/fr_PF.dat,sha256=oPN2EBo1hpLjlpDYPB51qEAx_4HNMWG5-Bh9JN6Kal0,616 -babel/locale-data/fr_PM.dat,sha256=gEP6Ay7r-p7uIjOGXP8eCa62yujlTBntENdeNd9KKes,616 -babel/locale-data/fr_RE.dat,sha256=pxNaLi-BUbe3E-Xf5ZXPoeut9NdT0D9OGEgmoSWZEr0,1055 -babel/locale-data/fr_RW.dat,sha256=gP8mpM7gx_X_9NKDm8B9PuTg83jwT5Nn3NOKPpVhi3w,636 -babel/locale-data/fr_SC.dat,sha256=qvz0N08LKCsPCPDZK1NmAhhzmaszBQ8mSlDbAsEwAoI,636 -babel/locale-data/fr_SN.dat,sha256=n6ijBmMl-ztuCLSePI-Q3yIyhzejn7KvRw5ZqjDoXsA,1018 -babel/locale-data/fr_SY.dat,sha256=tpDovgZ3aK7OumRwbntHTknV-tRL_HjcPK1aQINC-x8,1290 -babel/locale-data/fr_TD.dat,sha256=1H47veWKRj_iV9CxHMz7x7tfrL8IYdQaRWhTV455efw,1208 -babel/locale-data/fr_TG.dat,sha256=r_E5Vnj0Kb5CG16ApHFls4UreDeO7-o3uzpQdJjpbRM,616 -babel/locale-data/fr_TN.dat,sha256=hcGRJ2UjuFEFJqb1BDglGsXY8jCEIdM8--OrU3OqRhc,1228 -babel/locale-data/fr_VU.dat,sha256=FYYsiaoM8QZeClMnQzLiLZNXW9hULz-rCgRTYWvrIvs,1228 -babel/locale-data/fr_WF.dat,sha256=jn6VFRw_qpJFxpO-I6C9nhz_wRZ8cV2b2XRBgSzi710,616 -babel/locale-data/fr_YT.dat,sha256=x0WpQCLoqrLY3_nCF7JlcuekkoUtcpE4llC8kRmeRmY,616 -babel/locale-data/frr.dat,sha256=Itmd93DOwAMGcTkWE83BWDS1oXEVckX0EE6mSiOKyoA,102994 -babel/locale-data/frr_DE.dat,sha256=2ohnodzECbJBUTEta36Ts4amZKl2_LDhuD0-Hu5JBo0,654 -babel/locale-data/fur.dat,sha256=pvg46WO2uwbg27leWyWYXkoqYFiaE1u9Txsd_iZJeC8,32441 -babel/locale-data/fur_IT.dat,sha256=PpDH1opUDjndOtvpPadiOtITGyGxKZY0ziEhb-4HKbE,654 -babel/locale-data/fy.dat,sha256=zJce96OaCoG-oKfhQFisyjgVEK3pNVtF9RT-f9h7y1g,108200 -babel/locale-data/fy_NL.dat,sha256=I_M2hFVCAaVAL90yVMloW2eYmzO5C3NZztfEDAes3_I,653 -babel/locale-data/ga.dat,sha256=rx5MLgtNUisB9V7gu0e2dNyCXF2H9KhLVULuYvLNUh0,261523 -babel/locale-data/ga_GB.dat,sha256=NKphVJhmH8egga1dgfO-hSRVFhbxms2JvGMU3ybgj_Q,653 -babel/locale-data/ga_IE.dat,sha256=P948T1Tl4B7kL1T-uxii27q4IWO3yQQ5emTLv7iHvW8,653 -babel/locale-data/gaa.dat,sha256=W9tUKmjglnSUxVlpiGA0clKmagjmzgF1Fwj_UPmg8Ik,33806 -babel/locale-data/gaa_GH.dat,sha256=FF7vo24vzA2ru3t8py246uanyOqyoSkpD65F7KFTXlA,617 -babel/locale-data/gd.dat,sha256=AtSvR97H05LvFNLHpLsb7tOgKYaOjYvWFOLaDDH6xMk,280763 -babel/locale-data/gd_GB.dat,sha256=weaRul21diYZoqLk7SXacBy8GD1bH30cQO6AHejilPM,653 -babel/locale-data/gez.dat,sha256=6gIHA3BFc3-A6-dOq0pc6bqP2ej6kmG9Xvwa0EEhoMo,12743 -babel/locale-data/gez_ER.dat,sha256=MKpSW7Upkpe42Q2mbJStbjtnrW6Kyo8Tg0zXAxnHWqM,638 -babel/locale-data/gez_ET.dat,sha256=93QTSMihpcfFyRhttFplgAcsAhzrPjlbrz5HI4feRSc,636 -babel/locale-data/gl.dat,sha256=N2zvS1gg8f3l6SOLqIIiKCBsbxAcArFclISemEyGx7Y,153642 -babel/locale-data/gl_ES.dat,sha256=us8xWRQnNTO_KC42-FOU2cnLCNl96591qnxZ4exq5Fc,653 -babel/locale-data/gn.dat,sha256=2CbBgzd3kHlm8uIpKVV3yurmEXB_yNUfM2NKTFV95dU,2477 -babel/locale-data/gn_PY.dat,sha256=tArgCo85T_BUNmPc4sXVRz8X5wcHJmBS6XP0r8dPgA0,635 -babel/locale-data/gsw.dat,sha256=TGHS38pKauNWMdFFsCyCJ_9p17aLt14jMA56gxTDBM4,95265 -babel/locale-data/gsw_CH.dat,sha256=fQlRCd1MJ_gI6rcnkrU-lUE7ZM4syRLh-suMRT0DCOk,654 -babel/locale-data/gsw_FR.dat,sha256=vHNQ1q9Zyf6xLfsJpmqpaX1iHerMRt1dsJSPQdQDoTE,654 -babel/locale-data/gsw_LI.dat,sha256=dsg7TA7MECMrbTXC7i7zXIjrEoqrF2jZ_ckGiTMholo,654 -babel/locale-data/gu.dat,sha256=5A_T6q__RzuWdGg9Sfte2wOqRw_Adz-LJMJJzFJ87cg,208242 -babel/locale-data/gu_IN.dat,sha256=shXUMyk4ogB8uJPazALoReGyLqRXSYOeVBVMn313_Y4,658 -babel/locale-data/guz.dat,sha256=491I3x6EkMG0roSEKI3sHxtaweXWNXhXyk4vsG-LzDg,15427 -babel/locale-data/guz_KE.dat,sha256=Uz_x0_YFKJHDkSd46SvuIfO7W0c-uqYnasmmLNib3WU,636 -babel/locale-data/gv.dat,sha256=Ye3v_q7SrnCeiOVw1r7NRcTtUZ3RwOr1ZKs6skxYTos,3960 -babel/locale-data/gv_IM.dat,sha256=bXOsSoV303kSUECHz2xd5HhASRI-XCjqjxnxpk0Bt10,634 -babel/locale-data/ha.dat,sha256=fVfNqCuIXaCTV5E9XNMNVROCwgalJ8f4MKfEvKun3Ao,150526 -babel/locale-data/ha_Arab.dat,sha256=AUoCyyrZDTrf5SEZ2SRizimC11NMSCyonIkMRer1PxY,2197 -babel/locale-data/ha_Arab_NG.dat,sha256=oYJYqTkaDX2p4IJ9j3sEJT49dcVn_f9JyjO0rUJTmAc,616 -babel/locale-data/ha_Arab_SD.dat,sha256=7DD6pzCmpEdjwbTMAbAb_vJey6Qh0tIeSs8LjEpJxBo,678 -babel/locale-data/ha_GH.dat,sha256=Cwfuh0nEMJu4LGZmdOXJHp_5fZRdpTIODJ_Tpd83uPM,1231 -babel/locale-data/ha_NE.dat,sha256=glKzRtSaovyoiyBc-vjFbJ0wNuxfga49w_zeK3ANtvU,616 -babel/locale-data/ha_NG.dat,sha256=oYJYqTkaDX2p4IJ9j3sEJT49dcVn_f9JyjO0rUJTmAc,616 -babel/locale-data/haw.dat,sha256=aNXNxqponTYIxzV21MX0jIzG73qbhbNO6px2WGael6o,10375 -babel/locale-data/haw_US.dat,sha256=W2qJBTNdETpoN-Ue0UYbqyblj-gIbQNOUMr01RwJ2fw,654 -babel/locale-data/he.dat,sha256=o6AACO6FcY_o6bcUuLkcWgbWIT77K8N8BTBDLY0hk7A,217062 -babel/locale-data/he_IL.dat,sha256=KpQ8sod_QOZ94Tr0tfhlATzVVsrqXck6eq7txoQgCbc,678 -babel/locale-data/hi.dat,sha256=-uw13ozTXWlo39eLvIBYrfQxDROzFOdwmI5wXU66n9A,221948 -babel/locale-data/hi_IN.dat,sha256=UMkxSHgYSdX-hPgYa7rOOzft7xtJwcM0m5nTphUe9xM,658 -babel/locale-data/hi_Latn.dat,sha256=Dn0MJdILlsVYU9R5BJFSPGGr1a35jF4k9ebJMKLX69U,30945 -babel/locale-data/hi_Latn_IN.dat,sha256=UMkxSHgYSdX-hPgYa7rOOzft7xtJwcM0m5nTphUe9xM,658 -babel/locale-data/hnj.dat,sha256=ryXdpBMEUzFyZ_u_YiqAK689QOsEuUO_Auyuhty_4jY,2117 -babel/locale-data/hnj_Hmnp.dat,sha256=6GekZXtZVXtGcI_zk6NqXq3WxMnOx83IpRlJOyUws6s,746 -babel/locale-data/hnj_Hmnp_US.dat,sha256=OydEowg2bNVks5nd5ij7ZJqqQT0ztYv4HL1ZZ4gNajw,654 -babel/locale-data/hr.dat,sha256=kdzKpWmyLAIFw7D5DPKG7aHUQjYlM5fctcBLRoHeujg,207636 -babel/locale-data/hr_BA.dat,sha256=E_S8FRdn0yszTWFg9RlqTV5zEwCc7-qtod45Ud8LHyI,1188 -babel/locale-data/hr_HR.dat,sha256=PqaNYKNdTFgfgmeiINdYp2d3q8qpfkCUs1y4ZHmsphA,635 -babel/locale-data/hsb.dat,sha256=w5bDS336hxU-RChl4QgaayXgMgN_VBUh5GJXznPI_Bs,206887 -babel/locale-data/hsb_DE.dat,sha256=_ELT9KBswSqeyiwbcsP9nFY793FE8PQzEu_2w78Amro,654 -babel/locale-data/ht.dat,sha256=WTxEmIzmX4quBNzQMm0nY3afRvD44p7KBr19xZN_t3k,812 -babel/locale-data/ht_HT.dat,sha256=XR94sLQ8B6jF6SPd_4R6GGLmQQZGe8IoGSDnMYNIT9M,616 -babel/locale-data/hu.dat,sha256=dmS-7GInw1eGbifjgMyXc1m1nBg6dK6IOJIDoLb_Epo,147032 -babel/locale-data/hu_HU.dat,sha256=r3RZskHfUdk1Ba226wqBHAoRMXorlh-IkWLlnY_mBTs,653 -babel/locale-data/hy.dat,sha256=R6n2G8QdkkxlAxBgpTK7l-cPhwHg03vIG1pwmWVrCV4,202316 -babel/locale-data/hy_AM.dat,sha256=r1Bb6WWX2QxVlLP5vRVgox0HgoDOosA-h3oYKLeZMIY,635 -babel/locale-data/ia.dat,sha256=Wa4yDTDv9TA2Imo69v_49GqgV8LvQP1iuWGq6LFAMOk,133538 -babel/locale-data/ia_001.dat,sha256=fzAkHPu7PNj2TOl-kQcJLIdlEK6z-KfVFUVcy-YF5Zg,941 -babel/locale-data/id.dat,sha256=CfCCzK3VV98obeH7x1Ueax-xYczfCwextwJ7aetTQ5A,126977 -babel/locale-data/id_ID.dat,sha256=GvwO35RP_Y8vswE60ItYMQa7XUOBXGLk8tvL8kSJkY4,635 -babel/locale-data/ie.dat,sha256=9nzS8srJ9Mze83D7nKqXSlGvjOtu7utOyAJCLaffEm4,77957 -babel/locale-data/ie_EE.dat,sha256=93dmapYNE5HEAfIRwL_HjNV4fpCJhy1CseggbriCXL0,653 -babel/locale-data/ig.dat,sha256=ts_BawFD_ves9B7kX6GAIxwBY8FP4jNmkZidIdAVaDA,78550 -babel/locale-data/ig_NG.dat,sha256=2MR603ed1L8Y_aZhXPk7K7J8bgQPE4xvvpn8R9Jodiw,616 -babel/locale-data/ii.dat,sha256=WBkpukOuFcoryzq0QnHhdafGfad_u9vdJy0JO9vo-iI,7477 -babel/locale-data/ii_CN.dat,sha256=Vr_Oe23kFnz-EDfl7XeDv6GeTY0LSK-VV6UJpzU8yJE,635 -babel/locale-data/io.dat,sha256=laqBHV7QB933VjLibEOmRezXV9StwhKVzKvWp8e62YA,932 -babel/locale-data/io_001.dat,sha256=T6zZvvSVOcdpy3fhXbdPAWmHCvDTluick2U8_hUDu2E,912 -babel/locale-data/is.dat,sha256=r9gnQpf81NqPVskVKUiSGWa6VU3_NXzJcna1Gho28d8,163097 -babel/locale-data/is_IS.dat,sha256=f3tUj_oYhQ6_tGnn8yda4PZfqChiHmcZXo_nMDCtzSQ,653 -babel/locale-data/it.dat,sha256=n8pxKaMuS2ekD7EOSV-3PmVfdzm9NqxF5zA073x5itU,173579 -babel/locale-data/it_CH.dat,sha256=Tvtge1s4BcbCeKJmPBiSbtbf_qv8wrw3Eb4Dc1sF3dE,2942 -babel/locale-data/it_IT.dat,sha256=-e6EK0A6gEd_G4_BHlmd8_caQ-k_JrEemNgdA0czhsQ,653 -babel/locale-data/it_SM.dat,sha256=xjE63TohE8bjkNiOh-BhOkuB9YWpQ0n2rrBMJoAql5E,653 -babel/locale-data/it_VA.dat,sha256=wwsl_eAmNu9m2JPZnpBwFVh4gsk5AM9lSx8UAXhrMlE,653 -babel/locale-data/iu.dat,sha256=vZIG1AsqwcrTA6zfwBeQydVAn5qDQXT8yTK8wvbaVAA,3209 -babel/locale-data/iu_CA.dat,sha256=C449KxuskTW8w7z56Vv602QRYsQ90O0WbazR7VqlDa8,635 -babel/locale-data/iu_Latn.dat,sha256=oYsdWLomYvVJg6kIQIKYOdSGWyrEHftc2U9bZ0sHFD8,904 -babel/locale-data/iu_Latn_CA.dat,sha256=C449KxuskTW8w7z56Vv602QRYsQ90O0WbazR7VqlDa8,635 -babel/locale-data/ja.dat,sha256=86xPBOY1H1e0fLC6g_61R_ipS4QGbB9sXst_JwwdAWo,179695 -babel/locale-data/ja_JP.dat,sha256=o2pbFmlkEZjV7CHE9cYENYVv2kQA8texhNDhEecgQ9w,635 -babel/locale-data/jbo.dat,sha256=2eB9ymOoFb8abB-v7ISAGcAg4HEEMmpuseHIXFgtqkU,1008 -babel/locale-data/jbo_001.dat,sha256=wwLKEXJOwULYD-q92kQdvpiwW2fgis5YcsP2mT4tnT0,746 -babel/locale-data/jgo.dat,sha256=9X63SqWqQ7TK7Z2RKaQwFBP9sMog_4cQN5Sr_KBlyL0,9031 -babel/locale-data/jgo_CM.dat,sha256=1lDyjZLpK6fS6oSpfnVLdYd2IUgGlUQfF53BDdK7TLY,636 -babel/locale-data/jmc.dat,sha256=lRstulkfCj6kDI8VLH_3GS4awAqVsylMU1hvvWH7kJg,15374 -babel/locale-data/jmc_TZ.dat,sha256=fODp_OsVygbH0UoRsx4HHcXrde02bRKc-hy80sDkcK4,617 -babel/locale-data/jv.dat,sha256=SucM3ZDoHXO7PsbOYNLdx0UmQqWUll2i1hDqn-zJY9U,103138 -babel/locale-data/jv_ID.dat,sha256=G3zTSeqWMngemp5lAcghudTcv-i8egZS12P0-T1DNXw,635 -babel/locale-data/ka.dat,sha256=dIskDjc7ZdRZ1rGJIHxOP_47WO_cdjLDx4eltYETbUE,230234 -babel/locale-data/ka_GE.dat,sha256=zzw4NZBk8dsHIBeEzB3Vtf2_1nC4DhqblLoBUXfNHN8,635 -babel/locale-data/kaa.dat,sha256=KjLQ7_UT8pqVmgqy5BXJHe9hbPceKi6AIyefM4_0pYU,17585 -babel/locale-data/kaa_Cyrl.dat,sha256=zW7tFPX2CPo0NwR2o1DCKu8Tsvm-Aef9RAuy6AcaJXs,693 -babel/locale-data/kaa_Cyrl_UZ.dat,sha256=DugGAZvImbUSHvvGmGw0ekXXBj34vEjSkHhSxVXbc-g,636 -babel/locale-data/kaa_Latn.dat,sha256=zW7tFPX2CPo0NwR2o1DCKu8Tsvm-Aef9RAuy6AcaJXs,693 -babel/locale-data/kaa_Latn_UZ.dat,sha256=DugGAZvImbUSHvvGmGw0ekXXBj34vEjSkHhSxVXbc-g,636 -babel/locale-data/kab.dat,sha256=ykLRvdRtHpY8y7JDHOgC03KxAyvAfxzYQUl44CKKr6s,121528 -babel/locale-data/kab_DZ.dat,sha256=v6fd0kyletm1kfN2ibSUh6oddckpLHjPd_qrq6FQpd8,679 -babel/locale-data/kaj.dat,sha256=tKKi4eYHT0zEJLSHi2DefEGyTTu4RCKpnANu7NQhAPg,2696 -babel/locale-data/kaj_NG.dat,sha256=1RODrBAPxCrrlTgfhpdz-VxQkTdjJEJYHaJEsOgQsNk,617 -babel/locale-data/kam.dat,sha256=br3FOMP0rS2tAq1tSXwYq197v2XXHqWtX_TM-39QP1k,15506 -babel/locale-data/kam_KE.dat,sha256=8AQQhHQdaI4rBKXfTivg884XIDfZlbDEQM28KwvB2Og,636 -babel/locale-data/kcg.dat,sha256=dOZR1bP8sPbhD2Mk496Twz8MOivgGQoliuDMAWSO9fY,2549 -babel/locale-data/kcg_NG.dat,sha256=jCObRZa-t9Khgx_M8ps0PLbeit7rPCFtHeCOhtfCnOY,617 -babel/locale-data/kde.dat,sha256=cFVUYeayn6-kMX6PwQQ3J8rpHMql07_V7lSluInC2x8,15810 -babel/locale-data/kde_TZ.dat,sha256=2sfb3gejYM15YtZeRrc0AJgdaJhqgbVJ9c9yF9SlJmg,617 -babel/locale-data/kea.dat,sha256=o2awnBismFEWQemtUPASmZMVLlOkJNDtuD6egmvMatw,76097 -babel/locale-data/kea_CV.dat,sha256=LyzxPE6yP0f9hdD4uvu8CvEp8nmcmRDVuRI8YxBQ5wM,617 -babel/locale-data/ken.dat,sha256=PMrEuqYRQIhbpP2dPowHOL_NKKFpEBd8qNUV_NpZnDw,719 -babel/locale-data/ken_CM.dat,sha256=jNEojAAtQqJMkEq84kxHU5hGOVQCOW_hnGQScxycYMo,636 -babel/locale-data/kgp.dat,sha256=OKx1ZbBdHw75AdzrT-petm32Dj8wJdtUQH_oai7v0qI,184092 -babel/locale-data/kgp_BR.dat,sha256=FNSYAqPhkzoZzholxTydgasgkuw7jgxCfyKDbeRiLt4,636 -babel/locale-data/khq.dat,sha256=OcW4Zprt-WlTePo7DzquB-yS8vwfPqjaijpkrX5l75w,15753 -babel/locale-data/khq_ML.dat,sha256=ViqThOcZTHZBtebV8LJY-DXdWVX5jAR5pdlBrTKws2Q,617 -babel/locale-data/ki.dat,sha256=vffpb6Wy7TN__hZQH1SJYi81AxamhBnEK_XBCiF3OFc,15451 -babel/locale-data/ki_KE.dat,sha256=PNoxhP-WLxW2vH3fS8N9Nuy0tVimMP-ZAaCfLfnfDN0,635 -babel/locale-data/kk.dat,sha256=93jtpKcfiQBRbyeOyJgqETZXGOntosB2nGedgHShM1w,200871 -babel/locale-data/kk_Arab.dat,sha256=TBsZm6iLJ_dw2mNxDdQk95jELZ87mArNl12iktCwKwo,222820 -babel/locale-data/kk_Arab_CN.dat,sha256=HCoaBi32ENdpJx5usXdBR9aR__MHFeKV-wTo_53tQNk,635 -babel/locale-data/kk_Cyrl.dat,sha256=z3n_4vbUD_OcZhi9Tm8vkSu9h4RUgavZ_CFrujFbuWE,1584 -babel/locale-data/kk_Cyrl_KZ.dat,sha256=eaQW7Va1z2L34hs-D9QgrmyvOo4Lpy2uxxvxms6kL4s,635 -babel/locale-data/kk_KZ.dat,sha256=eaQW7Va1z2L34hs-D9QgrmyvOo4Lpy2uxxvxms6kL4s,635 -babel/locale-data/kkj.dat,sha256=IYeF4Uoeb0kfxooFLFcqmdIwGWbWh0MUfBpztTxWFEw,3373 -babel/locale-data/kkj_CM.dat,sha256=gqHwzVPG_Lo3svaIVe0Kfhj4DaKtstOxJJhUGvvpSbI,636 -babel/locale-data/kl.dat,sha256=D8SSjxLZliXOxyTxpC1x7jYbFQn4WbSV3lfotFFt3Jw,47570 -babel/locale-data/kl_GL.dat,sha256=c49k11GtBG8-vExsQNo_d1pFaQm0LYmXl7QcMSTEApA,616 -babel/locale-data/kln.dat,sha256=pldtRERWUhEOA85_zeFJfshpMl6gtgdELjGyNGtJzOo,17427 -babel/locale-data/kln_KE.dat,sha256=ZhkKgZ2lTdWAvWVOJEFlp4ASvjk2hcyTc2_P9s3mykQ,636 -babel/locale-data/km.dat,sha256=TueDML7PuHjlME0Bew9xgkFSFIxBOsJTzpMwyjyvuXs,176653 -babel/locale-data/km_KH.dat,sha256=bnykdq-RYJ0ZwajV5dFBVtM4anVkLjXutFdUxKByQ2o,635 -babel/locale-data/kn.dat,sha256=n0AHHLkpl4FIYeiSCEMDJF9mFr5nCgHcV8HSCOKz8yw,267281 -babel/locale-data/kn_IN.dat,sha256=rx1LJYeAP8xU6MIU3yhHLuije5OQWL_wzz5glztIdi0,658 -babel/locale-data/ko.dat,sha256=tYIinjTM5ZLw-AvWerUR8h4FRQ09LGfoRNyutl2iMLI,154621 -babel/locale-data/ko_CN.dat,sha256=U_q1api28ucaC-ABOBb822CgSq4nywhMN02uS7TCREI,1187 -babel/locale-data/ko_KP.dat,sha256=BCMOKQSzXNnjAUWfZZ8pbFoSDBed9tnnatyqIePNWio,816 -babel/locale-data/ko_KR.dat,sha256=CJqKij0oULIeCIG8McohdnhEsfJxUwmJHOpJY1bLrNY,635 -babel/locale-data/kok.dat,sha256=1pS0SXgetR-9DHtL71PAw2VIRp51a9KfxoSnw3BoxFs,179992 -babel/locale-data/kok_Deva.dat,sha256=EP15THeV74hd5G3DDXlwIHzxQvpECa67TioBGaur3l8,693 -babel/locale-data/kok_Deva_IN.dat,sha256=GiRy8Q6VCULzgIxNPFEZzABKOg13Un-DblBwXnBSeEk,659 -babel/locale-data/kok_Latn.dat,sha256=jVrQX1SeylbWO6B3ana7r9r1sRvMyOvBVwPfyXxB4uM,29867 -babel/locale-data/kok_Latn_IN.dat,sha256=GiRy8Q6VCULzgIxNPFEZzABKOg13Un-DblBwXnBSeEk,659 -babel/locale-data/kpe.dat,sha256=AHR_ZG1p518D4yulM_cs5E58iMsVXKk4Hi6k0V0IDhQ,1331 -babel/locale-data/kpe_GN.dat,sha256=7eabSFmszupn0IlzP6TRg1-7FZS9AB3JdBkDgJtIs64,1210 -babel/locale-data/kpe_LR.dat,sha256=4O2gzHn_orogC0qOiIyIox-JjyA73hEAe_fZ3Fj5tYc,617 -babel/locale-data/ks.dat,sha256=KySSApsfBfxM26TaXsK2gChd3noV1e5l41gUsaC3DA4,111691 -babel/locale-data/ks_Arab.dat,sha256=fzDL_rCiC-BqymPhF6GBX-d5uVkvZCE4e8JS2L8cjRU,850 -babel/locale-data/ks_Arab_IN.dat,sha256=-OE8PBigMDH8zQrxiaffmabm91Mi8-OVdDR2JEK8xPA,658 -babel/locale-data/ks_Deva.dat,sha256=weXN4SV8247g4MW8R7MHXM6sKk1qhf2Wmowls_zowAM,12510 -babel/locale-data/ks_Deva_IN.dat,sha256=-OE8PBigMDH8zQrxiaffmabm91Mi8-OVdDR2JEK8xPA,658 -babel/locale-data/ksb.dat,sha256=vbB4JVStmpZIu9_vi6f9-Dt40sN2wPvF9Y2JzJJ9oEY,15356 -babel/locale-data/ksb_TZ.dat,sha256=PvWBMUhpMhV27zE9BCEJW8oMqRXWK-6VwmSsXZDMTPY,617 -babel/locale-data/ksf.dat,sha256=IIgvqtnlTxMmLSk38JyZe0PA5gLEX86ZDcaVIULp7Zc,15952 -babel/locale-data/ksf_CM.dat,sha256=0OOsuUS0S_AUxUPySFQOZVbPcB3F2I4yzHZm63YypDU,636 -babel/locale-data/ksh.dat,sha256=M6W02xiGUibDW13RIB7psbwMA2wdNYQHz3Bp9XljhZ0,76781 -babel/locale-data/ksh_DE.dat,sha256=EhgYIJQaC3LGiWrcE2sdfUC8LOWJnciWORi_yoRkOvw,654 -babel/locale-data/ku.dat,sha256=beByrhXjB8iRhaNGxkQaxnUckutus3MJ61Pm-Ro8SqU,117087 -babel/locale-data/ku_TR.dat,sha256=LoNyoL-poYXgqejDzB9-zI_0TIJF3amwyzcs6UnHUII,635 -babel/locale-data/kw.dat,sha256=x5JFxfv4DvLd5Xnq5J7JlbcffhYNXwIn5-Jp3Q5QV5A,7242 -babel/locale-data/kw_GB.dat,sha256=ol7LWhJJFNoNkFEjtk4x1PpLkML9UbuVmZAuG47LokY,653 -babel/locale-data/kxv.dat,sha256=AMdZKJIlfkcpSetPdGRHX8LLN0O1s14NAtDzekSi01E,69503 -babel/locale-data/kxv_Deva.dat,sha256=MlejiNswS_LJAemj-vyEyvXc2SyminqNCWbaq3eceKk,86834 -babel/locale-data/kxv_Deva_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 -babel/locale-data/kxv_Latn.dat,sha256=XqtuF9yh0tBrDPoRpZj_1Mw-QUqt98klOlgZXh7qPus,693 -babel/locale-data/kxv_Latn_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 -babel/locale-data/kxv_Orya.dat,sha256=lsjWswZU3LWmYNE5tLn9cla-WSbdfqAoltC_OvxtR28,85964 -babel/locale-data/kxv_Orya_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 -babel/locale-data/kxv_Telu.dat,sha256=rXJrWxHMFrPeheoNt9NvKLzNoGLpNXiHpc4PsuS4tX0,88218 -babel/locale-data/kxv_Telu_IN.dat,sha256=Z43Yc1G4Bsmu7KukFfgXfIl1Cq2Rqa7RQQNYygE5Aak,659 -babel/locale-data/ky.dat,sha256=PAH_ov1xcnJAie5ltB3I_XAUJsrE_8tFwFlOjorRBts,179889 -babel/locale-data/ky_KG.dat,sha256=CdtjRR2iZgfG5pdz13MX-EHTDtN6BdLeZwGCmrZnrkU,635 -babel/locale-data/la.dat,sha256=nJ1hfe-gN1dlNH5qawfkwjv0bNzefm7UCEwnYv1O6ig,33837 -babel/locale-data/la_VA.dat,sha256=dhULocaXyJGitrljd1w688DTmezcmmY8YcoKsT30n34,653 -babel/locale-data/lag.dat,sha256=mtjtyLHLCfoUr4STtBqLXOxLnZ0igugTC9fZiyJZk8E,16281 -babel/locale-data/lag_TZ.dat,sha256=LlfEls8DDvoVp07MYnFdLBPUhkJs2kzwUDk45BZIO-g,617 -babel/locale-data/lb.dat,sha256=lUGm84DfwLgQ3vC0mL_foFf5dSZZKDTIuwCRf5BjgVs,135641 -babel/locale-data/lb_LU.dat,sha256=s0AY2-F7BqA4tMcRPjCfbzbXRbHsKnD6uIQGSkulCT8,653 -babel/locale-data/lg.dat,sha256=LnMo-qAcWHbbAuRmCTz4_nBGHgUx0aojWG7C8TFn__Q,15834 -babel/locale-data/lg_UG.dat,sha256=nyn_HKdBHGkHlbjlDX28L5Z9k59MKFqvmVL3vg222s0,639 -babel/locale-data/lij.dat,sha256=Ic982St82mPMfuQqqLswPtOy8LlglfcLYk_ftIDM65Q,129648 -babel/locale-data/lij_IT.dat,sha256=rtPerU7ltwkHHc7HErfwJfX8TDSCpTHvBzwKBdSt4x4,654 -babel/locale-data/lkt.dat,sha256=U_yXNWWJkqlepaZ9kS-FVOAHQKVmc0vhwgWgFRzXnqc,11180 -babel/locale-data/lkt_US.dat,sha256=ywWg9m80tNjumAxGNrBwYhXhc28rC4OSdyvkdXAKs5Y,654 -babel/locale-data/lld.dat,sha256=2JBQPu8T-cMVqzVOFs0Vz-0hU2RtHMyTvj0F8wPl2EU,95451 -babel/locale-data/lld_IT.dat,sha256=rHSiZg_-Ba6si2LONUCmQ841RxcX2p7V1jDSuwNNBg4,654 -babel/locale-data/lmo.dat,sha256=GYoR98Ap5hosG9bfENEA3sCkR-msF_KvxV2nLydjs9s,1659 -babel/locale-data/lmo_IT.dat,sha256=Xg4Qvye_hPNJLCTwoBuRMAvhtjQb8VlD9SaH_5hVG9s,654 -babel/locale-data/ln.dat,sha256=OHqUbo7M-600CtpOUNqq3B-Tt0_L3c2GgyR2xZhuMmE,23909 -babel/locale-data/ln_AO.dat,sha256=VNcFViSaGw3f92D60Uk7vxQ8i6Q9H1FD6paDpJh-r4M,636 -babel/locale-data/ln_CD.dat,sha256=Ao_lUWuswJgGHhVJYs2_Z8LyN4iqgYsAda8f9UarT6s,616 -babel/locale-data/ln_CF.dat,sha256=g5Jy7ovUkwcn0W5Ov5bu5SBc2iM4SWwUFwtzWzAAb34,616 -babel/locale-data/ln_CG.dat,sha256=jQld0yw63ufWbhlOt81wjf731zZluc2zWc1A65kBlAM,616 -babel/locale-data/lo.dat,sha256=hXtgT4nkqb8h669TsazfIFBsK23QNpcCZbqvb6SXWBo,189363 -babel/locale-data/lo_LA.dat,sha256=gNa7KRGYWI0h0PcHJ35bwnPskILOH-1jLjHCXP85jaU,635 -babel/locale-data/lrc.dat,sha256=GbK-bWw4TlGvGRyM4WxDujkUr7Gdz9k2A__fSelwEdk,13594 -babel/locale-data/lrc_IQ.dat,sha256=cy9DO9oAKI51NMitAb2MTbZoxtaAPzaYVGV0-xd0mGA,1255 -babel/locale-data/lrc_IR.dat,sha256=-ca4EmTX5VAi_5tDfimpevJFb-g8vAjgpe_jkDTDZWQ,679 -babel/locale-data/lt.dat,sha256=1h5TjFvNSuqrE3JntTmnLN9wW69jfkVakAIICcmoVbc,262613 -babel/locale-data/lt_LT.dat,sha256=j8dIQrFc0s77xJ4P-UGiDcaNqZW6CXOEiOh30FemNiY,653 -babel/locale-data/ltg.dat,sha256=qRaChoBWcmkL8As9MgCysSh58BP_F3US65EaA97ZgZQ,2632 -babel/locale-data/ltg_LV.dat,sha256=Gm8suGbSLaNGzzWPjuEKVdEqJH18ABue4htegryH3bw,636 -babel/locale-data/lu.dat,sha256=4TGAGkYbDHz6QAKQHHXoRkl0Wx-0LOiMkTjDgwJq0TU,15331 -babel/locale-data/lu_CD.dat,sha256=K3k0wMpIbjusgPC2W1q2sxyxynx2M78zOLjt4ikcoZA,616 -babel/locale-data/luo.dat,sha256=xJNSb25OVQogO42d-Jy2fR1FzeSfW9Swwt9Wat4EmsA,15201 -babel/locale-data/luo_KE.dat,sha256=jVUd3oO_U7IP6KmvSkVHYNmlCc8_-m2mpJMBaqArSFI,636 -babel/locale-data/luy.dat,sha256=pcTbaSctQyVgVVh4nXoXxMQGZa3Ip7hsfRN096zwEsY,15030 -babel/locale-data/luy_KE.dat,sha256=NVkQAtmDBXUws3KybnM706J3lwUc5fJo-YEjKd1zhiw,636 -babel/locale-data/lv.dat,sha256=s10obHsTYkBF99rADGvkxupv--JkRPQS2aM6sWvbM4A,201861 -babel/locale-data/lv_LV.dat,sha256=oS9W0pdiTNtMTeGOsef2K7Va2O6k-dQEtN02qCaAoFw,635 -babel/locale-data/mai.dat,sha256=Foj4spiHhuOjBZqaXQxZNCWwdCoslwB7KSX6nXQReu8,95488 -babel/locale-data/mai_IN.dat,sha256=DoclpHa5nlHcAflg286ycOjQ6trZP8uHThzcp37Y91E,659 -babel/locale-data/mas.dat,sha256=xxugbgGc3JxvM2GzV1vmKy1buszviy7NtSH-y6GdMZ4,16595 -babel/locale-data/mas_KE.dat,sha256=zH3Le4j03YNsPjt_-GL5RvPG0znnJKQ3ifbLhMZsoYQ,636 -babel/locale-data/mas_TZ.dat,sha256=VyRJlBqV_fukTqSR9Jxr-PujSA7u-MP9aXrJBVJCKiA,638 -babel/locale-data/mdf.dat,sha256=NQH9Yo77oyvxZj_93XIf0e00JDAoI2ydlCSS3klNgwk,2240 -babel/locale-data/mdf_RU.dat,sha256=sVnTxgzU2rEwl7P4d5UY6XOKv1XUCbtkEQO0cu2OlOk,654 -babel/locale-data/mer.dat,sha256=WFl2qQThWrilUbBaN4UJQb97ZrV3uOB4ZTO9kXsjtnQ,15426 -babel/locale-data/mer_KE.dat,sha256=WqDQTsO6BtbROdcj3p3omUZk8XB6HiJrIlCCj_wLPxo,636 -babel/locale-data/mfe.dat,sha256=n5lJPfd4pjC3xmVm123ivVpjg-WAkLQq9YONw1JlpTs,14835 -babel/locale-data/mfe_MU.dat,sha256=MHUOKiQPHuUbhT6hQXJEkpGDvTUyMV4btJTuiLhJQ4k,617 -babel/locale-data/mg.dat,sha256=9PbZhJ2v9RP2XSzwv7bBPTGlSNYJrUUISNkTwdF1Z7g,18776 -babel/locale-data/mg_MG.dat,sha256=uJbGeEzIn4Rh5Bh4hyV-Hs0_qez9KBeOi4hvfC3zkIw,616 -babel/locale-data/mgh.dat,sha256=ezIhzOd-qzUBRud5ZPjbtBBc_mON2HuKGu_okOahCbk,9753 -babel/locale-data/mgh_MZ.dat,sha256=aFZZ41qtARE6CeecCEx99tzW_fp-XfkAc10wpQ5YeGM,636 -babel/locale-data/mgo.dat,sha256=xyRaEmGV6Fw9nVfReQJY7KfgGwdnvQzScLiH0appsLU,3549 -babel/locale-data/mgo_CM.dat,sha256=Nsp33t4abZsKdDmIgB8tb08OsEh-7JXMY_JKCsd61zY,636 -babel/locale-data/mhn.dat,sha256=RpSxxevhvYxyX0dq1RAhIqoREyyPwKjsByXvOhQkAMc,693 -babel/locale-data/mhn_IT.dat,sha256=3UoH_l7yItN5qXKPaq6bWK_CvK_wD68zkxzLrCwxr9A,654 -babel/locale-data/mi.dat,sha256=ZlyyKSUJCtl7z94uIwEFuC6uBGWXOTM8F1TJLiJeSmw,85086 -babel/locale-data/mi_NZ.dat,sha256=RElP_P4F1HDHjSHsdt54vriFcz0TcIeT26pm-_dy-Mk,635 -babel/locale-data/mic.dat,sha256=Sp71dA3ZhTiiPTfk3b0Mm6y6CM0J-mGR0av-psrUGtI,1638 -babel/locale-data/mic_CA.dat,sha256=uZoBvRFux6M40HcGau7E6V0Pyxh45Bxi0SFngOxLWRQ,636 -babel/locale-data/mk.dat,sha256=xVJU-aubw2FQ_THzBUGd5LHR5IU1VjGkPOeRHpU-T98,216606 -babel/locale-data/mk_MK.dat,sha256=qlI1ZtLgyX2fcYS9pnTa8illdSnPmfNXORjws-H93lc,635 -babel/locale-data/ml.dat,sha256=tR0lt2uQuzmsBgXWIuRpz-AzD00S_OrvLc-QkiYAX-U,246736 -babel/locale-data/ml_IN.dat,sha256=VwCnSFs3oh7xo4-nS1HzWj_xICDzG6E-AW0Oxi6Lu08,658 -babel/locale-data/mn.dat,sha256=O3oSpnDd3Pt-dRrwdg1v245S6Iym3SBnZZ3gqOhQW5A,183647 -babel/locale-data/mn_MN.dat,sha256=qhJGIDXSglsa3BYCalsusoyiKgOj7PJ90Yi4ET2-nxc,635 -babel/locale-data/mn_Mong.dat,sha256=zqCuL28Sx1DS8LGqtC2nyTCoEpSFpF0vE3wvBxpHK1o,1726 -babel/locale-data/mn_Mong_CN.dat,sha256=WTF89EShdtBd0Vz62erFwl9KTG-GgEyzjjmKWuJCFH0,635 -babel/locale-data/mn_Mong_MN.dat,sha256=BTah7qfU9QayUb7evUKdF9c4Ptf4NqqO6SUeUql2Bak,12935 -babel/locale-data/mni.dat,sha256=aS_XIOmHRptFT_dTFuxgTvB2cEPKHJVVM_vtURTFo7A,12972 -babel/locale-data/mni_Beng.dat,sha256=7PTKZT_UE3WNiipyGHnn2SDNFNhAcqHhEJ8T0Y3O7gI,693 -babel/locale-data/mni_Beng_IN.dat,sha256=nAW_LfRtJqC6-ehdC8yyszjhibXqMAXPkuaz8POQav8,659 -babel/locale-data/mni_Mtei.dat,sha256=zdzwNyaRIil7lkjEf6ProQX0nwYlc5VF4r4gHNTUVTY,1720 -babel/locale-data/mni_Mtei_IN.dat,sha256=nAW_LfRtJqC6-ehdC8yyszjhibXqMAXPkuaz8POQav8,659 -babel/locale-data/moh.dat,sha256=FuywEiFRXQe1GUv9zPR15KOsZLr1TykmuglinLINTqE,1316 -babel/locale-data/moh_CA.dat,sha256=AXdrS_As8DrrTGs5IHmQwXoEXndFuoWoTGXPXcL4Pk4,636 -babel/locale-data/mr.dat,sha256=UG0FkufUtQ7qvzcK_WENRq6SgPy4zAcviYDQL-NrGY8,234157 -babel/locale-data/mr_IN.dat,sha256=HOu9QR1BLbciqF6BeTfWvD0L5Igwv5HrCWu9y_vNns0,658 -babel/locale-data/ms.dat,sha256=l_YQob4w4jdu0U-Z-eMYK2bPAyU0Xeekar0olT5G21s,114970 -babel/locale-data/ms_Arab.dat,sha256=9zretwvdo_9N3SIxLKsUtDVNZlmh0OhrPDxmtWqw22U,14359 -babel/locale-data/ms_Arab_BN.dat,sha256=vTEKSh0hjyiWJhf503-fwjADvTCN0AqRSzF6muL52JE,1335 -babel/locale-data/ms_Arab_MY.dat,sha256=w6ZZrz3qEmnsLsWtJA8rTohqF1cxctkzSj4WN0XHuHE,635 -babel/locale-data/ms_BN.dat,sha256=vTEKSh0hjyiWJhf503-fwjADvTCN0AqRSzF6muL52JE,1335 -babel/locale-data/ms_ID.dat,sha256=ZPgnDJgiycDHGZnN3Eg7OgZp1jIYtiZzMqm9f3LOtR8,3440 -babel/locale-data/ms_MY.dat,sha256=w6ZZrz3qEmnsLsWtJA8rTohqF1cxctkzSj4WN0XHuHE,635 -babel/locale-data/ms_SG.dat,sha256=jsfNag5WQWdejzh5hayA7_-IbY03MUVPAPDHUJyCZRg,654 -babel/locale-data/mt.dat,sha256=Xx_-as8jJEXOBAgfFX7vE6-2Qex4FWJ5rYW3DlCbu0A,49384 -babel/locale-data/mt_MT.dat,sha256=L5q5LizZQL0-FLj2U-wn2YhkpL3p7ZvjdyAvH1wwUG0,635 -babel/locale-data/mua.dat,sha256=N4zi8XyQdW_TmszNHQyn4zmia-MpCNWx_Vudoaj075Q,15861 -babel/locale-data/mua_CM.dat,sha256=Ib4K67KfndwGsoZDH0ehHA-F7V_SUj7q9VsK2juzaIQ,636 -babel/locale-data/mus.dat,sha256=zPqFwR9oCIJ0mA6evYwT75cWt_QBF-0eDZx1n6fmsDY,2722 -babel/locale-data/mus_US.dat,sha256=xcd4KKUIqBwSefSC0GGSDkvXL46eDWgWolJsGuSf654,654 -babel/locale-data/my.dat,sha256=9crk_2tyVPKBH_YWC_D0h8o1nbHH9k34e414JywM_6E,183534 -babel/locale-data/my_MM.dat,sha256=l53-D5YL-BbmLZYveFV2ISa4NcrSwTfKsRRc41Jd42U,635 -babel/locale-data/myv.dat,sha256=78-9U5eDf3V2q3Mj0MazIxh1n8sjyVjQ1O0Oyt5875M,16828 -babel/locale-data/myv_RU.dat,sha256=sncxwOpjfFkRASKHMP1ytKcjpF4w4GjU_JEVqXv84Tg,654 -babel/locale-data/mzn.dat,sha256=8Y5VGlzCxKXzBP5iFsyflLZSS1x0_3T8sqbwivfU45s,46144 -babel/locale-data/mzn_IR.dat,sha256=fjN1ek2fAzZyzFJpjX_MIc5oFOEqj7LS_SNdBOQS9ZY,679 -babel/locale-data/naq.dat,sha256=N07nsBSSEuNZ4feWvo7b4YWwbgjBwlxG9653O-OlhmI,15797 -babel/locale-data/naq_NA.dat,sha256=e5gAIOo7le-dVOAX_k2OW6gEyVfEUxcYDUJu_MBDY5M,617 -babel/locale-data/nb.dat,sha256=-bGHDSonSEdqfb2PxNC1_3jOHELm3CwV7xzEtpmWZUE,1330 -babel/locale-data/nb_NO.dat,sha256=J1UiN-AG900acMz4p0fBHtXDpS0HD6uGAi7gg9gOrsY,653 -babel/locale-data/nb_SJ.dat,sha256=XcK9_nKriUY_4k09kmgJalGTls-e5-YS-MCPEKLP8vA,634 -babel/locale-data/nd.dat,sha256=ITmP8mLDb63INBKlxe3yCZqRbvL83DrVBkIIAA_YjmA,15730 -babel/locale-data/nd_ZW.dat,sha256=KpZHrPr_0Ru0t9ojSK9ZiqGZ6-rSpvA7cX9C483ocRk,635 -babel/locale-data/nds.dat,sha256=q3dxEsEcdLTOigPQxhEXuaA4U03umMzIBNC4NkFvino,47839 -babel/locale-data/nds_DE.dat,sha256=Qmh5DjX65W58jXXXX3pxfxS_HVTJVavMspBPdph9sfc,654 -babel/locale-data/nds_NL.dat,sha256=E_EO3ylaBjJNThr211Yzefh5DRbkEFFVpbBMXIQ4U1U,654 -babel/locale-data/ne.dat,sha256=5Ob8IUrudfJp6yCzhHkmYC2mkVY1sd10I79pSXzups0,210152 -babel/locale-data/ne_IN.dat,sha256=SFloADZtyb42wygQmrMy7yx-aADGQECZJUey796oqnw,1292 -babel/locale-data/ne_NP.dat,sha256=LGc7Z5TRi0HRNQdY4bqPitV73gzTPWLd_g-6aPyvryc,635 -babel/locale-data/nl.dat,sha256=iGSLuhpd3RP4i7tOj5RU-vJ1KC_Ij6d5npxRBE5HisQ,149984 -babel/locale-data/nl_AW.dat,sha256=HYycPPe5HDhuP_zN89ysXR2AqjIbx_-XB2n0-TUPhxw,638 -babel/locale-data/nl_BE.dat,sha256=xHmyTOK9-rwPAH6fYdlPFZv-xinmTFNRN-LIGxb59HE,1876 -babel/locale-data/nl_BQ.dat,sha256=lZQzrmWsFTpiWwT9l6FakrNoll3QNP3drIMcxFkAy6Y,635 -babel/locale-data/nl_CW.dat,sha256=doEeLSsQVaSocP4jr5LxNrT5luSiG4hkgsMvoYjJBtY,638 -babel/locale-data/nl_NL.dat,sha256=bGo4nyjhqe1VtDHlP5EUmNkBKsViUSiH3h_ikXcPuRo,653 -babel/locale-data/nl_SR.dat,sha256=d-YXA5QA5b2Bh-WhxNTkyKQ4ROhuEg1CeGN5XmzWcZM,696 -babel/locale-data/nl_SX.dat,sha256=ciTQIntv0njAJGIUENzo6stN8yarfh-eBpmEzxmpJbs,638 -babel/locale-data/nmg.dat,sha256=NEWUb0RI43oeWByH9l1FvrLDvVQY6501jviEYmM_CCs,15468 -babel/locale-data/nmg_CM.dat,sha256=0A-cVUgt1l1ITnPRdcgWiQ68ZGqHDYd-k2wSc_nXnMQ,636 -babel/locale-data/nn.dat,sha256=E-Y0lC-8QKCXdc8CC_ZPN3bstsXsM3MD4Lu62XMMq3U,64042 -babel/locale-data/nn_NO.dat,sha256=98eWK3CBq5T_dI4a6dQH2TktRygMH0EnpO1EO7w8lVY,653 -babel/locale-data/nnh.dat,sha256=cWTTqJEXln6pUYON7H4PRrYr_uRWktSzcLGCC8jnTno,3530 -babel/locale-data/nnh_CM.dat,sha256=dt9ZNHX5ZQo-kcyT4-mVRCUiuzL-H2A9e0s0JrHwZts,636 -babel/locale-data/no.dat,sha256=xxkSV9bwDKWKK6dmGIaZGo4snfkON9eJUp3-uvlEC1w,191212 -babel/locale-data/nqo.dat,sha256=VQzcAnkUnAETAL8xosXBsQltlC4yqpSZNMkwtsWPRgI,109412 -babel/locale-data/nqo_GN.dat,sha256=3eOSTBaZM0RNLUDZFr__KTR_9iZoGUFq9M6bhtmHjcU,617 -babel/locale-data/nr.dat,sha256=ZHIIC8BaR5XBmnULOTfmAYzXmqImq5QmELqbFPKKQJo,2167 -babel/locale-data/nr_ZA.dat,sha256=e4_czqcyMye0qthJd2hzn_9inTS8WjjbFOzRJ3wAS24,635 -babel/locale-data/nso.dat,sha256=dsq0mvwg5QK35al0bVwZGUL961EuGS05vlFR5qb4HwM,6470 -babel/locale-data/nso_ZA.dat,sha256=TdOqVTzKNigLD6zOPR7W7QHT-Aw40ylsUE7bmMwgaKY,636 -babel/locale-data/nus.dat,sha256=4G1Wwsq1yWIew4nA-QBo2j8LmbqBTRZ0gCU0LYLUSUk,8215 -babel/locale-data/nus_SS.dat,sha256=Is_mWvnSS7j05Vk9RHcCfyLOdhOeOWRqgXDf8FGNEJQ,617 -babel/locale-data/nv.dat,sha256=7CBom-BDX6Wk2o878hmZ8DBY8QAS8Z99ExdmdVpq-vQ,721 -babel/locale-data/nv_US.dat,sha256=ak7j2GDaOMbAAmXQhfC9PRM8E9h_hJZL3UDqxqjCtoI,653 -babel/locale-data/ny.dat,sha256=XXvyFcRnNcH9KcAH1K7jzS5xLFsXMlXVW3yowhXPIO0,2152 -babel/locale-data/ny_MW.dat,sha256=kyhkcpqgIwSrUcfxve2OS2P9S1AfXItRHmy94K7cQ48,616 -babel/locale-data/nyn.dat,sha256=nVlbaYbL0QCkpVvX2rYYUFRsuI8BOjcReljkcamobSY,15668 -babel/locale-data/nyn_UG.dat,sha256=fEm-RMI6-PvIjbMUoZK68dTaiAmOqH1iDQBn23a6r2w,640 -babel/locale-data/oc.dat,sha256=0GZZe142JNEQ78GP7q1Sq7ZolzkVuvuK-QDCpLu4Z6Y,66841 -babel/locale-data/oc_ES.dat,sha256=H-3UJZkJCacW_BELiE_wXsXSVHp-dfxv59o3Hed8uho,33273 -babel/locale-data/oc_FR.dat,sha256=L80Ivl6dUpQJF4fAY1xaAeCRO9BkN0l6oCr8nRwogsM,653 -babel/locale-data/om.dat,sha256=wbIQO2bCc4PnuCB14zg9Gw9XW3mlv8D_D0C39SMnAnw,67446 -babel/locale-data/om_ET.dat,sha256=v0wHN7iwcuJN-iFgWFPrGdplH6P-0c8B2OEqYOEeb40,635 -babel/locale-data/om_KE.dat,sha256=DZ-2HywpH9NGEronFox-2AprzyoSg0xJAS62LNqZxcQ,1365 -babel/locale-data/or.dat,sha256=bdzcLCC-BRprtZBlm8pvQhvGKe2zTqjQM1JPQZrGapM,226771 -babel/locale-data/or_IN.dat,sha256=ur6a2cCdapMoKd-6KLXR2xAF2YU8xaueeW7UTPL2yLU,658 -babel/locale-data/os.dat,sha256=2ARxbjnZ_WYF6VQTsvXN4epcXlWpkXaVEhUNOJpAv8Q,14821 -babel/locale-data/os_GE.dat,sha256=8NgIdMuGlNow1dXmjW6y4xOis1_3raiXcj_jLBPirpI,635 -babel/locale-data/os_RU.dat,sha256=76MsfCZxiy_RXfhW7Do3Z8q4-jhp1hOXTBIsA6SlA-Q,695 -babel/locale-data/osa.dat,sha256=Ob-cu1978hig_-zOnB-PJW1_496aZ_yT6S7VTFkHoEY,4538 -babel/locale-data/osa_US.dat,sha256=qFfjNw8tVOdprzqK2wBlGH4eNMxDxDQqDDReucJZR-E,654 -babel/locale-data/pa.dat,sha256=pMTu_1agUQqVCaM7VB5fe9wM45MEsfVkDyzkKp6UlUE,202684 -babel/locale-data/pa_Arab.dat,sha256=VBfuWzoA5UdGbWTTG3cPAYL0ApWSup5U9LWBBh4bGUE,3789 -babel/locale-data/pa_Arab_PK.dat,sha256=1Si67Mz8DZfo2D8Ld0b5IlZpvnSMgg4W8_zZLrHrWQ0,635 -babel/locale-data/pa_Guru.dat,sha256=fs7wqdmuGaKQCCwGkT3lRQSKmf1dxG3jyLboZeVtPmE,1276 -babel/locale-data/pa_Guru_IN.dat,sha256=kpz873B1I65nfS3Z5vDLmmGliFVPlPM58sB6jW9nkBY,658 -babel/locale-data/pap.dat,sha256=EzSKg0e-PaX4UNpqi0JTYSjLYwqljGKI-uJLjR1uWhQ,28121 -babel/locale-data/pap_AW.dat,sha256=XZSGzQPijHNf5GjxUjDJQt88iD0ETRdZSTF8gn-1U4M,617 -babel/locale-data/pap_CW.dat,sha256=Mu-gCfh3bv9pzz9RrylMffuphhqgiRaODEZm6TtCpaI,617 -babel/locale-data/pcm.dat,sha256=BPyAoOzA7PpWZ4G9w2NMBzu1JqtXwnZbeCSrLk7j-Qs,161833 -babel/locale-data/pcm_NG.dat,sha256=3gBp38NIuVEbHlKAtQPQXJjT3DKvy7zvUqMAZS6pGzk,617 -babel/locale-data/pis.dat,sha256=aCXK39YyoYqy97wCU2idxYXLL9cC7myCIiJZeKgq-po,1510 -babel/locale-data/pis_SB.dat,sha256=-CDkXv_877c__o56bBeYHAAq2X9CcnKdI65DiE61_M0,617 -babel/locale-data/pl.dat,sha256=zWkc9nD_Ipzoj-nuymRuCLdWD3dG_wQicGQE5NpjSLU,227506 -babel/locale-data/pl_PL.dat,sha256=3s2XpVyaKha_K9MesXKD6sZNGSn1PVRtuz9XYp1xrsg,653 -babel/locale-data/prg.dat,sha256=vsad78h5dkCtaL_Gc-AGmXy3cc6Od1j4WCPGBdX6Tt0,17267 -babel/locale-data/prg_PL.dat,sha256=5jT2Yeek_e_MpGShmy2v-0WWhzQkQ0WsJQlUYfIOcfU,654 -babel/locale-data/ps.dat,sha256=2Yvun7eRlWEkh6Om_QxdW3Hq3AwyYqoM8n0418VjcbU,158607 -babel/locale-data/ps_AF.dat,sha256=sNE6bgx_nV0VWKa8InelUaFYSa3Fpx6byJw0KT4B1o4,678 -babel/locale-data/ps_PK.dat,sha256=ZtzTnH2thc6-J5PbuMOq3lPu57kbqDFxO53s7BSdVSQ,7119 -babel/locale-data/pt.dat,sha256=6Db_e_tQGLJop802pMj8aeNGcuKxa9iKTCZYBNM4crs,183448 -babel/locale-data/pt_AO.dat,sha256=49hMN9AJBDNjcsQw-nl-jY_VKHanO0PGfvd--OxqOxI,1022 -babel/locale-data/pt_BR.dat,sha256=dVKYp9IwRMbjZ_ee-eXFPudSuTRrROvbFzY3dYTYmn0,635 -babel/locale-data/pt_CH.dat,sha256=zAvD0sYytEquwzY8ZRbVCcy7DS2ZzUXofl5qXOixZH4,653 -babel/locale-data/pt_CV.dat,sha256=zOQn8d7--PKrhRUhQ5mPKW1Km6Tyh5JF8hstfxpITzM,1039 -babel/locale-data/pt_GQ.dat,sha256=qI1hvtuSHOev12ips9o3Vy78NJpOMjhkW625M60lqZU,616 -babel/locale-data/pt_GW.dat,sha256=SRxbFDIg9FwEjGjutRI9P1SeLiZBqStwSqPuHBFZwLQ,1002 -babel/locale-data/pt_LU.dat,sha256=z9aq0BTWEnk4lXveNpmi89TKcXN_t6Ud4A_C97uyOuw,672 -babel/locale-data/pt_MO.dat,sha256=shItJIIDrxwlcQhWLhlZG0uhyTlSBDcTag5ivxmb9TA,1635 -babel/locale-data/pt_MZ.dat,sha256=UZ1nkXS9CpiCM1IpOG5XpGK2QqM5mE5lAgRLTGAWl-4,1042 -babel/locale-data/pt_PT.dat,sha256=_H5DG5pOSZR_5gjjycrMBKaOJd_euwJln1pa3MPd1Mg,96277 -babel/locale-data/pt_ST.dat,sha256=Cm2HUxlGtZaz8IXX5Ohms-HYuKgazuBcOZBz5rWOzEU,1022 -babel/locale-data/pt_TL.dat,sha256=CIIDM_Zo0xiu4gXngxYtkYK6ldTNZMRaikSD_sAokMw,1002 -babel/locale-data/qu.dat,sha256=UueUbNLnx5ZwsqJcvzKUTdK5_wKBOmR2haCj0zhd4RQ,93300 -babel/locale-data/qu_BO.dat,sha256=7RUvNrmuLtwqP-qab667GocH_c4gIvpas3dbN1uaGxU,878 -babel/locale-data/qu_EC.dat,sha256=_5V4RYoPb3eCAA7jOCNfB7WZqd9sWP1E0tlF8mGeqFc,837 -babel/locale-data/qu_PE.dat,sha256=Vdd0fcK2dtFgyui3Q9NElHuJ841RI-T5l2UHeBq8MQ8,635 -babel/locale-data/quc.dat,sha256=dIAxk2IuDZhWTj-NKC9PiHBZOnG9ARyANdJZUfoVlJw,775 -babel/locale-data/quc_GT.dat,sha256=8M8KtmnsJiTCaf-Nl5X174Omtyc6a0ri07kysDC3-5Y,636 -babel/locale-data/raj.dat,sha256=qkuyRruXTTQLDgijSA0Equfz19Aqg0p5Mg0heaGKT00,2428 -babel/locale-data/raj_IN.dat,sha256=SS8GPzJu5j6nfXlDoX4NFPN2QScl4M0YNsrFmWklymM,659 -babel/locale-data/rhg.dat,sha256=dHahMWjQ_tUshtlLauqf9b4Y36LNJFGrMBsVmjBzlqQ,4611 -babel/locale-data/rhg_Rohg.dat,sha256=DAOuwC-7uaIuZtrIXvUkYt6YkxqaKpHOma1m0-XTtrI,693 -babel/locale-data/rhg_Rohg_BD.dat,sha256=DcLSBtf_k_f6B102-vbhBOjfzNZjiTDvEUdIOQfVc8M,1212 -babel/locale-data/rhg_Rohg_MM.dat,sha256=HspZzbytcZQ853Jstp-eFEdqSMAD3-shgjrdxY_KQrE,636 -babel/locale-data/rif.dat,sha256=4KtUTy4D-OsLftEaIHU2fw1NUhx0pwJXXie51jI3X10,49902 -babel/locale-data/rif_MA.dat,sha256=Qq7ZdymuPdjHeYDUDGCjMuQBRU7w5qGdgzzZ6wPfN2o,617 -babel/locale-data/rm.dat,sha256=aQ4MmwhjJbBakV6-C-6yjDvN8jvVa8Mk0ovdYgVkR9k,95006 -babel/locale-data/rm_CH.dat,sha256=JHAlYXMD9Vks43cU_gcSEQmqo6pogWPl_R4VMUK2iHI,653 -babel/locale-data/rn.dat,sha256=LwQTzbFINNeIhU5qJsEnh-raxcCDXVMGA6ZL95aU9UE,16241 -babel/locale-data/rn_BI.dat,sha256=r5Cweh7L4EAZK-qTmJuloWc33iXeR4IJUbknrl0XC3g,616 -babel/locale-data/ro.dat,sha256=13UpR66j-nQg4kbgGH4ja6ygDq1k3CCErykEWeeG8yY,200714 -babel/locale-data/ro_MD.dat,sha256=pUZY970zkBSBpVOIA3Ss-6vDJUF3ZCEWYNs4_LXcpAY,2881 -babel/locale-data/ro_RO.dat,sha256=lLYIYWhDUmq1EZ9oV5SETauVa_5uNN5p52dW7XeemRk,635 -babel/locale-data/rof.dat,sha256=Yafgax503CHjHvrc_njDTMVOOD2B36sRvWvDE99geMI,15473 -babel/locale-data/rof_TZ.dat,sha256=I_qwGjqqrcwUR_3UNydHOm_EiiLZYyRV0q-s1qfSSi0,617 -babel/locale-data/root.dat,sha256=QDYvctKQfBJlCMUl24Gf71HIZQq14_V1LcTK-6udwzQ,51999 -babel/locale-data/ru.dat,sha256=8WD_7_Tz0ieaEjXNlFQy8xFSfCwsVf_CUcoHyFfqkgs,305832 -babel/locale-data/ru_BY.dat,sha256=99UlyhoMWnfyhI5ltXGaJkycWLVfJ3FwdxyRm-yr6kM,676 -babel/locale-data/ru_KG.dat,sha256=X-iAT1QuQGhk1mIetpGyWqDlwiBskLfwqrK6ant1XS8,659 -babel/locale-data/ru_KZ.dat,sha256=P1_Gn3esOwDLL8sd8vVYbFA8J7QEsaDAWfZvDsn-WwA,656 -babel/locale-data/ru_MD.dat,sha256=LU12oBwr7MfPTzfs8Dx7uUZVgHin8ybX_oPHrPIJTkw,654 -babel/locale-data/ru_RU.dat,sha256=IVG440mXjphW6iCsKysZCW1Gem_z5_O0lZI6-R3oG_E,653 -babel/locale-data/ru_UA.dat,sha256=WYPDEOUjHl7mtejs302KAURq_ckDn6CFjH-IwmiFF6g,1210 -babel/locale-data/rw.dat,sha256=1KiBkw5N7hioc-ESs0oqgdyOaVdOlsNp1iIz7g3k83E,8418 -babel/locale-data/rw_RW.dat,sha256=tw-gtzH64PeIAmNGeobgUypw7RAyKITeFAcQPGY0MG8,616 -babel/locale-data/rwk.dat,sha256=hCqo5glP1WqFJrEGyW6r3t43NZ7bVHAOGFGDkUzeM3Y,15369 -babel/locale-data/rwk_TZ.dat,sha256=s9duai8NRoHwn8YpQrlMd3VDf6jtQxxcUmu9CIIMzC0,617 -babel/locale-data/sa.dat,sha256=8r4iZXIQ3vnGxlSmOtDhwM-T_iaYN4SXuTSdGh-2JMg,15978 -babel/locale-data/sa_IN.dat,sha256=ABNamoPpYRbO7bK6BGm0tK92kd7su7_OP7Di0wW0zZo,658 -babel/locale-data/sah.dat,sha256=Qlb3HEv5JslfLAkUjLJz_XdRdyLhloQ9uf9q2b_O6jM,39310 -babel/locale-data/sah_RU.dat,sha256=jHlseiwMbtN-IsETeG2dsaIn6OB2kqtAogazMnn0f60,654 -babel/locale-data/saq.dat,sha256=grj85np3tZzXQmLdtYQcu76ynQv5Jq__KIyaDECFIj0,15787 -babel/locale-data/saq_KE.dat,sha256=kN94cJJt8AT0yBRDqUPGoJtMZdOb2e-83NNsq8hmQ64,636 -babel/locale-data/sat.dat,sha256=K_IKB3mSsOkYuYDtbGW3lbGjVk4ctB56WWkJJvE0-mI,66131 -babel/locale-data/sat_Deva.dat,sha256=0EHZ6UcxsVOfi399xQbOkzaH1OU4jkZ10uqmmWnK8VI,1943 -babel/locale-data/sat_Deva_IN.dat,sha256=Bzh9Vmzia_8KLAryt9VqCl9ter9tKxng4b8oFYbYK6I,659 -babel/locale-data/sat_Olck.dat,sha256=mszOmf6B29mFbE6qN6Moy6SfBjwsjaCsbk75dJR245Q,905 -babel/locale-data/sat_Olck_IN.dat,sha256=Bzh9Vmzia_8KLAryt9VqCl9ter9tKxng4b8oFYbYK6I,659 -babel/locale-data/sbp.dat,sha256=1qHxon8_TpGGnLHUBzy5cR1mCEnZmw1EuYz3QLgnT-c,15609 -babel/locale-data/sbp_TZ.dat,sha256=chWs0tZniO53SbtBquTHGS6E4v36tgYL2b3enxHcdCU,617 -babel/locale-data/sc.dat,sha256=ItaljMMLxOEM4DEG_yDNVk8H4AmQrq1xCnDE8AfRDDs,189970 -babel/locale-data/sc_IT.dat,sha256=0G0vL9YXZQLkTmnzJuuygM5ty2Tw5liDg_aWHLvg32k,653 -babel/locale-data/scn.dat,sha256=UKMqekdt_t_Qe_ebhg3eaRsTs37ECpWLEiWSCFjMTLI,70373 -babel/locale-data/scn_IT.dat,sha256=dTXZIHDRhbFDCw02T0wL1iaJ2kfY1ZUzfbvUs4_WKLs,654 -babel/locale-data/sd.dat,sha256=VDMIluqNpBChcALvjd2lGll6sRK6srAF7p2CogEgXlE,154300 -babel/locale-data/sd_Arab.dat,sha256=jgkaYGUhPXll1T7CT68ridccZaERHeZdwUzGoAZBUps,879 -babel/locale-data/sd_Arab_PK.dat,sha256=c3AXdatnrTNDHW_SOh8fNNye4CVey81FnkSXwj7MmcI,635 -babel/locale-data/sd_Deva.dat,sha256=ZZathBrGJyoqspPsHF769J84zQq1y62vN8RAjGj_Cms,13957 -babel/locale-data/sd_Deva_IN.dat,sha256=0KQFEKHV6hHidc5hxrxQEvhO-YyvFf5P5z6t9hPenH4,658 -babel/locale-data/sdh.dat,sha256=5cD8u88qanpCtA8kPvHH9AdAi2-HbhznORqis1PYx6M,1032 -babel/locale-data/sdh_IQ.dat,sha256=B_vUMOiGWVcD1HaR4Zk8XxVzcmMFSISe2vRscyA1lYY,679 -babel/locale-data/sdh_IR.dat,sha256=hwMmQnne41X_lyjbyNbQq34Lpkl91bAgoavPZRuozwU,679 -babel/locale-data/se.dat,sha256=JnN5s3b1OrcP7qmurb8Rb9NKc-ICOr_RmdFs2EBKoRA,54830 -babel/locale-data/se_FI.dat,sha256=9_VJ0wzYFPto0YF2Ge0qieFV9_DOVqqYnPjlC5wnAr8,44207 -babel/locale-data/se_NO.dat,sha256=nm5u2VQ3ne0SUFZtFqwsRa8Gg97Xn1OSxjoP2I-i_PU,653 -babel/locale-data/se_SE.dat,sha256=6M9OxtmQAY_p9fZ_6L8zqo6i--bo_X00cvhTDEbX_UU,694 -babel/locale-data/seh.dat,sha256=viFuK-Mg4wPnvTcrvL-iEzNqFc7Ri_VP_r9pC673SNE,15433 -babel/locale-data/seh_MZ.dat,sha256=Sv94DueqrM3R5RjhCdwrKZsnNdL1WpjLBg6QhBnoaX0,636 -babel/locale-data/ses.dat,sha256=aRW4cH7nGNS5S3B3ff7o02GqcIqn1mVbBRGg3NdxXUc,15812 -babel/locale-data/ses_ML.dat,sha256=y14qGwP_r6VxzJhklYIWfYkolu0OHB-6nJ4tvF7J-iU,617 -babel/locale-data/sg.dat,sha256=ERl6d_xd8JRnkG_D857gWi2xONyI-cEmzt-Un_hXbu4,16476 -babel/locale-data/sg_CF.dat,sha256=QCNYqo_5l2Sv9JUFEWF6YLDR3MknhgkyMz1mO5j2Gv8,616 -babel/locale-data/shi.dat,sha256=fuYtv_xjgmJ0-aAQiuWcJXZY3mpeq__hz4lnouEUyWE,21830 -babel/locale-data/shi_Latn.dat,sha256=aQrfwwcvkgPaWFSev9-3WgwQXhhXj1_5myuR-QifnPo,15435 -babel/locale-data/shi_Latn_MA.dat,sha256=X8cwkkvm2LjcClQWzHRxxmiA8P0c37D7P8lf6RO4iNQ,617 -babel/locale-data/shi_Tfng.dat,sha256=VjsR4oSiK9oFfdIdpaVDwNSIBvFbQ3Jvm63K24bRqk0,974 -babel/locale-data/shi_Tfng_MA.dat,sha256=X8cwkkvm2LjcClQWzHRxxmiA8P0c37D7P8lf6RO4iNQ,617 -babel/locale-data/shn.dat,sha256=awBFmgdlHbpKsnUqWZaGAQ-YbTPm9UWkJKMn9IdJ7pM,890 -babel/locale-data/shn_MM.dat,sha256=7VauV3cmVfAJz7kA76hsF3FdwM75CVjvHdjx6lCoH4M,636 -babel/locale-data/shn_TH.dat,sha256=QHPmr3V4GzTbb6QA5SDl2EsVzQBjpbaDP6OHfJZ0S0Y,636 -babel/locale-data/si.dat,sha256=iuq-0rbecyzhX0pRX5UYdgKKzrn09pkLucKursW_Z94,216775 -babel/locale-data/si_LK.dat,sha256=cWCgV9WmnEwIIsBmhsIVzSzbrV8hkkXaV1xH1ultlUw,635 -babel/locale-data/sid.dat,sha256=zyKwuzw51B0E8sWg60qJ3kO4rzqTtcNBRnH5D3Zm1mU,2176 -babel/locale-data/sid_ET.dat,sha256=vtwy9BuaZwb5A3XdIl-3mws1cEAVk4s8oKy7i-GnM9o,636 -babel/locale-data/sk.dat,sha256=u_TnuG4-zvLxkxQupdNvPS-TYi28xeiPxLit28hxwZM,218242 -babel/locale-data/sk_SK.dat,sha256=eMjPq18mQOAXvkJN5WV6lKEAPyt7-eleGme2pHHevgA,653 -babel/locale-data/skr.dat,sha256=Mrx5bET3kh5Gzd8-2J9Rm4_b_wPZTDoHuhcxqdYj5hA,1702 -babel/locale-data/skr_PK.dat,sha256=UJpLMmJJIM5jeI-tXWPBNX4Qd3WS2nTwp3bvJg5D3WY,636 -babel/locale-data/sl.dat,sha256=eEJU8du-WwCkCviFmusUqH8H6sMl2PHG_khSumUB3co,210137 -babel/locale-data/sl_SI.dat,sha256=3Hk2t-MUfAUtqu_Gy0_fTjOhwBUtxdnndQ7Kowg0qek,635 -babel/locale-data/sma.dat,sha256=BaBVbhsEPN9V4JGtMt2TpMdf0oIrL8Maxt2FXDIEEss,944 -babel/locale-data/sma_NO.dat,sha256=OeIJ-EJtvWH6z2dZ1Hv8QdmQC7M7r51t-_C6LD6ZyxE,654 -babel/locale-data/sma_SE.dat,sha256=M_zYllAUd2UaWvccZ6ShB9-Gd8JYzIEln-88Q23syaU,654 -babel/locale-data/smj.dat,sha256=eyHR5AZqbxxlTYhyxJfvTjpz_v6zfh3TrDtcDkCgTig,939 -babel/locale-data/smj_NO.dat,sha256=5fz-YVJEoXswvC-RdFcJWmt-EMGcNXA3c9j_pfmxqCA,654 -babel/locale-data/smj_SE.dat,sha256=7l73FEZcAemOwXSrfPB3X7a0cto3J4oM7AO9i9PMIBU,654 -babel/locale-data/smn.dat,sha256=LGkyaifqt24L0NeExCtzhg8rKEKnkfJNEKtt67B-ViE,40399 -babel/locale-data/smn_FI.dat,sha256=VRY3dbCyxEmDUTep9bQXDRldd5wSg4skqQpztnMXplk,654 -babel/locale-data/sms.dat,sha256=ylATYbrfu8SVEVdQRbC7dTSOKCNSX7O1gjS2wJXA1tU,6521 -babel/locale-data/sms_FI.dat,sha256=b-Mk20lC32xysqU5v6jGjDUYDZaMDvoMwWOZNdT5yuA,654 -babel/locale-data/sn.dat,sha256=FNTHDspBYVPY7Nse1haEc19O0Sf3btsqNWQFs2Zzcgc,16732 -babel/locale-data/sn_ZW.dat,sha256=8HnH2raHHl8PI25abODlThEhjIOGXJb_u7cyQd5YzZw,635 -babel/locale-data/so.dat,sha256=sAFkgMg-qaZYhHT_lyoaggZ8UM_KX1xbS4miB3l5KN8,166227 -babel/locale-data/so_DJ.dat,sha256=VWnaj_MuW4dJnlO84t60ySCvfJL7FXI4nIP9PTL5Amo,656 -babel/locale-data/so_ET.dat,sha256=EvRRqU4yQPMYhkEK8a4MR808fwKHmW7EvPvEJdjltnE,655 -babel/locale-data/so_KE.dat,sha256=OSiRggRZ7CTNmqiGOvW1W2Cw2RqjVESTVOdUfgVgWyU,1208 -babel/locale-data/so_SO.dat,sha256=2IPbOqSxQGB5FukQ-B-MEme9FHYn22ClXmAV4x08pNE,616 -babel/locale-data/sq.dat,sha256=8lTejyAm-94ixpS6Dly7NQSHNsamftqLIdO4OxPWqok,160229 -babel/locale-data/sq_AL.dat,sha256=MN2RvkKGG2PXjCRsMHsvcnwnIOOp9tNSpcInh2uCWaI,635 -babel/locale-data/sq_MK.dat,sha256=L_GaZJ9XuQMs0zw-xZlrefPF36SH0sJqMrdDDbwicZM,1208 -babel/locale-data/sq_XK.dat,sha256=qILZGRwAhuIBJ9xi_T5WTrrZAVouFjQSvybrwgXTsog,1187 -babel/locale-data/sr.dat,sha256=AAE3LN_CLRWzljEERIYedZepxLIDWzENkL4wmu8Ib6c,265725 -babel/locale-data/sr_Cyrl.dat,sha256=Tbo90CyBAWciKeDesPhmXf7sdBQfjWsmyyrKBco_NzU,1990 -babel/locale-data/sr_Cyrl_BA.dat,sha256=sKEcBX9G9OwCmIZEpTphQyW_LM4xoDGe90s_470_AJs,41992 -babel/locale-data/sr_Cyrl_ME.dat,sha256=rDZOf-zykN151tfgGiO1SUJiClzQ6t40gF927bMLn1I,2320 -babel/locale-data/sr_Cyrl_RS.dat,sha256=Tn9u1EvJAZZEIdgXloi326qjpq8b1N087nRQ9R1JbWE,635 -babel/locale-data/sr_Cyrl_XK.dat,sha256=qVevTILYnuaIjRyPy4rDTavVg-VXxKcZ5fVdh5c-aHQ,1574 -babel/locale-data/sr_Latn.dat,sha256=1joKXYRv5UiiT21s_Za6qE-VA8XpFi1HzYnVOjKpxb8,217950 -babel/locale-data/sr_Latn_BA.dat,sha256=of4wpeCglVK_Td6HnHUjHRwuFtCVtWj3kHb3nl4B0D8,32834 -babel/locale-data/sr_Latn_ME.dat,sha256=Mr0GNVLFIMZpBakd4dEUzJUlAN4nyX2BMAqydgtMzWo,2089 -babel/locale-data/sr_Latn_RS.dat,sha256=Tn9u1EvJAZZEIdgXloi326qjpq8b1N087nRQ9R1JbWE,635 -babel/locale-data/sr_Latn_XK.dat,sha256=wIz7XXQrRFYpzMpR_cgSWikPWI0ehSx5a0gRBgt3rFo,1486 -babel/locale-data/ss.dat,sha256=UAB0p3UMDn8KfBY32a7HVWXWpHlPa26Nr_Woaah2c3U,2178 -babel/locale-data/ss_SZ.dat,sha256=X-Va1uVrsPa1ZX5hwhQ-ME6Q_bovUb24XXFS8UQ87lQ,1208 -babel/locale-data/ss_ZA.dat,sha256=xwT3vZ4tmKEvJpVwFMV9U2j2FLF1tC9PEDN6MC6Qbf4,635 -babel/locale-data/ssy.dat,sha256=QyBWLhH_sreFEBHMQ7aiqBsrGvDeQHyYAKioqksa2iU,2999 -babel/locale-data/ssy_ER.dat,sha256=9CRIaVxgOMeMTc0tcV0Yz9yZzq_uD3g3ylZzqVpYzsM,617 -babel/locale-data/st.dat,sha256=b-jiK03L_9F4QqWW8qmRJ3UZntZDI9eMJglRho3MN7c,8016 -babel/locale-data/st_LS.dat,sha256=pSzdNm6-O5V6-14VcgF2iGNYQPFYIXQlY3SqUW3GcjA,1227 -babel/locale-data/st_ZA.dat,sha256=5tdUX7dxXEL2xiS_0IvHvqhKF-gWXFsi4supkP0RDAc,635 -babel/locale-data/su.dat,sha256=G66EuouIQrCxgKGW9JzPfMZ94NAN87P6REyULyyrFGw,11817 -babel/locale-data/su_Latn.dat,sha256=ievzq_m7eD5pZWLnVlH7oMIdeadf8GIMqEuVPJTBjyM,745 -babel/locale-data/su_Latn_ID.dat,sha256=axNaukDpD_-G-_4R7P1u5w4dh-ToRPN_Yo-QpC1cFRU,635 -babel/locale-data/sv.dat,sha256=RTvQ2li9cKHIEbVl7fI8ENVrmcCa1gERk3S9O2bHKmA,198967 -babel/locale-data/sv_AX.dat,sha256=QFGzIe9UCr5gCpgHunvdPiZ-126vP7GYORL9jjip9-E,653 -babel/locale-data/sv_FI.dat,sha256=Pn3EDh-VD0QJFWupEZ6w4TqAa-WC5GZ8oTA4oqLazvc,2526 -babel/locale-data/sv_SE.dat,sha256=c9PG5p2y_4rnxepGD4uOzemBU3EOQhQNO4BtUS1m3rE,653 -babel/locale-data/sw.dat,sha256=w0uX0MB-_bZQWOnbdyDVg1uRVe1j-K-egjRExoZpE8U,145419 -babel/locale-data/sw_CD.dat,sha256=W7Fw8Wqwo3_x4hWlssmWbVTj4hhSQmRv8c-VSnQdlg8,2577 -babel/locale-data/sw_KE.dat,sha256=YMNWSTJ8qxo0d1KvQwAN5ECRaZ9LGc0BDfX4enggVlc,47036 -babel/locale-data/sw_TZ.dat,sha256=I1bDbpEDiR1jkCgJ_W9fWCPuPxMhjQmwG3sIueKb-Rk,616 -babel/locale-data/sw_UG.dat,sha256=kuNdg2N_dLwUKFm7wSheL1wLOdyrukPxLfDw0PN7tT8,660 -babel/locale-data/syr.dat,sha256=vuGk7MOpvWxjzuHSoG8mAJEKGbI70UoRq-DxlvwqT5c,111105 -babel/locale-data/syr_IQ.dat,sha256=Mrw63eJwh0vRe3Lpj0vRf6Q4cUZXJcx9tlWkuAmMrss,679 -babel/locale-data/syr_SY.dat,sha256=l0Yl2UMjm_7kuzvoG2-KqBp5XR88gdjf1ZlpRRNFMuc,679 -babel/locale-data/szl.dat,sha256=8PpGSXa6ES6yxXD2YOBjG16b6BW9jDZo86RKB0MCNn8,88759 -babel/locale-data/szl_PL.dat,sha256=1D5dKaG2l2Haxde8uraWI2J5lzw1dIWGeJtBesua-IM,654 -babel/locale-data/ta.dat,sha256=RRIrxhZ9pdpGShIrQx7ZQBOPKWn15OGkeL8yQOy-tz8,277264 -babel/locale-data/ta_IN.dat,sha256=BOFaDBw7fc7C6xSSFYNfkhCgCUd7ydHprJ6u0DaneSE,658 -babel/locale-data/ta_LK.dat,sha256=Od6ln5oUnWfaAHwt_SDafWv844c7A7_7fTT2OPVTuyw,1208 -babel/locale-data/ta_MY.dat,sha256=PEUmjhs_RUr1R5kH9uHExdnG59Hy8LmbU18vXZYYPtA,1325 -babel/locale-data/ta_SG.dat,sha256=tbuefTSiXPMwpeV085Qp53l5uDHLsMMXmt8TPEG2fWY,1344 -babel/locale-data/te.dat,sha256=oRTQlv5dIZYav2aQWijvc2wm0VwoR1TwWUPL_qhzkuU,261668 -babel/locale-data/te_IN.dat,sha256=LICnXU-D0uhvxVpQ986Ul1JeAQktXaX53sXZSUY3L2g,658 -babel/locale-data/teo.dat,sha256=jJ-266Aw_zTeJjwGj_VASqzm6Y3CcQYkVPXKuV_kzT8,16012 -babel/locale-data/teo_KE.dat,sha256=S_lAg36VkA1w384kzPfXRLmJmGWfmz3ll7MzOQAtcr8,657 -babel/locale-data/teo_UG.dat,sha256=0WlDglms5bCe1AMwCqroaXpOXu9jQroxllMwXWLb8q0,640 -babel/locale-data/tg.dat,sha256=ErMv0Y4SGno9gTSJbhMVKZXiSe_KNSUFaoBeIDaP8tk,117163 -babel/locale-data/tg_TJ.dat,sha256=TCrsEipGp0reAS2mAvwT58tyzBnYJfL5oNHWpEe1ttc,635 -babel/locale-data/th.dat,sha256=lyG_l3_QzfpnjCv8LVaRTvpvfImkVrqcDoUKrnXlIR4,215576 -babel/locale-data/th_TH.dat,sha256=_QhA0WcHmW6Cr_-gU9e6cdpzTMDT19WyHas58fWDUnc,635 -babel/locale-data/ti.dat,sha256=K4kS476YQonyQifjLpS4UkE7hNa2k5aIDBJRIdh2eDg,214273 -babel/locale-data/ti_ER.dat,sha256=m9w0P5TPQ8IqpWKldwdfnw5_1Upv09AjLdO5WUbcRic,962 -babel/locale-data/ti_ET.dat,sha256=q0b9svPg8aj9xmbXr7F6fPHqeTJmclBl5t_hqQk7IuU,635 -babel/locale-data/tig.dat,sha256=cgqxy3wiehP8yceqa5-mrJVT23IP_bjlkeUOEvxspUM,13481 -babel/locale-data/tig_ER.dat,sha256=OoQ0U6yyzmjKwDtzLUpqR09h_ZaGvzXlp7t-HqNxmm4,617 -babel/locale-data/tk.dat,sha256=z5i4kshTasDEolkNpqXnzlZh75EgbAhL0ylT0_0oioo,164932 -babel/locale-data/tk_TM.dat,sha256=lADxEQoz-_ImDBmpCFUqDPuZ0ozVUhZKOQF2ZwyXVlw,635 -babel/locale-data/tn.dat,sha256=LgoZgcUCS4t_RsyQQKiVsJ7Kbs7HoInZAMndRk6DUCM,8596 -babel/locale-data/tn_BW.dat,sha256=jWMFP1tw-o7rslHAlAIgjEXflko8UCDNaSuQ8ccvNHI,654 -babel/locale-data/tn_ZA.dat,sha256=S3dMOsYSpBwkUv0tW9q0kKawQQl6yx77LexAL5wQqDU,635 -babel/locale-data/to.dat,sha256=CBr6gRgu0qo_-kIoOluPwu_ZdH4-SPPJHto_ERcDzHA,145255 -babel/locale-data/to_TO.dat,sha256=jcfJ2Pjof8jGE4q1y5LHSP9rb4IanN58kR7OgG8d5Q0,616 -babel/locale-data/tok.dat,sha256=kuSPgVhnv8Rr55G-epQDVyh8FfeKxJbd2J0hierJdUQ,8870 -babel/locale-data/tok_001.dat,sha256=PPUMevT2TBOGFSq-U-AccYkLHSbkWnBRDvxtpokTCD4,693 -babel/locale-data/tpi.dat,sha256=mqTAJweAOy1SAH2l4mJAKktX-ZeAdvTC8JnBPxXjB0A,4030 -babel/locale-data/tpi_PG.dat,sha256=uZD_c8yCtiYwt2EO2VOYPzhSTqWMQBOJMCuMCXe3Css,617 -babel/locale-data/tr.dat,sha256=ULbjOvmQqcT1Z2_8bBOe7LzwgBUg3LWM2MiLkmb8M6w,150824 -babel/locale-data/tr_CY.dat,sha256=3EmCqxcPvFuZcHCzpTmHb6pjtPwqQvwCfn27B8rtXNw,1227 -babel/locale-data/tr_TR.dat,sha256=7lS5CMZw76SVlMJxsmIJW5IfL7k9t8MrfKcnElqISSI,635 -babel/locale-data/trv.dat,sha256=iEbFtCCE_U5rmjAv0Pe5J_UigixMKq1cPIThdZui1Lg,9404 -babel/locale-data/trv_TW.dat,sha256=CTQtGdDW4Z0n2Ckb6B63HgEwNE44YTFnJCetwd8q9GU,636 -babel/locale-data/trw.dat,sha256=1e1LGCtVq79RclEMJbgRE4ATdqflV_KDHxBwInSfAKc,110107 -babel/locale-data/trw_PK.dat,sha256=pBHdOulrwRLiQuC96g8ilUzpKBeG6kglBqUXB8MlURA,636 -babel/locale-data/ts.dat,sha256=p7EFPucK9HkTVXeNKTU00s-Oas5U_HCPGX9hqT15p9I,5522 -babel/locale-data/ts_ZA.dat,sha256=AByEO55W3DVVOBIBxIp1F2QwY01-5v6v8sDFOGSxCF0,635 -babel/locale-data/tt.dat,sha256=X-J0BKEHy01aoARwwb0SWDuNm1lZFrT0akKrzBZjHPM,111014 -babel/locale-data/tt_RU.dat,sha256=cmv2MzolKOrsRoqQcFZGcXQj_33dLWGh9oUjnebCnz0,653 -babel/locale-data/twq.dat,sha256=nOSN5UZAxcCgdTxQFh14Cyk5K38olCyAuT6MrVW5PeE,15509 -babel/locale-data/twq_NE.dat,sha256=y8xQDxMtyStWC83tm_JiekFxAY0Fj-CWWdmic9GBwd4,617 -babel/locale-data/tyv.dat,sha256=y90_CH7Yksd1Kuc6KhykhpkdGVLUgFDV-B4oSxGqn5E,693 -babel/locale-data/tyv_RU.dat,sha256=vP_6cjMLB8I8YL7Nqrq4Gu_AWGFuANbZSdBc7ORn_IA,654 -babel/locale-data/tzm.dat,sha256=wYFO2vX38bht1nAdcVA7dxKVMh-3yC5sItggFaXkKBo,15436 -babel/locale-data/tzm_MA.dat,sha256=8eku_7voAqAjN651VYKCuIWvdYGOScCyqbt1rtSVO_g,617 -babel/locale-data/ug.dat,sha256=jRJJxRRKuYljj6kYicIhxWk8ySzpMT9AOePaBk3EO94,117801 -babel/locale-data/ug_CN.dat,sha256=2UselU1_d6K7NgQj0EDnDfb4Llk51eQZ7z78oYE-Ywg,635 -babel/locale-data/uk.dat,sha256=b8iEhvLdQxq7rtlqpx1c9vp6-n3jNGPGSuBWNhZAnFA,339125 -babel/locale-data/uk_UA.dat,sha256=usJw_J4Uu9OD9nrUxNQAx6vMJBWDjbGuc9l7ssJAyuw,635 -babel/locale-data/ur.dat,sha256=RTL85URWDMA3albZQODUCbVTA9oxeYXo5kWtuL1XW6g,167002 -babel/locale-data/ur_IN.dat,sha256=9GV9YIa7Mftq14Po2C6fjMG5sWkU8J5LS5bq1iIcsHg,10550 -babel/locale-data/ur_PK.dat,sha256=EEAGl6fLd1-a7X0n2PwRxlqwYZ0a0ELm2DuLII9spX0,635 -babel/locale-data/uz.dat,sha256=iwj3ukxb4FK0rZYtC66ftQmTtr1SgTzy12Wu3KkQYQo,138052 -babel/locale-data/uz_Arab.dat,sha256=wt8BwimSNmoFzrnduGDXMHhqj3B8-XqqN6ghg2Xm74A,3814 -babel/locale-data/uz_Arab_AF.dat,sha256=68eUCR8KeB0QDQRCvNMiOIkZVRozGKFTDednrQL1X_Y,678 -babel/locale-data/uz_Cyrl.dat,sha256=RpmyW2YhTwN_o6sKyfJxcktFI6VMqkO6YDzDE8XoOTE,76581 -babel/locale-data/uz_Cyrl_UZ.dat,sha256=MWJtHPdj_pF2h6abRP4wUwDWAUF4-zr7jBn2RQcZ0z8,635 -babel/locale-data/uz_Latn.dat,sha256=L5H7Xfz2Ho-vMqviRPC4ai0MnqoF9CIebiq7muISvSA,1292 -babel/locale-data/uz_Latn_UZ.dat,sha256=MWJtHPdj_pF2h6abRP4wUwDWAUF4-zr7jBn2RQcZ0z8,635 -babel/locale-data/vai.dat,sha256=T_MWlDAfbp9qgeArFd8yPDPbyThU15kFu19QTQqAlCE,17416 -babel/locale-data/vai_Latn.dat,sha256=MecJYJumVm7wCyCo-YeAY4eAon5WZ4ZqR0shQ3Pdpu8,14255 -babel/locale-data/vai_Latn_LR.dat,sha256=LdVmBAL7nzDp-Ab_uHidBAi2crIf9nXhf8tBNfa7xdI,617 -babel/locale-data/vai_Vaii.dat,sha256=l_IXC76zzJ2lPLfWfjWzSw6xVtFwr-x8LofcHQsSG_0,693 -babel/locale-data/vai_Vaii_LR.dat,sha256=LdVmBAL7nzDp-Ab_uHidBAi2crIf9nXhf8tBNfa7xdI,617 -babel/locale-data/ve.dat,sha256=5bInQioXkePSDbBerQqf4sYPTiB9-zpKuDokCXX5eNA,2372 -babel/locale-data/ve_ZA.dat,sha256=uJndHi9RMKtDWd58f_Q3g5WgXjL3W7KLQj91rVueabQ,635 -babel/locale-data/vec.dat,sha256=zokLfsgTK7cSP4KaSV8EMpPlHiOkI6tG3rWM7o7vcAM,160413 -babel/locale-data/vec_IT.dat,sha256=mC9jRfk9ndmkpRIpWZNCwdMwvxvB6QX6_9HanawdaUw,654 -babel/locale-data/vi.dat,sha256=F95a-fvLPIeJqOlwx4gr9TnWZ0VpSPyzNwJ0RPc1qE8,134138 -babel/locale-data/vi_VN.dat,sha256=cGwN0vw-qIaeA2g89U97N1xHSMmmaIVNPCLL2P_QJMk,635 -babel/locale-data/vmw.dat,sha256=mhERiGM-732X9KO0CThGEyjFtSbquT1_rmzYPnru2SM,1796 -babel/locale-data/vmw_MZ.dat,sha256=sz0j5RufN9oAzTalcjWNhKtCoEXzeRr5Cv6qCouK45E,636 -babel/locale-data/vo.dat,sha256=werwY25uwomUcf9q1dyQ8Rwgg2aCoLp78XUzBeSw0LY,4609 -babel/locale-data/vo_001.dat,sha256=iF2xY9XIdxC0mOYEp9hjYZp2EG-si_YaNYXri_60-4w,850 -babel/locale-data/vun.dat,sha256=VGNPlnoRipnocGYPgeb39d9MmE6fe5nTMWtCk4m1AEY,15373 -babel/locale-data/vun_TZ.dat,sha256=YKrOeOS1VnU3BWQ7HWtxjS-gMKJcKAPQLf4HTgkzf70,617 -babel/locale-data/wa.dat,sha256=1r6krApI8oecIKQt_9RzbpwSN0LcWSjlG34Y9Rfh07Y,880 -babel/locale-data/wa_BE.dat,sha256=j4YADQloo07Oek1j3aOrLYkzcFkrViS1UdRrA0Z56-o,653 -babel/locale-data/wae.dat,sha256=qBKStFqpkAruVDgeZJvX6U0X-hq_NkETTqgR2e7A4UQ,29641 -babel/locale-data/wae_CH.dat,sha256=Z0Wzu5Q9IVTnLGwWu54ozVx-fgnFz0IE3yrky8Y1IkQ,654 -babel/locale-data/wal.dat,sha256=AD9ZfdNNjEhX2n9T0ykd_XwEbXK-wxmsLqq39O4BjWU,8406 -babel/locale-data/wal_ET.dat,sha256=tNjeuU4GPTWtDmsroGmi4VUql0dA57v1tEtDF4rslVg,636 -babel/locale-data/wbp.dat,sha256=kzXK04f538n-PrJl6nRKIaYHMid6rJ4ACmzPcCz7NgU,746 -babel/locale-data/wbp_AU.dat,sha256=BT2E1-WiomuRrteCzN1_AfLyDUdEOm0ehT5miAiepBQ,636 -babel/locale-data/wo.dat,sha256=iB86SoTMrnphiHy_om1Yxs-J8L5YFWYlbmiQjK6jc2k,61849 -babel/locale-data/wo_SN.dat,sha256=fvlwe-ImWs04fsxSjTsXf8IPxEIiEi45BX8xhoBC_yg,616 -babel/locale-data/xh.dat,sha256=xRy095MO_brgAUoDB3UKwhgDvOkBlhJ3cWj9dM2fvHI,64334 -babel/locale-data/xh_ZA.dat,sha256=9p6wHW1sxOghJMAgsP6BC3RdUhuvLpHoCkqvjsCmPps,635 -babel/locale-data/xnr.dat,sha256=Ot10zi4F4H5Yqx6kIJGPay65Rm-Gam2eJ7kAOgoJoJ0,142585 -babel/locale-data/xnr_IN.dat,sha256=UbNwudCNihEFVZu3ymanDhJ52YoupG0SDGwmDByPhzQ,659 -babel/locale-data/xog.dat,sha256=V4U36ibPCWDoUMf31-o9mDCwylsYU0nZEx5pt-MWecM,15866 -babel/locale-data/xog_UG.dat,sha256=bMX0Tk5zS4P2LqlOLLpVP4nZxBFhS_qc-ndXjilDCNI,640 -babel/locale-data/yav.dat,sha256=UupgFFmPVcV-IgY3Fd8Y9GoOcOWgY1P22ZHuA-4QSMU,14543 -babel/locale-data/yav_CM.dat,sha256=CsaKwep5CARetyBX-JXHFvUVsFe1KTN4Pyz1Wh7NTHw,636 -babel/locale-data/yi.dat,sha256=OWr2zb3k_toEmkSXcPweqE13Tr5RhvlMm4z358uCQiY,24264 -babel/locale-data/yi_UA.dat,sha256=DesjWHTuxEBrjOeq8c-B82j4Hnwu0ElR2IH3fZPmq-A,635 -babel/locale-data/yo.dat,sha256=iWLg-08Fk9QJLZPuokgbCeXkyfyQNaov51TT3i1pKgY,110427 -babel/locale-data/yo_BJ.dat,sha256=CuxWXwNyq_dWEw-rEwu3-4HAmZfZGwPkljOt0ttEWRc,50090 -babel/locale-data/yo_NG.dat,sha256=BEIndIiMT1Mo18S5DAWpjefthptCVQkIPeVs0umSop0,616 -babel/locale-data/yrl.dat,sha256=2nwDuw5Q_dL4H2Oxl5ClzZU2Uw10j-koP5RgfnJ67y0,186854 -babel/locale-data/yrl_BR.dat,sha256=aUgLCNwgEORk2wtuez8AqkYahB8_mX41IWSqogt39YE,636 -babel/locale-data/yrl_CO.dat,sha256=pT0rllDKHZAUIK_itrkWP7irmih4im131Ume_wKzyks,9211 -babel/locale-data/yrl_VE.dat,sha256=_Tqu_pGUkzrd4M9SKXpn4g93z7Q5xlUMAtGvROS5x6M,9211 -babel/locale-data/yue.dat,sha256=lvTaoHXSVZHqcb5UCR8IeDiVRCE9IRsrDkhv0bGwzCg,143872 -babel/locale-data/yue_Hans.dat,sha256=ybTM8LU-H-QunhqnLmCQ9bE6zdsYMJPHdlh_hBv3AWk,145194 -babel/locale-data/yue_Hans_CN.dat,sha256=MOZnu5r0xokf672rwAXfgzG3VEeTfNlDbu36fR1bB6I,636 -babel/locale-data/yue_Hant.dat,sha256=76wefuhsGIjk0jT05KSTEU_pbb7AfNE2u56jdRoJaDM,1306 -babel/locale-data/yue_Hant_CN.dat,sha256=l0nSr9oItZCMdphhuVjzHML7FpqAKZorutkLijFAwm4,1196 -babel/locale-data/yue_Hant_HK.dat,sha256=eMZ7mOTEyKzQz0MDIjvP_Z57CS9nFIP4-Uf0Jpx1CY4,636 -babel/locale-data/yue_Hant_MO.dat,sha256=d6oknkpchm3cIK_Ylk-PjHAaPKhtyg8cGFVBdqtIJBg,636 -babel/locale-data/za.dat,sha256=5EbAmE-qqvt_e4geaFxBGJi81CVoZFK4dCcJ346P4yU,12705 -babel/locale-data/za_CN.dat,sha256=WzhnaughSM1C1twuKYBVrelLEer-guzT43a9yBNXCsg,635 -babel/locale-data/zgh.dat,sha256=Q2D9KQFgDfWk4fXOdEbPcLq3yX_zxjhlK_PVI61Ok4U,22018 -babel/locale-data/zgh_MA.dat,sha256=sV8aPBmHY23lKIzsRna-tPz3UiXoKCMQ1Ur2tCyx2YA,617 -babel/locale-data/zh.dat,sha256=SpSkmdVN5PX7CCdso1B-SyQZKMDTyZfZeT2l_kj2FoI,152100 -babel/locale-data/zh_Hans.dat,sha256=-wR64b65stjA2KWj4GWcBWiF3yI0A24p4K7wApj8qtc,1305 -babel/locale-data/zh_Hans_CN.dat,sha256=zz2qPpWEP9ba12T9cGsPXGI7Bg5LcmZ3NkAMiVTf61A,635 -babel/locale-data/zh_Hans_HK.dat,sha256=yPyZyU-xXSNyU1FYJIYpWwnh0d3uXOra9V9aj2j_YQU,3621 -babel/locale-data/zh_Hans_MO.dat,sha256=08kUXwHOXA0pRA8qIX7e4p0Nzl1XHSBjwhMh7f-nZBE,3752 -babel/locale-data/zh_Hans_MY.dat,sha256=iEFxl2XEm1zBzd1K0wXpiZFvU7l0UrNURh2p9DZdRLU,1300 -babel/locale-data/zh_Hans_SG.dat,sha256=OHZNiUfnteSXuzel-lZAMVgv58iiVTNhjq81T9Qu94s,3948 -babel/locale-data/zh_Hant.dat,sha256=31aRGt18bl6MdiI9iAozwltdkSQQ370TvzI8gid8CBI,154802 -babel/locale-data/zh_Hant_HK.dat,sha256=NXPCE_4pYgLatoeKLdQ0d8PQ-Mk_GeMF1s1JYX9m27M,49152 -babel/locale-data/zh_Hant_MO.dat,sha256=qCWPPEr87U1aemaIuf6nCYQ-Q3w_1HcXrYoncYVBd3g,657 -babel/locale-data/zh_Hant_MY.dat,sha256=3n4syQpQ9sXVe82sOEH7yQIdrU3dAtMhp7fEhzGN8_E,1203 -babel/locale-data/zh_Hant_TW.dat,sha256=5N4K8I3XG89knCaSoy97HGzTVOTiUcd_VmfvNJlA4Bg,635 -babel/locale-data/zh_Latn.dat,sha256=-wR64b65stjA2KWj4GWcBWiF3yI0A24p4K7wApj8qtc,1305 -babel/locale-data/zh_Latn_CN.dat,sha256=zz2qPpWEP9ba12T9cGsPXGI7Bg5LcmZ3NkAMiVTf61A,635 -babel/locale-data/zu.dat,sha256=e48g3oI1FjkROV3yHZ2GAwn_8wr2y3dMkj98NHW4X8Q,138626 -babel/locale-data/zu_ZA.dat,sha256=jHloBfkNbQETXgE-3xVxvESitWb994_-SoY0G4_JL5E,635 -babel/localedata.py,sha256=94b-NvjmQuidIpptiz2NM-d_3615AxMnV8rQcaWalp0,9133 -babel/localtime/__init__.py,sha256=EuxZJNvscBH6gM6XKF3pBLWaeRTK3GdNB12WxG3jhaA,1019 -babel/localtime/__pycache__/__init__.cpython-312.pyc,, -babel/localtime/__pycache__/_fallback.cpython-312.pyc,, -babel/localtime/__pycache__/_helpers.cpython-312.pyc,, -babel/localtime/__pycache__/_unix.cpython-312.pyc,, -babel/localtime/__pycache__/_win32.cpython-312.pyc,, -babel/localtime/_fallback.py,sha256=9h1CFwVX3laiA2oaZgAywRqEr0ghI4PuTCBKkFgbamI,1199 -babel/localtime/_helpers.py,sha256=ZmLc8m46W-3GtsstLBdrw6BU3ZhSob8vKHkxgcZG9uw,1704 -babel/localtime/_unix.py,sha256=22Xkddk98UiDP1LkDDjvxTL9J_5UXIFyDbxj_gD-gv8,3903 -babel/localtime/_win32.py,sha256=cjImlpejBFxzeP-BqTN2OOevboB06kZAlT_GMqQPqAc,3198 -babel/messages/__init__.py,sha256=Km-kd7AFHT231GjF16TlHdlWn7s3zQh1xA9SbKrxkWQ,329 -babel/messages/__pycache__/__init__.cpython-312.pyc,, -babel/messages/__pycache__/_compat.cpython-312.pyc,, -babel/messages/__pycache__/catalog.cpython-312.pyc,, -babel/messages/__pycache__/checkers.cpython-312.pyc,, -babel/messages/__pycache__/extract.cpython-312.pyc,, -babel/messages/__pycache__/frontend.cpython-312.pyc,, -babel/messages/__pycache__/jslexer.cpython-312.pyc,, -babel/messages/__pycache__/mofile.cpython-312.pyc,, -babel/messages/__pycache__/plurals.cpython-312.pyc,, -babel/messages/__pycache__/pofile.cpython-312.pyc,, -babel/messages/__pycache__/setuptools_frontend.cpython-312.pyc,, -babel/messages/_compat.py,sha256=DNyCNMwH5vXgl_pq7cUYz3VcRC2gIPqeQLO5nN5neWE,1163 -babel/messages/catalog.py,sha256=7mtA4SWOcJAK_wJvONRKt1AgOKmTVq7PTI1tP73-Jk8,38647 -babel/messages/checkers.py,sha256=KX50Rceb6rYuCndDYeDijvRe2w1jg4l7RQd7NpaXRY0,6209 -babel/messages/extract.py,sha256=Ss2wc-uYicxaZ1szXE2SPpWRYx4BxD6wsR-L0VFPd-M,36875 -babel/messages/frontend.py,sha256=hwbxnZkfeFRriKKAC8NHbb6rHSQ4xOVcE9pbP0Qce3M,47518 -babel/messages/jslexer.py,sha256=_RRI0T-MDby3sBykA4fEM28qDSBwqdjfsFR2HW6GlsA,7262 -babel/messages/mofile.py,sha256=GgCmyPLbbChLVMsC6Wk3fiEunnru4u8N0gEvoUxYDr0,6907 -babel/messages/plurals.py,sha256=Azqltuu3uRrDh0TM-nckgaHlsYLC9-_qcDmWvcRCOwI,7489 -babel/messages/pofile.py,sha256=aNynbXhhulJizuJQUYKvjbV7ktwWqHLINCzK3MdxZYE,25437 -babel/messages/setuptools_frontend.py,sha256=m1l9NHuawj1pSncZeC82cUJfdwxib_C7JSUb_2EhbUM,3485 -babel/numbers.py,sha256=meEj4-Rp5B_WXd-lmHGunWwrw8PL_1oyc5IOPePnHiI,63469 -babel/plural.py,sha256=zU3sxaLb-jYTcimy-QOZSnDuLv0it-BxcT5H9E49mM0,23186 -babel/py.typed,sha256=DtCsIDq6KOv2NOEdQjTbeMWJKRh6ZEL2E-6Mf1RLeMA,59 -babel/support.py,sha256=oYq9nmEbvqpSwTLO69hOZ1TGb51okrEoyFN6M8NklfM,27906 -babel/units.py,sha256=0WsfuBXU150pPwbznrnt6Mc6vyrij2d9yTJj9MXnbGw,13982 -babel/util.py,sha256=-Zch-KLaAZgpC4jGGV8twKH8R8pCwd731ozEkUUI1qg,9395 diff --git a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/WHEEL deleted file mode 100644 index 0885d055..00000000 --- a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (80.10.2) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/entry_points.txt deleted file mode 100644 index 95235a55..00000000 --- a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/entry_points.txt +++ /dev/null @@ -1,20 +0,0 @@ -[babel.checkers] -num_plurals = babel.messages.checkers:num_plurals -python_format = babel.messages.checkers:python_format - -[babel.extractors] -ignore = babel.messages.extract:extract_nothing -javascript = babel.messages.extract:extract_javascript -python = babel.messages.extract:extract_python - -[console_scripts] -pybabel = babel.messages.frontend:main - -[distutils.commands] -compile_catalog = babel.messages.setuptools_frontend:compile_catalog -extract_messages = babel.messages.setuptools_frontend:extract_messages -init_catalog = babel.messages.setuptools_frontend:init_catalog -update_catalog = babel.messages.setuptools_frontend:update_catalog - -[distutils.setup_keywords] -message_extractors = babel.messages.setuptools_frontend:check_message_extractors diff --git a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/licenses/LICENSE deleted file mode 100644 index 96f467d2..00000000 --- a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2013-2026 by the Babel Team, see AUTHORS for more information. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/top_level.txt deleted file mode 100644 index 98f65931..00000000 --- a/.venv/lib/python3.12/site-packages/babel-2.18.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -babel diff --git a/.venv/lib/python3.12/site-packages/babel/__init__.py b/.venv/lib/python3.12/site-packages/babel/__init__.py deleted file mode 100644 index 2fd88bef..00000000 --- a/.venv/lib/python3.12/site-packages/babel/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -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-2026 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.18.0' - -__all__ = [ - 'Locale', - 'UnknownLocaleError', - '__version__', - 'default_locale', - 'get_locale_identifier', - 'negotiate_locale', - 'parse_locale', -] diff --git a/.venv/lib/python3.12/site-packages/babel/core.py b/.venv/lib/python3.12/site-packages/babel/core.py deleted file mode 100644 index 4210b46b..00000000 --- a/.venv/lib/python3.12/site-packages/babel/core.py +++ /dev/null @@ -1,1384 +0,0 @@ -""" -babel.core -~~~~~~~~~~ - -Core locale representation and locale data access. - -:copyright: (c) 2013-2026 by the Babel Team. -:license: BSD, see LICENSE for more details. -""" - -from __future__ import annotations - -import os -import pickle -from collections.abc import Iterable, Mapping -from typing import TYPE_CHECKING, Any, Literal - -from babel import localedata -from babel.plural import PluralRule - -__all__ = [ - 'Locale', - 'UnknownLocaleError', - 'default_locale', - 'get_cldr_version', - 'get_global', - 'get_locale_identifier', - 'negotiate_locale', - 'parse_locale', -] - -if TYPE_CHECKING: - from typing_extensions import TypeAlias - - _GLOBAL_KEY: TypeAlias = Literal[ - "all_currencies", - "cldr", - "currency_fractions", - "language_aliases", - "likely_subtags", - "meta_zones", - "parent_exceptions", - "script_aliases", - "territory_aliases", - "territory_currencies", - "territory_languages", - "territory_zones", - "variant_aliases", - "windows_zone_mapping", - "zone_aliases", - "zone_territories", - ] - - _global_data: Mapping[_GLOBAL_KEY, Mapping[str, Any]] | None - -_global_data = None -_default_plural_rule = PluralRule({}) - - -def _raise_no_data_error(): - raise RuntimeError( - 'The babel data files are not available. ' - 'This usually happens because you are using ' - 'a source checkout from Babel and you did ' - 'not build the data files. Just make sure ' - 'to run "python setup.py import_cldr" before ' - 'installing the library.', - ) - - -def get_global(key: _GLOBAL_KEY) -> Mapping[str, Any]: - """Return the dictionary for the given key in the global data. - - The global data is stored in the ``babel/global.dat`` file and contains - information independent of individual locales. - - >>> get_global('zone_aliases')['UTC'] - 'Etc/UTC' - >>> get_global('zone_territories')['Europe/Berlin'] - 'DE' - - The keys available are: - - - ``all_currencies`` - - ``cldr`` (metadata) - - ``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', -} # fmt: skip - - -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 - 'English (United States)' - - A `Locale` object can also be instantiated from a raw locale string: - - >>> locale = Locale.parse('en-US', sep='-') - >>> repr(locale) - "Locale('en', territory='US')" - - `Locale` objects provide access to a collection of locale data, such as - territory and language names, number and date format patterns, and more: - - >>> locale.number_symbols['latn']['decimal'] - '.' - - If a locale is requested for which no locale data is available, an - `UnknownLocaleError` is raised: - - >>> Locale.parse('en_XX') - Traceback (most recent call last): - ... - UnknownLocaleError: unknown locale 'en_XX' - - For more information see :rfc:`3066`. - """ - - def __init__( - self, - language: str, - territory: str | None = None, - script: str | None = None, - variant: str | None = None, - modifier: str | None = None, - ) -> None: - """Initialize the locale object from the given identifier components. - - >>> locale = Locale('en', 'US') - >>> locale.language - 'en' - >>> locale.territory - 'US' - - :param language: the language code - :param territory: the territory (country or region) code - :param script: the script code - :param variant: the variant code - :param modifier: a modifier (following the '@' symbol, sometimes called '@variant') - :raise `UnknownLocaleError`: if no locale data is available for the - requested locale - """ - #: the language code - self.language = language - #: the territory (country or region) code - self.territory = territory - #: the script code - self.script = script - #: the variant code - self.variant = variant - #: the modifier - self.modifier = modifier - self.__data: localedata.LocaleDataDict | None = None - - identifier = str(self) - identifier_without_modifier = identifier.partition('@')[0] - if localedata.exists(identifier): - self.__data_identifier = identifier - elif localedata.exists(identifier_without_modifier): - self.__data_identifier = identifier_without_modifier - else: - raise UnknownLocaleError(identifier) - - @classmethod - def default( - cls, - category: str | None = None, - aliases: Mapping[str, str] = LOCALE_ALIASES, - ) -> Locale: - """Return the system default locale for the specified category. - - >>> for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LC_MESSAGES']: - ... os.environ[name] = '' - >>> os.environ['LANG'] = 'fr_FR.UTF-8' - >>> Locale.default('LC_MESSAGES') - Locale('fr', territory='FR') - - The following fallbacks to the variable are always considered: - - - ``LANGUAGE`` - - ``LC_ALL`` - - ``LC_CTYPE`` - - ``LANG`` - - :param category: one of the ``LC_XXX`` environment variable names - :param aliases: a dictionary of aliases for locale identifiers - """ - # XXX: use likely subtag expansion here instead of the - # aliases dictionary. - locale_string = default_locale(category, aliases=aliases) - return cls.parse(locale_string) - - @classmethod - def negotiate( - cls, - preferred: Iterable[str], - available: Iterable[str], - sep: str = '_', - aliases: Mapping[str, str] = LOCALE_ALIASES, - ) -> Locale | None: - """Find the best match between available and requested locale strings. - - >>> Locale.negotiate(['de_DE', 'en_US'], ['de_DE', 'de_AT']) - Locale('de', territory='DE') - >>> Locale.negotiate(['de_DE', 'en_US'], ['en', 'de']) - Locale('de') - >>> Locale.negotiate(['de_DE', 'de'], ['en_US']) - - You can specify the character used in the locale identifiers to separate - the different components. This separator is applied to both lists. Also, - case is ignored in the comparison: - - >>> Locale.negotiate(['de-DE', 'de'], ['en-us', 'de-de'], sep='-') - Locale('de', territory='DE') - - :param preferred: the list of locale identifiers preferred by the user - :param available: the list of locale identifiers available - :param aliases: a dictionary of aliases for locale identifiers - :param sep: separator for parsing; e.g. Windows tends to use '-' instead of '_'. - """ - identifier = negotiate_locale(preferred, available, sep=sep, aliases=aliases) - if identifier: - return Locale.parse(identifier, sep=sep) - return None - - @classmethod - def parse( - cls, - identifier: Locale | str | None, - sep: str = '_', - resolve_likely_subtags: bool = True, - ) -> Locale: - """Create a `Locale` instance for the given locale identifier. - - >>> l = Locale.parse('de-DE', sep='-') - >>> l.display_name - 'Deutsch (Deutschland)' - - If the `identifier` parameter is not a string, but actually a `Locale` - object, that object is returned: - - >>> Locale.parse(l) - Locale('de', territory='DE') - - If the `identifier` parameter is neither of these, such as `None` - or an empty string, e.g. because a default locale identifier - could not be determined, a `TypeError` is raised: - - >>> Locale.parse(None) - Traceback (most recent call last): - ... - TypeError: ... - - This also can perform resolving of likely subtags which it does - by default. This is for instance useful to figure out the most - likely locale for a territory you can use ``'und'`` as the - language tag: - - >>> Locale.parse('und_AT') - Locale('de', territory='AT') - - Modifiers are optional, and always at the end, separated by "@": - - >>> Locale.parse('de_AT@euro') - Locale('de', territory='AT', modifier='euro') - - :param identifier: the locale identifier string - :param sep: optional component separator - :param resolve_likely_subtags: if this is specified then a locale will - have its likely subtag resolved if the - locale otherwise does not exist. For - instance ``zh_TW`` by itself is not a - locale that exists but Babel can - automatically expand it to the full - form of ``zh_hant_TW``. Note that this - expansion is only taking place if no - locale exists otherwise. For instance - there is a locale ``en`` that can exist - by itself. - :raise `ValueError`: if the string does not appear to be a valid locale - identifier - :raise `UnknownLocaleError`: if no locale data is available for the - requested locale - :raise `TypeError`: if the identifier is not a string or a `Locale` - :raise `ValueError`: if the identifier is not a valid string - """ - if isinstance(identifier, Locale): - return identifier - - if not identifier: - msg = ( - f"Empty locale identifier value: {identifier!r}\n\n" - f"If you didn't explicitly pass an empty value to a Babel function, " - f"this could be caused by there being no suitable locale environment " - f"variables for the API you tried to use." - ) - if isinstance(identifier, str): - # `parse_locale` would raise a ValueError, so let's do that here - raise ValueError(msg) - raise TypeError(msg) - - if not isinstance(identifier, str): - raise TypeError(f"Unexpected value for identifier: {identifier!r}") - - parts = parse_locale(identifier, sep=sep) - input_id = get_locale_identifier(parts) - - def _try_load(parts): - try: - return cls(*parts) - except UnknownLocaleError: - return None - - def _try_load_reducing(parts): - # Success on first hit, return it. - locale = _try_load(parts) - if locale is not None: - return locale - - # Now try without script and variant - locale = _try_load(parts[:2]) - if locale is not None: - return locale - - locale = _try_load(parts) - if locale is not None: - return locale - if not resolve_likely_subtags: - raise UnknownLocaleError(input_id) - - # From here onwards is some very bad likely subtag resolving. This - # whole logic is not entirely correct but good enough (tm) for the - # time being. This has been added so that zh_TW does not cause - # errors for people when they upgrade. Later we should properly - # implement ICU like fuzzy locale objects and provide a way to - # maximize and minimize locale tags. - - if len(parts) == 5: - language, territory, script, variant, modifier = parts - else: - language, territory, script, variant = parts - modifier = None - language = get_global('language_aliases').get(language, language) - territory = get_global('territory_aliases').get(territory or '', (territory,))[0] - script = get_global('script_aliases').get(script or '', script) - variant = get_global('variant_aliases').get(variant or '', variant) - - if territory == 'ZZ': - territory = None - if script == 'Zzzz': - script = None - - parts = language, territory, script, variant, modifier - - # First match: try the whole identifier - new_id = get_locale_identifier(parts) - likely_subtag = get_global('likely_subtags').get(new_id) - if likely_subtag is not None: - locale = _try_load_reducing(parse_locale(likely_subtag)) - if locale is not None: - return locale - - # If we did not find anything so far, try again with a - # simplified identifier that is just the language - likely_subtag = get_global('likely_subtags').get(language) - if likely_subtag is not None: - parts2 = parse_locale(likely_subtag) - if len(parts2) == 5: - language2, _, script2, variant2, modifier2 = parts2 - else: - language2, _, script2, variant2 = parts2 - modifier2 = None - locale = _try_load_reducing( - (language2, territory, script2, variant2, modifier2), - ) - if locale is not None: - return locale - - raise UnknownLocaleError(input_id) - - def __eq__(self, other: object) -> bool: - for key in ('language', 'territory', 'script', 'variant', 'modifier'): - if not hasattr(other, key): - return False - return ( - self.language == getattr(other, 'language') # noqa: B009 - and self.territory == getattr(other, 'territory') # noqa: B009 - and self.script == getattr(other, 'script') # noqa: B009 - and self.variant == getattr(other, 'variant') # noqa: B009 - and self.modifier == getattr(other, 'modifier') # noqa: B009 - ) - - def __ne__(self, other: object) -> bool: - return not self.__eq__(other) - - def __hash__(self) -> int: - return hash((self.language, self.territory, self.script, self.variant, self.modifier)) - - def __repr__(self) -> str: - parameters = [''] - for key in ('territory', 'script', 'variant', 'modifier'): - value = getattr(self, key) - if value is not None: - parameters.append(f"{key}={value!r}") - return f"Locale({self.language!r}{', '.join(parameters)})" - - def __str__(self) -> str: - return get_locale_identifier( - (self.language, self.territory, self.script, self.variant, self.modifier), - ) - - @property - def _data(self) -> localedata.LocaleDataDict: - if self.__data is None: - self.__data = localedata.LocaleDataDict(localedata.load(self.__data_identifier)) - return self.__data - - def get_display_name(self, locale: Locale | str | None = None) -> str | None: - """Return the display name of the locale using the given locale. - - The display name will include the language, territory, script, and - variant, if those are specified. - - >>> Locale('zh', 'CN', script='Hans').get_display_name('en') - 'Chinese (Simplified, China)' - - Modifiers are currently passed through verbatim: - - >>> Locale('it', 'IT', modifier='euro').get_display_name('en') - 'Italian (Italy, euro)' - - :param locale: the locale to use - """ - if locale is None: - locale = self - locale = Locale.parse(locale) - retval = locale.languages.get(self.language) - if retval and (self.territory or self.script or self.variant): - details = [] - if self.script: - details.append(locale.scripts.get(self.script)) - if self.territory: - details.append(locale.territories.get(self.territory)) - if self.variant: - details.append(locale.variants.get(self.variant)) - if self.modifier: - details.append(self.modifier) - detail_string = ', '.join(atom for atom in details if atom) - if detail_string: - retval += f" ({detail_string})" - return retval - - display_name = property( - get_display_name, - doc="""\ - The localized display name of the locale. - - >>> Locale('en').display_name - 'English' - >>> Locale('en', 'US').display_name - 'English (United States)' - >>> Locale('sv').display_name - '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') - '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 - '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 - '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 - 'latinica' - """, - ) - - @property - def english_name(self) -> str | None: - """The english display name of the locale. - - >>> Locale('de').english_name - 'German' - >>> Locale('de', 'DE').english_name - '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'] - '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'] - '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'] - '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'] - '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'] - 'Colombian Peso' - >>> Locale('de', 'DE').currencies['COP'] - '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'] - '$' - >>> Locale('es', 'CO').currency_symbols['USD'] - 'US$' - """ - return self._data['currency_symbols'] - - @property - def number_symbols(self) -> localedata.LocaleDataDict: - """Symbols used in number formatting by number system. - - .. note:: The format of the value returned may change between - Babel versions. - - >>> Locale('fr', 'FR').number_symbols["latn"]['decimal'] - ',' - >>> Locale('fa', 'IR').number_symbols["arabext"]['decimal'] - '٫' - >>> Locale('fa', 'IR').number_symbols["latn"]['decimal'] - '.' - """ - return self._data['number_symbols'] - - @property - def other_numbering_systems(self) -> localedata.LocaleDataDict: - """ - Mapping of other numbering systems available for the locale. - See: https://www.unicode.org/reports/tr35/tr35-numbers.html#otherNumberingSystems - - >>> Locale('el', 'GR').other_numbering_systems['traditional'] - 'grek' - - .. note:: The format of the value returned may change between - Babel versions. - """ - return self._data['numbering_systems'] - - @property - def default_numbering_system(self) -> str: - """The default numbering system used by the locale. - >>> Locale('el', 'GR').default_numbering_system - 'latn' - """ - return self._data['default_numbering_system'] - - @property - def decimal_formats(self) -> localedata.LocaleDataDict: - """Locale patterns for decimal number formatting. - - .. note:: The format of the value returned may change between - Babel versions. - - >>> Locale('en', 'US').decimal_formats[None] - - """ - return self._data['decimal_formats'] - - @property - def compact_decimal_formats(self) -> localedata.LocaleDataDict: - """Locale patterns for compact decimal number formatting. - - .. note:: The format of the value returned may change between - Babel versions. - - >>> Locale('en', 'US').compact_decimal_formats["short"]["one"]["1000"] - - """ - return self._data['compact_decimal_formats'] - - @property - def currency_formats(self) -> localedata.LocaleDataDict: - """Locale patterns for currency number formatting. - - .. note:: The format of the value returned may change between - Babel versions. - - >>> Locale('en', 'US').currency_formats['standard'] - - >>> Locale('en', 'US').currency_formats['accounting'] - - """ - return self._data['currency_formats'] - - @property - def compact_currency_formats(self) -> localedata.LocaleDataDict: - """Locale patterns for compact currency number formatting. - - .. note:: The format of the value returned may change between - Babel versions. - - >>> Locale('en', 'US').compact_currency_formats["short"]["one"]["1000"] - - """ - return self._data['compact_currency_formats'] - - @property - def percent_formats(self) -> localedata.LocaleDataDict: - """Locale patterns for percent number formatting. - - .. note:: The format of the value returned may change between - Babel versions. - - >>> Locale('en', 'US').percent_formats[None] - - """ - return self._data['percent_formats'] - - @property - def scientific_formats(self) -> localedata.LocaleDataDict: - """Locale patterns for scientific number formatting. - - .. note:: The format of the value returned may change between - Babel versions. - - >>> Locale('en', 'US').scientific_formats[None] - - """ - return self._data['scientific_formats'] - - # { Calendar Information and Date Formatting - - @property - def periods(self) -> localedata.LocaleDataDict: - """Locale display names for day periods (AM/PM). - - >>> Locale('en', 'US').periods['am'] - '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] - 'Donnerstag' - """ - return self._data['days'] - - @property - def months(self) -> localedata.LocaleDataDict: - """Locale display names for months. - - >>> Locale('de', 'DE').months['format']['wide'][10] - 'Oktober' - """ - return self._data['months'] - - @property - def quarters(self) -> localedata.LocaleDataDict: - """Locale display names for quarters. - - >>> Locale('de', 'DE').quarters['format']['wide'][1] - '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] - 'Anno Domini' - >>> Locale('en', 'US').eras['abbreviated'][0] - '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'] - 'British Summer Time' - >>> Locale('en', 'US').time_zones['America/St_Johns']['city'] - 'St. John’s' - """ - 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'] - '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'] - '%(1)s (%(0)s)' - >>> Locale('pt', 'BR').zone_formats['region'] - 'Horário %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'] - '{1}, {0}' - >>> Locale('th').datetime_formats['medium'] - '{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'] - ['E d.\\u2009–\\u2009', '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'] - '{0}, {1}' - >>> Locale('en').list_patterns['standard']['end'] - '{0}, and {1}' - >>> Locale('en_GB').list_patterns['standard']['end'] - '{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'] - 'américain' - >>> Locale('en', 'US').measurement_systems['US'] - 'US' - - """ - return self._data['measurement_systems'] - - @property - def character_order(self) -> str: - """The text direction for the language. - - >>> Locale('de', 'DE').character_order - 'left-to-right' - >>> Locale('ar', 'SA').character_order - 'right-to-left' - """ - return self._data['character_order'] - - @property - def text_direction(self) -> str: - """The text direction for the language in CSS short-hand form. - - >>> Locale('de', 'DE').text_direction - 'ltr' - >>> Locale('ar', 'SA').text_direction - 'rtl' - """ - return ''.join(word[0] for word in self.character_order.split('-')) - - @property - def unit_display_names(self) -> localedata.LocaleDataDict: - """Display names for units of measurement. - - .. seealso:: - - You may want to use :py:func:`babel.units.get_unit_name` instead. - - .. note:: The format of the value returned may change between - Babel versions. - - """ - return self._data['unit_display_names'] - - -def default_locale( - category: str | tuple[str, ...] | list[str] | None = None, - aliases: Mapping[str, str] = LOCALE_ALIASES, -) -> str | None: - """Returns the system default locale for a given category, based on - environment variables. - - >>> for name in ['LANGUAGE', 'LC_ALL', 'LC_CTYPE']: - ... os.environ[name] = '' - >>> os.environ['LANG'] = 'fr_FR.UTF-8' - >>> default_locale('LC_MESSAGES') - 'fr_FR' - - The "C" or "POSIX" pseudo-locales are treated as aliases for the - "en_US_POSIX" locale: - - >>> os.environ['LC_MESSAGES'] = 'POSIX' - >>> default_locale('LC_MESSAGES') - 'en_US_POSIX' - - The following fallbacks to the variable are always considered: - - - ``LANGUAGE`` - - ``LC_ALL`` - - ``LC_CTYPE`` - - ``LANG`` - - :param category: one or more of the ``LC_XXX`` environment variable names - :param aliases: a dictionary of aliases for locale identifiers - """ - - varnames = ('LANGUAGE', 'LC_ALL', 'LC_CTYPE', 'LANG') - if category: - if isinstance(category, str): - varnames = (category, *varnames) - elif isinstance(category, (list, tuple)): - varnames = (*category, *varnames) - else: - raise TypeError(f"Invalid type for category: {category!r}") - - for name in varnames: - if not name: - continue - locale = os.getenv(name) - if locale: - if name == 'LANGUAGE' and ':' in locale: - # the LANGUAGE variable may contain a colon-separated list of - # language codes; we just pick the language on the list - locale = locale.split(':')[0] - if locale.split('.')[0] in ('C', 'POSIX'): - locale = 'en_US_POSIX' - elif aliases and locale in aliases: - locale = aliases[locale] - try: - return get_locale_identifier(parse_locale(locale)) - except ValueError: - pass - return None - - -def negotiate_locale( - preferred: Iterable[str], - available: Iterable[str], - sep: str = '_', - aliases: Mapping[str, str] = LOCALE_ALIASES, -) -> str | None: - """Find the best match between available and requested locale strings. - - >>> negotiate_locale(['de_DE', 'en_US'], ['de_DE', 'de_AT']) - 'de_DE' - >>> negotiate_locale(['de_DE', 'en_US'], ['en', 'de']) - 'de' - - Case is ignored by the algorithm, the result uses the case of the preferred - locale identifier: - - >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) - 'de_DE' - - >>> negotiate_locale(['de_DE', 'en_US'], ['de_de', 'de_at']) - 'de_DE' - - By default, some web browsers unfortunately do not include the territory - in the locale identifier for many locales, and some don't even allow the - user to easily add the territory. So while you may prefer using qualified - locale identifiers in your web-application, they would not normally match - the language-only locale sent by such browsers. To workaround that, this - function uses a default mapping of commonly used language-only locale - identifiers to identifiers including the territory: - - >>> negotiate_locale(['ja', 'en_US'], ['ja_JP', 'en_US']) - 'ja_JP' - - Some browsers even use an incorrect or outdated language code, such as "no" - for Norwegian, where the correct locale identifier would actually be "nb_NO" - (Bokmål) or "nn_NO" (Nynorsk). The aliases are intended to take care of - such cases, too: - - >>> negotiate_locale(['no', 'sv'], ['nb_NO', 'sv_SE']) - 'nb_NO' - - You can override this default mapping by passing a different `aliases` - dictionary to this function, or you can bypass the behavior althogher by - setting the `aliases` parameter to `None`. - - :param preferred: the list of locale strings preferred by the user - :param available: the list of locale strings available - :param sep: character that separates the different parts of the locale - strings - :param aliases: a dictionary of aliases for locale identifiers - """ - available = [a.lower() for a in available if a] - for locale in preferred: - ll = locale.lower() - if ll in available: - return locale - if aliases: - alias = aliases.get(ll) - if alias: - alias = alias.replace('_', sep) - if alias.lower() in available: - return alias - parts = locale.split(sep) - if len(parts) > 1 and parts[0].lower() in available: - return parts[0] - return None - - -def parse_locale( - identifier: str, - sep: str = '_', -) -> ( - tuple[str, str | None, str | None, str | None] - | tuple[str, str | None, str | None, str | None, str | None] -): - """Parse a locale identifier into a tuple of the form ``(language, - territory, script, variant, modifier)``. - - >>> parse_locale('zh_CN') - ('zh', 'CN', None, None) - >>> parse_locale('zh_Hans_CN') - ('zh', 'CN', 'Hans', None) - >>> parse_locale('ca_es_valencia') - ('ca', 'ES', None, 'VALENCIA') - >>> parse_locale('en_150') - ('en', '150', None, None) - >>> parse_locale('en_us_posix') - ('en', 'US', None, 'POSIX') - >>> parse_locale('it_IT@euro') - ('it', 'IT', None, None, 'euro') - >>> parse_locale('it_IT@custom') - ('it', 'IT', None, None, 'custom') - >>> parse_locale('it_IT@') - ('it', 'IT', None, None) - - The default component separator is "_", but a different separator can be - specified using the `sep` parameter. - - The optional modifier is always separated with "@" and at the end: - - >>> parse_locale('zh-CN', sep='-') - ('zh', 'CN', None, None) - >>> parse_locale('zh-CN@custom', sep='-') - ('zh', 'CN', None, None, 'custom') - - If the identifier cannot be parsed into a locale, a `ValueError` exception - is raised: - - >>> parse_locale('not_a_LOCALE_String') - Traceback (most recent call last): - ... - ValueError: 'not_a_LOCALE_String' is not a valid locale identifier - - Encoding information is removed from the identifier, while modifiers are - kept: - - >>> parse_locale('en_US.UTF-8') - ('en', 'US', None, None) - >>> parse_locale('de_DE.iso885915@euro') - ('de', 'DE', None, None, 'euro') - - See :rfc:`4646` for more information. - - :param identifier: the locale identifier string - :param sep: character that separates the different components of the locale - identifier - :raise `ValueError`: if the string does not appear to be a valid locale - identifier - """ - if not identifier: - raise ValueError("empty locale identifier") - identifier, _, modifier = identifier.partition('@') - if '.' in identifier: - # this is probably the charset/encoding, which we don't care about - identifier = identifier.split('.', 1)[0] - - parts = identifier.split(sep) - lang = parts.pop(0).lower() - if not lang.isalpha(): - raise ValueError(f"expected only letters, got {lang!r}") - - script = territory = variant = None - if parts and len(parts[0]) == 4 and parts[0].isalpha(): - script = parts.pop(0).title() - - if parts: - if len(parts[0]) == 2 and parts[0].isalpha(): - territory = parts.pop(0).upper() - elif len(parts[0]) == 3 and parts[0].isdigit(): - territory = parts.pop(0) - - if parts and ( - len(parts[0]) == 4 - and parts[0][0].isdigit() - or len(parts[0]) >= 5 - and parts[0][0].isalpha() - ): - variant = parts.pop().upper() - - if parts: - raise ValueError(f"{identifier!r} is not a valid locale identifier") - - # TODO(3.0): always return a 5-tuple - if modifier: - return lang, territory, script, variant, modifier - else: - return lang, territory, script, variant - - -def get_locale_identifier( - tup: tuple[str] - | tuple[str, str | None] - | tuple[str, str | None, str | None] - | tuple[str, str | None, str | None, str | None] - | tuple[str, str | None, str | None, str | None, str | None], - sep: str = "_", -) -> str: - """The reverse of :func:`parse_locale`. It creates a locale identifier out - of a ``(language, territory, script, variant, modifier)`` tuple. Items can be set to - ``None`` and trailing ``None``\\s can also be left out of the tuple. - - >>> get_locale_identifier(('de', 'DE', None, '1999', 'custom')) - 'de_DE_1999@custom' - >>> get_locale_identifier(('fi', None, None, None, 'custom')) - 'fi@custom' - - - .. versionadded:: 1.0 - - :param tup: the tuple as returned by :func:`parse_locale`. - :param sep: the separator for the identifier. - """ - tup = tuple(tup[:5]) # type: ignore # length should be no more than 5 - lang, territory, script, variant, modifier = tup + (None,) * (5 - len(tup)) - ret = sep.join(filter(None, (lang, script, territory, variant))) - return f'{ret}@{modifier}' if modifier else ret - - -def get_cldr_version() -> str: - """Return the Unicode CLDR version used by this Babel installation. - - Generally, you should be able to assume that the return value of this - function is a string representing a version number, e.g. '47'. - - >>> get_cldr_version() - '47' - - .. versionadded:: 2.18 - - :rtype: str - """ - return str(get_global("cldr")["version"]) diff --git a/.venv/lib/python3.12/site-packages/babel/dates.py b/.venv/lib/python3.12/site-packages/babel/dates.py deleted file mode 100644 index 69610a7f..00000000 --- a/.venv/lib/python3.12/site-packages/babel/dates.py +++ /dev/null @@ -1,2040 +0,0 @@ -""" -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-2026 by the Babel Team. -:license: BSD, see LICENSE for more details. -""" - -from __future__ import annotations - -import math -import re -import warnings -from functools import lru_cache -from typing import TYPE_CHECKING, Literal, SupportsInt - -try: - import pytz -except ModuleNotFoundError: - pytz = None - import zoneinfo - -import datetime -from collections.abc import Iterable - -from babel import localtime -from babel.core import Locale, default_locale, get_global -from babel.localedata import LocaleDataDict - -if TYPE_CHECKING: - from typing_extensions import TypeAlias - - _Instant: TypeAlias = datetime.date | datetime.time | float | None - _PredefinedTimeFormat: TypeAlias = Literal['full', 'long', 'medium', 'short'] - _Context: TypeAlias = Literal['format', 'stand-alone'] - _DtOrTzinfo: TypeAlias = datetime.datetime | datetime.tzinfo | str | int | datetime.time | None # fmt: skip - -# "If a given short metazone form is known NOT to be understood in a given -# locale and the parent locale has this value such that it would normally -# be inherited, the inheritance of this value can be explicitly disabled by -# use of the 'no inheritance marker' as the value, which is 3 simultaneous [sic] -# empty set characters ( U+2205 )." -# - https://www.unicode.org/reports/tr35/tr35-dates.html#Metazone_Names - -NO_INHERITANCE_MARKER = '\u2205\u2205\u2205' - -UTC = datetime.timezone.utc -LOCALTZ = localtime.LOCALTZ - -LC_TIME = default_locale('LC_TIME') - - -def _localize(tz: datetime.tzinfo, dt: datetime.datetime) -> datetime.datetime: - # Support localizing with both pytz and zoneinfo tzinfos - # nothing to do - if dt.tzinfo is tz: - return dt - - if hasattr(tz, 'localize'): # pytz - return tz.localize(dt) - - if dt.tzinfo is None: - # convert naive to localized - return dt.replace(tzinfo=tz) - - # convert timezones - return dt.astimezone(tz) - - -def _get_dt_and_tzinfo( - dt_or_tzinfo: _DtOrTzinfo, -) -> tuple[datetime.datetime | None, datetime.tzinfo]: - """ - Parse a `dt_or_tzinfo` value into a datetime and a tzinfo. - - See the docs for this function's callers for semantics. - - :rtype: tuple[datetime, tzinfo] - """ - if dt_or_tzinfo is None: - dt = datetime.datetime.now() - tzinfo = LOCALTZ - elif isinstance(dt_or_tzinfo, str): - dt = None - tzinfo = get_timezone(dt_or_tzinfo) - elif isinstance(dt_or_tzinfo, int): - dt = None - tzinfo = UTC - elif isinstance(dt_or_tzinfo, (datetime.datetime, datetime.time)): - dt = _get_datetime(dt_or_tzinfo) - tzinfo = dt.tzinfo if dt.tzinfo is not None else UTC - else: - dt = None - tzinfo = dt_or_tzinfo - return dt, tzinfo - - -def _get_tz_name(dt_or_tzinfo: _DtOrTzinfo) -> str: - """ - Get the timezone name out of a time, datetime, or tzinfo object. - - :rtype: str - """ - dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) - if hasattr(tzinfo, 'zone'): # pytz object - return tzinfo.zone - elif hasattr(tzinfo, 'key') and tzinfo.key is not None: # ZoneInfo object - return tzinfo.key - else: - return tzinfo.tzname(dt or datetime.datetime.now(UTC)) - - -def _get_datetime(instant: _Instant) -> datetime.datetime: - """ - Get a datetime out of an "instant" (date, time, datetime, number). - - .. warning:: The return values of this function may depend on the system clock. - - If the instant is None, the current moment is used. - If the instant is a time, it's augmented with today's date. - - Dates are converted to naive datetimes with midnight as the time component. - - >>> from datetime import date, datetime - >>> _get_datetime(date(2015, 1, 1)) - datetime.datetime(2015, 1, 1, 0, 0) - - UNIX timestamps are converted to datetimes. - - >>> _get_datetime(1400000000) - datetime.datetime(2014, 5, 13, 16, 53, 20) - - Other values are passed through as-is. - - >>> x = datetime(2015, 1, 1) - >>> _get_datetime(x) is x - True - - :param instant: date, time, datetime, integer, float or None - :type instant: date|time|datetime|int|float|None - :return: a datetime - :rtype: datetime - """ - if instant is None: - return datetime.datetime.now(UTC).replace(tzinfo=None) - elif isinstance(instant, (int, float)): - return datetime.datetime.fromtimestamp(instant, UTC).replace(tzinfo=None) - elif isinstance(instant, datetime.time): - return datetime.datetime.combine(datetime.date.today(), instant) - elif isinstance(instant, datetime.date) and not isinstance(instant, datetime.datetime): # fmt: skip - return datetime.datetime.combine(instant, datetime.time()) - # TODO (3.x): Add an assertion/type check for this fallthrough branch: - return instant - - -def _ensure_datetime_tzinfo( - dt: datetime.datetime, - tzinfo: datetime.tzinfo | None = None, -) -> datetime.datetime: - """ - Ensure the datetime passed has an attached tzinfo. - - If the datetime is tz-naive to begin with, UTC is attached. - - If a tzinfo is passed in, the datetime is normalized to that timezone. - - >>> from datetime import datetime - >>> _get_tz_name(_ensure_datetime_tzinfo(datetime(2015, 1, 1))) - 'UTC' - - >>> tz = get_timezone("Europe/Stockholm") - >>> _ensure_datetime_tzinfo(datetime(2015, 1, 1, 13, 15, tzinfo=UTC), tzinfo=tz).hour - 14 - - :param datetime: Datetime to augment. - :param tzinfo: optional tzinfo - :return: datetime with tzinfo - :rtype: datetime - """ - if dt.tzinfo is None: - dt = dt.replace(tzinfo=UTC) - if tzinfo is not None: - dt = dt.astimezone(get_timezone(tzinfo)) - if hasattr(tzinfo, 'normalize'): # pytz - dt = tzinfo.normalize(dt) - return dt - - -def _get_time( - time: datetime.time | datetime.datetime | None, - tzinfo: datetime.tzinfo | None = None, -) -> datetime.time: - """ - Get a timezoned time from a given instant. - - .. warning:: The return values of this function may depend on the system clock. - - :param time: time, datetime or None - :rtype: time - """ - if time is None: - time = datetime.datetime.now(UTC) - elif isinstance(time, (int, float)): - time = datetime.datetime.fromtimestamp(time, UTC) - - if time.tzinfo is None: - time = time.replace(tzinfo=UTC) - - if isinstance(time, datetime.datetime): - if tzinfo is not None: - time = time.astimezone(tzinfo) - if hasattr(tzinfo, 'normalize'): # pytz - time = tzinfo.normalize(time) - time = time.timetz() - elif tzinfo is not None: - time = time.replace(tzinfo=tzinfo) - return time - - -def get_timezone(zone: str | datetime.tzinfo | None = None) -> datetime.tzinfo: - """Looks up a timezone by name and returns it. The timezone object - returned comes from ``pytz`` or ``zoneinfo``, whichever is available. - It corresponds to the `tzinfo` interface and can be used with all of - the functions of Babel that operate with dates. - - If a timezone is not known a :exc:`LookupError` is raised. If `zone` - is ``None`` a local zone object is returned. - - :param zone: the name of the timezone to look up. If a timezone object - itself is passed in, it's returned unchanged. - """ - if zone is None: - return LOCALTZ - if not isinstance(zone, str): - return zone - - if pytz: - try: - return pytz.timezone(zone) - except pytz.UnknownTimeZoneError as e: - exc = e - else: - assert zoneinfo - try: - return zoneinfo.ZoneInfo(zone) - except zoneinfo.ZoneInfoNotFoundError as e: - exc = e - - raise LookupError(f"Unknown timezone {zone}") from exc - - -def get_period_names( - width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', - context: _Context = 'stand-alone', - locale: Locale | str | None = None, -) -> LocaleDataDict: - """Return the names for day periods (AM/PM) used by the locale. - - >>> get_period_names(locale='en_US')['am'] - 'AM' - - :param width: the width to use, one of "abbreviated", "narrow", or "wide" - :param context: the context, either "format" or "stand-alone" - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - """ - return Locale.parse(locale or LC_TIME).day_periods[context][width] - - -def get_day_names( - width: Literal['abbreviated', 'narrow', 'short', 'wide'] = 'wide', - context: _Context = 'format', - locale: Locale | str | None = None, -) -> LocaleDataDict: - """Return the day names used by the locale for the specified format. - - >>> get_day_names('wide', locale='en_US')[1] - 'Tuesday' - >>> get_day_names('short', locale='en_US')[1] - 'Tu' - >>> get_day_names('abbreviated', locale='es')[1] - 'mar' - >>> get_day_names('narrow', context='stand-alone', locale='de_DE')[1] - 'D' - - :param width: the width to use, one of "wide", "abbreviated", "short" or "narrow" - :param context: the context, either "format" or "stand-alone" - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - """ - return Locale.parse(locale or LC_TIME).days[context][width] - - -def get_month_names( - width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', - context: _Context = 'format', - locale: Locale | str | None = None, -) -> LocaleDataDict: - """Return the month names used by the locale for the specified format. - - >>> get_month_names('wide', locale='en_US')[1] - 'January' - >>> get_month_names('abbreviated', locale='es')[1] - 'ene' - >>> get_month_names('narrow', context='stand-alone', locale='de_DE')[1] - 'J' - - :param width: the width to use, one of "wide", "abbreviated", or "narrow" - :param context: the context, either "format" or "stand-alone" - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - """ - return Locale.parse(locale or LC_TIME).months[context][width] - - -def get_quarter_names( - width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', - context: _Context = 'format', - locale: Locale | str | None = None, -) -> LocaleDataDict: - """Return the quarter names used by the locale for the specified format. - - >>> get_quarter_names('wide', locale='en_US')[1] - '1st quarter' - >>> get_quarter_names('abbreviated', locale='de_DE')[1] - 'Q1' - >>> get_quarter_names('narrow', locale='de_DE')[1] - '1' - - :param width: the width to use, one of "wide", "abbreviated", or "narrow" - :param context: the context, either "format" or "stand-alone" - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - """ - return Locale.parse(locale or LC_TIME).quarters[context][width] - - -def get_era_names( - width: Literal['abbreviated', 'narrow', 'wide'] = 'wide', - locale: Locale | str | None = None, -) -> LocaleDataDict: - """Return the era names used by the locale for the specified format. - - >>> get_era_names('wide', locale='en_US')[1] - 'Anno Domini' - >>> get_era_names('abbreviated', locale='de_DE')[1] - 'n. Chr.' - - :param width: the width to use, either "wide", "abbreviated", or "narrow" - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - """ - return Locale.parse(locale or LC_TIME).eras[width] - - -def get_date_format( - format: _PredefinedTimeFormat = 'medium', - locale: Locale | str | None = None, -) -> DateTimePattern: - """Return the date formatting patterns used by the locale for the specified - format. - - >>> get_date_format(locale='en_US') - - >>> get_date_format('full', locale='de_DE') - - - :param format: the format to use, one of "full", "long", "medium", or - "short" - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - """ - return Locale.parse(locale or LC_TIME).date_formats[format] - - -def get_datetime_format( - format: _PredefinedTimeFormat = 'medium', - locale: Locale | str | None = None, -) -> DateTimePattern: - """Return the datetime formatting patterns used by the locale for the - specified format. - - >>> get_datetime_format(locale='en_US') - '{1}, {0}' - - :param format: the format to use, one of "full", "long", "medium", or - "short" - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - """ - patterns = Locale.parse(locale or LC_TIME).datetime_formats - if format not in patterns: - format = None - return patterns[format] - - -def get_time_format( - format: _PredefinedTimeFormat = 'medium', - locale: Locale | str | None = None, -) -> DateTimePattern: - """Return the time formatting patterns used by the locale for the specified - format. - - >>> get_time_format(locale='en_US') - - >>> get_time_format('full', locale='de_DE') - - - :param format: the format to use, one of "full", "long", "medium", or - "short" - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - """ - return Locale.parse(locale or LC_TIME).time_formats[format] - - -def get_timezone_gmt( - datetime: _Instant = None, - width: Literal['long', 'short', 'iso8601', 'iso8601_short'] = 'long', - locale: Locale | str | None = None, - return_z: bool = False, -) -> str: - """Return the timezone associated with the given `datetime` object formatted - as string indicating the offset from GMT. - - >>> from datetime import datetime - >>> dt = datetime(2007, 4, 1, 15, 30) - >>> get_timezone_gmt(dt, locale='en') - 'GMT+00:00' - >>> get_timezone_gmt(dt, locale='en', return_z=True) - 'Z' - >>> get_timezone_gmt(dt, locale='en', width='iso8601_short') - '+00' - >>> tz = get_timezone('America/Los_Angeles') - >>> dt = _localize(tz, datetime(2007, 4, 1, 15, 30)) - >>> get_timezone_gmt(dt, locale='en') - 'GMT-07:00' - >>> get_timezone_gmt(dt, 'short', locale='en') - '-0700' - >>> get_timezone_gmt(dt, locale='en', width='iso8601_short') - '-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') - 'UTC-07:00' - - .. versionadded:: 0.9 - - :param datetime: the ``datetime`` object; if `None`, the current date and - time in UTC is used - :param width: either "long" or "short" or "iso8601" or "iso8601_short" - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - :param return_z: True or False; Function returns indicator "Z" - when local time offset is 0 - """ - datetime = _ensure_datetime_tzinfo(_get_datetime(datetime)) - locale = Locale.parse(locale or LC_TIME) - - offset = datetime.tzinfo.utcoffset(datetime) - seconds = offset.days * 24 * 60 * 60 + offset.seconds - hours, seconds = divmod(seconds, 3600) - if return_z and hours == 0 and seconds == 0: - return 'Z' - elif seconds == 0 and width == 'iso8601_short': - return '%+03d' % hours - elif width == 'short' or width == 'iso8601_short': - pattern = '%+03d%02d' - elif width == 'iso8601': - pattern = '%+03d:%02d' - else: - pattern = locale.zone_formats['gmt'] % '%+03d:%02d' - return pattern % (hours, seconds // 60) - - -def get_timezone_location( - dt_or_tzinfo: _DtOrTzinfo = None, - locale: Locale | str | None = None, - return_city: bool = False, -) -> str: - """Return a representation of the given timezone using "location format". - - The result depends on both the local display name of the country and the - city associated with the time zone: - - >>> tz = get_timezone('America/St_Johns') - >>> print(get_timezone_location(tz, locale='de_DE')) - Kanada (St. John’s) (Ortszeit) - >>> print(get_timezone_location(tz, locale='en')) - Canada (St. John’s) Time - >>> print(get_timezone_location(tz, locale='en', return_city=True)) - St. John’s - >>> tz = get_timezone('America/Mexico_City') - >>> get_timezone_location(tz, locale='de_DE') - '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') - 'Mitteleuropäische Zeit' - - .. versionadded:: 0.9 - - :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines - the timezone; if `None`, the current date and time in - UTC is assumed - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - :param return_city: True or False, if True then return exemplar city (location) - for the time zone - :return: the localized timezone name using location format - - """ - locale = Locale.parse(locale or LC_TIME) - - zone = _get_tz_name(dt_or_tzinfo) - - # Get the canonical time-zone code - zone = get_global('zone_aliases').get(zone, zone) - - info = locale.time_zones.get(zone, {}) - - # Otherwise, if there is only one timezone for the country, return the - # localized country name - region_format = locale.zone_formats['region'] - territory = get_global('zone_territories').get(zone) - if territory not in locale.territories: - territory = 'ZZ' # invalid/unknown - territory_name = locale.territories[territory] - if ( - not return_city - and territory - and len(get_global('territory_zones').get(territory, [])) == 1 - ): - return region_format % territory_name - - # Otherwise, include the city in the output - fallback_format = locale.zone_formats['fallback'] - if 'city' in info: - city_name = info['city'] - else: - metazone = get_global('meta_zones').get(zone) - metazone_info = locale.meta_zones.get(metazone, {}) - if 'city' in metazone_info: - city_name = metazone_info['city'] - elif '/' in zone: - city_name = zone.split('/', 1)[1].replace('_', ' ') - else: - city_name = zone.replace('_', ' ') - - if return_city: - return city_name - return region_format % ( - fallback_format - % { - '0': city_name, - '1': territory_name, - } - ) - - -def get_timezone_name( - dt_or_tzinfo: _DtOrTzinfo = None, - width: Literal['long', 'short'] = 'long', - uncommon: bool = False, - locale: Locale | str | None = None, - zone_variant: Literal['generic', 'daylight', 'standard'] | None = None, - return_zone: bool = False, -) -> str: - r"""Return the localized display name for the given timezone. The timezone - may be specified using a ``datetime`` or `tzinfo` object. - - >>> from datetime import time - >>> dt = time(15, 30, tzinfo=get_timezone('America/Los_Angeles')) - >>> get_timezone_name(dt, locale='en_US') # doctest: +SKIP - 'Pacific Standard Time' - >>> get_timezone_name(dt, locale='en_US', return_zone=True) - 'America/Los_Angeles' - >>> get_timezone_name(dt, width='short', locale='en_US') # doctest: +SKIP - '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') - 'Pacific Time' - >>> get_timezone_name(tz, 'short', locale='en_US') - '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') - 'Mitteleuropäische Zeit' - >>> get_timezone_name(tz, locale='pt_BR') - 'Horário 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') - 'Neufundland-Zeit' - - Note that short format is currently not supported for all timezones and - all locales. This is partially because not every timezone has a short - code in every locale. In that case it currently falls back to the long - format. - - For more information see `LDML Appendix J: Time Zone Display Names - `_ - - .. versionadded:: 0.9 - - .. versionchanged:: 1.0 - Added `zone_variant` support. - - :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines - the timezone; if a ``tzinfo`` object is used, the - resulting display name will be generic, i.e. - independent of daylight savings time; if `None`, the - current date in UTC is assumed - :param width: either "long" or "short" - :param uncommon: deprecated and ignored - :param zone_variant: defines the zone variation to return. By default the - variation is defined from the datetime object - passed in. If no datetime object is passed in, the - ``'generic'`` variation is assumed. The following - values are valid: ``'generic'``, ``'daylight'`` and - ``'standard'``. - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - :param return_zone: True or False. If true then function - returns long time zone ID - """ - dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo) - locale = Locale.parse(locale or LC_TIME) - - zone = _get_tz_name(dt_or_tzinfo) - - if zone_variant is None: - if dt is None: - zone_variant = 'generic' - else: - dst = tzinfo.dst(dt) - zone_variant = "daylight" if dst else "standard" - else: - if zone_variant not in ('generic', 'standard', 'daylight'): - raise ValueError('Invalid zone variation') - - # Get the canonical time-zone code - zone = get_global('zone_aliases').get(zone, zone) - if return_zone: - return zone - info = locale.time_zones.get(zone, {}) - # Try explicitly translated zone names first - if width in info and zone_variant in info[width]: - value = info[width][zone_variant] - if value != NO_INHERITANCE_MARKER: - return value - - 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 and name != NO_INHERITANCE_MARKER: - return name - - # If we have a concrete datetime, we assume that the result can't be - # independent of daylight savings time, so we return the GMT offset - if dt is not None: - return get_timezone_gmt(dt, width=width, locale=locale) - - return get_timezone_location(dt_or_tzinfo, locale=locale) - - -def format_date( - date: datetime.date | None = None, - format: _PredefinedTimeFormat | str = 'medium', - locale: Locale | str | None = None, -) -> str: - """Return a date formatted according to the given pattern. - - >>> from datetime import date - >>> d = date(2007, 4, 1) - >>> format_date(d, locale='en_US') - 'Apr 1, 2007' - >>> format_date(d, format='full', locale='de_DE') - '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') - "Sun, Apr 1, '07" - - :param date: the ``date`` or ``datetime`` object; if `None`, the current - date is used - :param format: one of "full", "long", "medium", or "short", or a custom - date/time pattern - :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. - """ - if date is None: - date = datetime.date.today() - elif isinstance(date, datetime.datetime): - date = date.date() - - locale = Locale.parse(locale or LC_TIME) - if format in ('full', 'long', 'medium', 'short'): - format = get_date_format(format, locale=locale) - pattern = parse_pattern(format) - return pattern.apply(date, locale) - - -def format_datetime( - datetime: _Instant = None, - format: _PredefinedTimeFormat | str = 'medium', - tzinfo: datetime.tzinfo | None = None, - locale: Locale | str | None = None, -) -> str: - r"""Return a date formatted according to the given pattern. - - >>> from datetime import datetime - >>> dt = datetime(2007, 4, 1, 15, 30) - >>> format_datetime(dt, locale='en_US') - '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') - '2007.04.01 AD at 11:30:00 EDT' - - :param datetime: the `datetime` object; if `None`, the current date and - time is used - :param format: one of "full", "long", "medium", or "short", or a custom - date/time pattern - :param tzinfo: the timezone to apply to the time for display - :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. - """ - datetime = _ensure_datetime_tzinfo(_get_datetime(datetime), tzinfo) - - locale = Locale.parse(locale or LC_TIME) - if format in ('full', 'long', 'medium', 'short'): - return ( - get_datetime_format(format, locale=locale) - .replace("'", "") - .replace('{0}', format_time(datetime, format, tzinfo=None, locale=locale)) - .replace('{1}', format_date(datetime, format, locale=locale)) - ) - else: - return parse_pattern(format).apply(datetime, locale) - - -def format_time( - time: datetime.time | datetime.datetime | float | None = None, - format: _PredefinedTimeFormat | str = 'medium', - tzinfo: datetime.tzinfo | None = None, - locale: Locale | str | None = None, -) -> str: - r"""Return a time formatted according to the given pattern. - - >>> from datetime import datetime, time - >>> t = time(15, 30) - >>> format_time(t, locale='en_US') - '3:30:00\u202fPM' - >>> format_time(t, format='short', locale='de_DE') - '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') - "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') - "09 o'clock AM, Eastern Daylight Time" - - As that example shows, when this function gets passed a - ``datetime.datetime`` value, the actual time in the formatted string is - adjusted to the timezone specified by the `tzinfo` parameter. If the - ``datetime`` is "naive" (i.e. it has no associated timezone information), - it is assumed to be in UTC. - - These timezone calculations are **not** performed if the value is of type - ``datetime.time``, as without date information there's no way to determine - what a given time would translate to in a different timezone without - information about whether daylight savings time is in effect or not. This - means that time values are left as-is, and the value of the `tzinfo` - parameter is only used to display the timezone name if needed: - - >>> t = time(15, 30) - >>> format_time(t, format='full', tzinfo=get_timezone('Europe/Paris'), - ... locale='fr_FR') # doctest: +SKIP - '15:30:00 heure normale d\u2019Europe centrale' - >>> format_time(t, format='full', tzinfo=get_timezone('US/Eastern'), - ... locale='en_US') # doctest: +SKIP - '3:30:00\u202fPM Eastern Standard Time' - - :param time: the ``time`` or ``datetime`` object; if `None`, the current - time in UTC is used - :param format: one of "full", "long", "medium", or "short", or a custom - date/time pattern - :param tzinfo: the time-zone to apply to the time for display - :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. - """ - - # get reference date for if we need to find the right timezone variant - # in the pattern - ref_date = time.date() if isinstance(time, datetime.datetime) else None - - time = _get_time(time, tzinfo) - - locale = Locale.parse(locale or LC_TIME) - if format in ('full', 'long', 'medium', 'short'): - format = get_time_format(format, locale=locale) - return parse_pattern(format).apply(time, locale, reference_date=ref_date) - - -def format_skeleton( - skeleton: str, - datetime: _Instant = None, - tzinfo: datetime.tzinfo | None = None, - fuzzy: bool = True, - locale: Locale | str | None = None, -) -> str: - r"""Return a time and/or date formatted according to the given pattern. - - The skeletons are defined in the CLDR data and provide more flexibility - than the simple short/long/medium formats, but are a bit harder to use. - The are defined using the date/time symbols without order or punctuation - and map to a suitable format for the given locale. - - >>> from datetime import datetime - >>> t = datetime(2007, 4, 1, 15, 30) - >>> format_skeleton('MMMEd', t, locale='fr') - 'dim. 1 avr.' - >>> format_skeleton('MMMEd', t, locale='en') - 'Sun, Apr 1' - >>> format_skeleton('yMMd', t, locale='fi') # yMMd is not in the Finnish locale; yMd gets used - '1.4.2007' - >>> format_skeleton('yMMd', t, fuzzy=False, locale='fi') # yMMd is not in the Finnish locale, an error is thrown - Traceback (most recent call last): - ... - KeyError: yMMd - >>> format_skeleton('GH', t, fuzzy=True, locale='fi_FI') # GH is not in the Finnish locale and there is no close match, an error is thrown - Traceback (most recent call last): - ... - KeyError: None - - After the skeleton is resolved to a pattern `format_datetime` is called so - all timezone processing etc is the same as for that. - - :param skeleton: A date time skeleton as defined in the cldr data. - :param datetime: the ``time`` or ``datetime`` object; if `None`, the current - time in UTC is used - :param tzinfo: the time-zone to apply to the time for display - :param fuzzy: If the skeleton is not found, allow choosing a skeleton that's - close enough to it. If there is no close match, a `KeyError` - is thrown. - :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. - """ - locale = Locale.parse(locale or LC_TIME) - if fuzzy and skeleton not in locale.datetime_skeletons: - skeleton = match_skeleton(skeleton, locale.datetime_skeletons) - format = locale.datetime_skeletons[skeleton] - return format_datetime(datetime, format, tzinfo, locale) - - -TIMEDELTA_UNITS: tuple[tuple[str, int], ...] = ( - ('year', 3600 * 24 * 365), - ('month', 3600 * 24 * 30), - ('week', 3600 * 24 * 7), - ('day', 3600 * 24), - ('hour', 3600), - ('minute', 60), - ('second', 1), -) - - -def format_timedelta( - delta: datetime.timedelta | int, - granularity: Literal[ - 'year', - 'month', - 'week', - 'day', - 'hour', - 'minute', - 'second', - ] = 'second', - threshold: float = 0.85, - add_direction: bool = False, - format: Literal['narrow', 'short', 'medium', 'long'] = 'long', - locale: Locale | str | None = None, -) -> str: - """Return a time delta according to the rules of the given locale. - - >>> from datetime import timedelta - >>> format_timedelta(timedelta(weeks=12), locale='en_US') - '3 months' - >>> format_timedelta(timedelta(seconds=1), locale='es') - '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') - '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') - '1 day' - >>> format_timedelta(timedelta(hours=23), threshold=1.1, locale='en_US') - '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') - 'in 1 hour' - >>> format_timedelta(timedelta(hours=-1), add_direction=True, locale='en') - '1 hour ago' - - The format parameter controls how compact or wide the presentation is: - - >>> format_timedelta(timedelta(hours=3), format='short', locale='en') - '3 hr' - >>> format_timedelta(timedelta(hours=3), format='narrow', locale='en') - '3h' - - :param delta: a ``timedelta`` object representing the time difference to - format, or the delta in seconds as an `int` value - :param granularity: determines the smallest unit that should be displayed, - the value can be one of "year", "month", "week", "day", - "hour", "minute" or "second" - :param threshold: factor that determines at which point the presentation - switches to the next higher unit - :param add_direction: if this flag is set to `True` the return value will - include directional information. For instance a - positive timedelta will include the information about - it being in the future, a negative will be information - about the value being in the past. - :param format: the format, can be "narrow", "short" or "long". ( - "medium" is deprecated, currently converted to "long" to - maintain compatibility) - :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. - """ - if format not in ('narrow', 'short', 'medium', 'long'): - raise TypeError('Format must be one of "narrow", "short" or "long"') - if format == 'medium': - warnings.warn( - '"medium" value for format param of format_timedelta is deprecated. Use "long" instead', - category=DeprecationWarning, - stacklevel=2, - ) - format = 'long' - if isinstance(delta, datetime.timedelta): - seconds = int((delta.days * 86400) + delta.seconds) - else: - seconds = delta - locale = Locale.parse(locale or LC_TIME) - date_fields = locale._data["date_fields"] - unit_patterns = locale._data["unit_patterns"] - - def _iter_patterns(a_unit): - if add_direction: - # Try to find the length variant version first ("year-narrow") - # before falling back to the default. - unit_rel_patterns = date_fields.get(f"{a_unit}-{format}") or date_fields[a_unit] - if seconds >= 0: - yield unit_rel_patterns['future'] - else: - yield unit_rel_patterns['past'] - a_unit = f"duration-{a_unit}" - unit_pats = unit_patterns.get(a_unit, {}) - yield unit_pats.get(format) - # We do not support `` tags at all while ingesting CLDR data, - # so these aliases specified in `root.xml` are hard-coded here: - # - # - if format in ("long", "narrow"): - yield unit_pats.get("short") - - for unit, secs_per_unit in TIMEDELTA_UNITS: - value = abs(seconds) / secs_per_unit - if value >= threshold or unit == granularity: - if unit == granularity and value > 0: - value = max(1, value) - value = int(round(value)) - plural_form = locale.plural_form(value) - pattern = None - for patterns in _iter_patterns(unit): - if patterns is not None: - pattern = patterns.get(plural_form) or patterns.get('other') - if pattern: - break - # This really should not happen - if pattern is None: - return '' - return pattern.replace('{0}', str(value)) - - return '' - - -def _format_fallback_interval( - start: _Instant, - end: _Instant, - skeleton: str | None, - tzinfo: datetime.tzinfo | None, - locale: Locale, -) -> str: - if skeleton in locale.datetime_skeletons: # Use the given skeleton - format = lambda dt: format_skeleton(skeleton, dt, tzinfo, locale=locale) - elif all( - # Both are just dates - (isinstance(d, datetime.date) and not isinstance(d, datetime.datetime)) - for d in (start, end) - ): - format = lambda dt: format_date(dt, locale=locale) - elif all( - # Both are times - (isinstance(d, datetime.time) and not isinstance(d, datetime.date)) - for d in (start, end) - ): - format = lambda dt: format_time(dt, tzinfo=tzinfo, locale=locale) - else: - format = lambda dt: format_datetime(dt, tzinfo=tzinfo, locale=locale) - - formatted_start = format(start) - formatted_end = format(end) - - if formatted_start == formatted_end: - return format(start) - - return ( - locale.interval_formats.get(None, "{0}-{1}") - .replace("{0}", formatted_start) - .replace("{1}", formatted_end) - ) - - -def format_interval( - start: _Instant, - end: _Instant, - skeleton: str | None = None, - tzinfo: datetime.tzinfo | None = None, - fuzzy: bool = True, - locale: Locale | str | None = None, -) -> str: - """ - Format an interval between two instants according to the locale's rules. - - >>> from datetime import date, time - >>> format_interval(date(2016, 1, 15), date(2016, 1, 17), "yMd", locale="fi") - '15.–17.1.2016' - - >>> format_interval(time(12, 12), time(16, 16), "Hm", locale="en_GB") - '12:12–16: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–16: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~2017/01/01' - - >>> format_interval(time(16, 18), time(16, 24), "xxx", locale="ja") - '16:18:00~16:24:00' - - >>> format_interval(date(2016, 1, 15), date(2016, 1, 17), "xxx", locale="de") - '15.01.2016\\u2009–\\u200917.01.2016' - - :param start: First instant (datetime/date/time) - :param end: Second instant (datetime/date/time) - :param skeleton: The "skeleton format" to use for formatting. - :param tzinfo: tzinfo to use (if none is already attached) - :param fuzzy: If the skeleton is not found, allow choosing a skeleton that's - close enough to it. - :param locale: A locale object or identifier. Defaults to the system time locale. - :return: Formatted interval - """ - locale = Locale.parse(locale or LC_TIME) - - # NB: The quote comments below are from the algorithm description in - # https://www.unicode.org/reports/tr35/tr35-dates.html#intervalFormats - - # > Look for the intervalFormatItem element that matches the "skeleton", - # > starting in the current locale and then following the locale fallback - # > chain up to, but not including root. - - interval_formats = locale.interval_formats - - if skeleton not in interval_formats or not skeleton: - # > If no match was found from the previous step, check what the closest - # > match is in the fallback locale chain, as in availableFormats. That - # > is, this allows for adjusting the string value field's width, - # > including adjusting between "MMM" and "MMMM", and using different - # > variants of the same field, such as 'v' and 'z'. - if skeleton and fuzzy: - skeleton = match_skeleton(skeleton, interval_formats) - else: - skeleton = None - if not skeleton: # Still no match whatsoever? - # > Otherwise, format the start and end datetime using the fallback pattern. - return _format_fallback_interval(start, end, skeleton, tzinfo, locale) - - skel_formats = interval_formats[skeleton] - - if start == end: - return format_skeleton(skeleton, start, tzinfo, fuzzy=fuzzy, locale=locale) - - start = _ensure_datetime_tzinfo(_get_datetime(start), tzinfo=tzinfo) - end = _ensure_datetime_tzinfo(_get_datetime(end), tzinfo=tzinfo) - - start_fmt = DateTimeFormat(start, locale=locale) - end_fmt = DateTimeFormat(end, locale=locale) - - # > If a match is found from previous steps, compute the calendar field - # > with the greatest difference between start and end datetime. If there - # > is no difference among any of the fields in the pattern, format as a - # > single date using availableFormats, and return. - - for field in PATTERN_CHAR_ORDER: # These are in largest-to-smallest order - if field in skel_formats and start_fmt.extract(field) != end_fmt.extract(field): - # > If there is a match, use the pieces of the corresponding pattern to - # > format the start and end datetime, as above. - return "".join( - parse_pattern(pattern).apply(instant, locale) - for pattern, instant in zip(skel_formats[field], (start, end)) - ) - - # > Otherwise, format the start and end datetime using the fallback pattern. - - return _format_fallback_interval(start, end, skeleton, tzinfo, locale) - - -def get_period_id( - time: _Instant, - tzinfo: datetime.tzinfo | None = None, - type: Literal['selection'] | None = None, - locale: Locale | str | None = None, -) -> str: - """ - Get the day period ID for a given time. - - This ID can be used as a key for the period name dictionary. - - >>> from datetime import time - >>> get_period_names(locale="de")[get_period_id(time(7, 42), locale="de")] - 'Morgen' - - >>> get_period_id(time(0), locale="en_US") - 'midnight' - - >>> get_period_id(time(0), type="selection", locale="en_US") - 'morning1' - - :param time: The time to inspect. - :param tzinfo: The timezone for the time. See ``format_time``. - :param type: The period type to use. Either "selection" or None. - The selection type is used for selecting among phrases such as - “Your email arrived yesterday evening” or “Your email arrived last night”. - :param locale: the `Locale` object, or a locale string. Defaults to the system time locale. - :return: period ID. Something is always returned -- even if it's just "am" or "pm". - """ - time = _get_time(time, tzinfo) - seconds_past_midnight = int(time.hour * 60 * 60 + time.minute * 60 + time.second) - locale = Locale.parse(locale or LC_TIME) - - # The LDML rules state that the rules may not overlap, so iterating in arbitrary - # order should be alright, though `at` periods should be preferred. - rulesets = locale.day_period_rules.get(type, {}).items() - - for rule_id, rules in rulesets: - for rule in rules: - if "at" in rule and rule["at"] == seconds_past_midnight: - return rule_id - - for rule_id, rules in rulesets: - for rule in rules: - if "from" in rule and "before" in rule: - if rule["from"] < rule["before"]: - if rule["from"] <= seconds_past_midnight < rule["before"]: - return rule_id - else: - # e.g. from="21:00" before="06:00" - if ( - rule["from"] <= seconds_past_midnight < 86400 - or 0 <= seconds_past_midnight < rule["before"] - ): - return rule_id - - start_ok = end_ok = False - - if "from" in rule and seconds_past_midnight >= rule["from"]: - start_ok = True - if "to" in rule and seconds_past_midnight <= rule["to"]: - # This rule type does not exist in the present CLDR data; - # excuse the lack of test coverage. - end_ok = True - if "before" in rule and seconds_past_midnight < rule["before"]: - end_ok = True - if "after" in rule: - raise NotImplementedError("'after' is deprecated as of CLDR 29.") - - if start_ok and end_ok: - return rule_id - - if seconds_past_midnight < 43200: - return "am" - else: - return "pm" - - -class ParseError(ValueError): - pass - - -def parse_date( - string: str, - locale: Locale | str | None = None, - format: _PredefinedTimeFormat | str = 'medium', -) -> datetime.date: - """Parse a date from a string. - - If an explicit format is provided, it is used to parse the date. - - >>> parse_date('01.04.2004', format='dd.MM.yyyy') - datetime.date(2004, 4, 1) - - If no format is given, or if it is one of "full", "long", "medium", - or "short", the function first tries to interpret the string as - ISO-8601 date format and then uses the date format for the locale - as a hint to determine the order in which the date fields appear in - the string. - - >>> parse_date('4/1/04', locale='en_US') - datetime.date(2004, 4, 1) - >>> parse_date('01.04.2004', locale='de_DE') - datetime.date(2004, 4, 1) - >>> parse_date('2004-04-01', locale='en_US') - datetime.date(2004, 4, 1) - >>> parse_date('2004-04-01', locale='de_DE') - datetime.date(2004, 4, 1) - >>> parse_date('01.04.04', locale='de_DE', format='short') - datetime.date(2004, 4, 1) - - :param string: the string containing the date - :param locale: a `Locale` object or a locale identifier - :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. - :param format: the format to use, either an explicit date format, - or one of "full", "long", "medium", or "short" - (see ``get_time_format``) - """ - numbers = re.findall(r'(\d+)', string) - if not numbers: - raise ParseError("No numbers were found in input") - - use_predefined_format = format in ('full', 'long', 'medium', 'short') - # we try ISO-8601 format first, meaning similar to formats - # extended YYYY-MM-DD or basic YYYYMMDD - iso_alike = re.match( - r'^(\d{4})-?([01]\d)-?([0-3]\d)$', - string, - flags=re.ASCII, # allow only ASCII digits - ) - if iso_alike and use_predefined_format: - try: - return datetime.date(*map(int, iso_alike.groups())) - except ValueError: - pass # a locale format might fit better, so let's continue - - if use_predefined_format: - fmt = get_date_format(format=format, locale=locale) - else: - fmt = parse_pattern(format) - format_str = fmt.pattern.lower() - year_idx = format_str.index('y') - month_idx = format_str.find('m') - if month_idx < 0: - month_idx = format_str.index('l') - day_idx = format_str.index('d') - - indexes = sorted([(year_idx, 'Y'), (month_idx, 'M'), (day_idx, 'D')]) - indexes = {item[1]: idx for idx, item in enumerate(indexes)} - - # FIXME: this currently only supports numbers, but should also support month - # names, both in the requested locale, and english - - year = numbers[indexes['Y']] - year = 2000 + int(year) if len(year) == 2 else int(year) - month = int(numbers[indexes['M']]) - day = int(numbers[indexes['D']]) - if month > 12: - month, day = day, month - return datetime.date(year, month, day) - - -def parse_time( - string: str, - locale: Locale | str | None = None, - format: _PredefinedTimeFormat | str = 'medium', -) -> datetime.time: - """Parse a time from a string. - - This function uses the time format for the locale as a hint to determine - the order in which the time fields appear in the string. - - If an explicit format is provided, the function will use it to parse - the time instead. - - >>> parse_time('15:30:00', locale='en_US') - datetime.time(15, 30) - >>> parse_time('15:30:00', format='H:mm:ss') - datetime.time(15, 30) - - :param string: the string containing the time - :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale. - :param format: the format to use, either an explicit time format, - or one of "full", "long", "medium", or "short" - (see ``get_time_format``) - :return: the parsed time - :rtype: `time` - """ - numbers = re.findall(r'(\d+)', string) - if not numbers: - raise ParseError("No numbers were found in input") - - # TODO: try ISO format first? - if format in ('full', 'long', 'medium', 'short'): - fmt = get_time_format(format=format, locale=locale) - else: - fmt = parse_pattern(format) - format_str = fmt.pattern.lower() - hour_idx = format_str.find('h') - if hour_idx < 0: - hour_idx = format_str.index('k') - min_idx = format_str.index('m') - # format might not contain seconds - if (sec_idx := format_str.find('s')) < 0: - sec_idx = math.inf - - indexes = sorted([(hour_idx, 'H'), (min_idx, 'M'), (sec_idx, 'S')]) - indexes = {item[1]: idx for idx, item in enumerate(indexes)} - - # TODO: support time zones - - # Check if the format specifies a period to be used; - # if it does, look for 'pm' to figure out an offset. - hour_offset = 0 - if 'a' in format_str and 'pm' in string.lower(): - hour_offset = 12 - - # Parse up to three numbers from the string. - minute = second = 0 - hour = int(numbers[indexes['H']]) + hour_offset - if len(numbers) > 1: - minute = int(numbers[indexes['M']]) - if len(numbers) > 2: - second = int(numbers[indexes['S']]) - return datetime.time(hour, minute, second) - - -class DateTimePattern: - def __init__(self, pattern: str, format: DateTimeFormat): - self.pattern = pattern - self.format = format - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.pattern!r}>" - - def __str__(self) -> str: - pat = self.pattern - return pat - - def __mod__(self, other: DateTimeFormat) -> str: - if not isinstance(other, DateTimeFormat): - return NotImplemented - return self.format % other - - def apply( - self, - datetime: datetime.date | datetime.time, - locale: Locale | str | None, - reference_date: datetime.date | None = None, - ) -> str: - return self % DateTimeFormat(datetime, locale, reference_date) - - -class DateTimeFormat: - def __init__( - self, - value: datetime.date | datetime.time, - locale: Locale | str, - reference_date: datetime.date | None = None, - ) -> None: - assert isinstance(value, (datetime.date, datetime.datetime, datetime.time)) - if isinstance(value, (datetime.datetime, datetime.time)) and value.tzinfo is None: - value = value.replace(tzinfo=UTC) - self.value = value - self.locale = Locale.parse(locale) - self.reference_date = reference_date - - def __getitem__(self, name: str) -> str: - char = name[0] - num = len(name) - if char == 'G': - return self.format_era(char, num) - elif char in ('y', 'Y', 'u'): - return self.format_year(char, num) - elif char in ('Q', 'q'): - return self.format_quarter(char, num) - elif char in ('M', 'L'): - return self.format_month(char, num) - elif char in ('w', 'W'): - return self.format_week(char, num) - elif char == 'd': - return self.format(self.value.day, num) - elif char == 'D': - return self.format_day_of_year(num) - elif char == 'F': - return self.format_day_of_week_in_month() - elif char in ('E', 'e', 'c'): - return self.format_weekday(char, num) - elif char in ('a', 'b', 'B'): - return self.format_period(char, num) - elif char == 'h': - if self.value.hour % 12 == 0: - return self.format(12, num) - else: - return self.format(self.value.hour % 12, num) - elif char == 'H': - return self.format(self.value.hour, num) - elif char == 'K': - return self.format(self.value.hour % 12, num) - elif char == 'k': - if self.value.hour == 0: - return self.format(24, num) - else: - return self.format(self.value.hour, num) - elif char == 'm': - return self.format(self.value.minute, num) - elif char == 's': - return self.format(self.value.second, num) - elif char == 'S': - return self.format_frac_seconds(num) - elif char == 'A': - return self.format_milliseconds_in_day(num) - elif char in ('z', 'Z', 'v', 'V', 'x', 'X', 'O'): - return self.format_timezone(char, num) - else: - raise KeyError(f"Unsupported date/time field {char!r}") - - def extract(self, char: str) -> int: - char = str(char)[0] - if char == 'y': - return self.value.year - elif char == 'M': - return self.value.month - elif char == 'd': - return self.value.day - elif char == 'H': - return self.value.hour - elif char == 'h': - return self.value.hour % 12 or 12 - elif char == 'm': - return self.value.minute - elif char == 'a': - return int(self.value.hour >= 12) # 0 for am, 1 for pm - else: - raise NotImplementedError( - f"Not implemented: extracting {char!r} from {self.value!r}", - ) - - def format_era(self, char: str, num: int) -> str: - width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[max(3, num)] - era = int(self.value.year >= 0) - return get_era_names(width, self.locale)[era] - - def format_year(self, char: str, num: int) -> str: - value = self.value.year - if char.isupper(): - month = self.value.month - if month == 1 and self.value.day < 7 and self.get_week_of_year() >= 52: - value -= 1 - elif month == 12 and self.value.day > 25 and self.get_week_of_year() <= 2: - value += 1 - year = self.format(value, num) - if num == 2: - year = year[-2:] - return year - - def format_quarter(self, char: str, num: int) -> str: - quarter = (self.value.month - 1) // 3 + 1 - if num <= 2: - return '%0*d' % (num, quarter) - width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[num] - context = {'Q': 'format', 'q': 'stand-alone'}[char] - return get_quarter_names(width, context, self.locale)[quarter] - - def format_month(self, char: str, num: int) -> str: - if num <= 2: - return '%0*d' % (num, self.value.month) - width = {3: 'abbreviated', 4: 'wide', 5: 'narrow'}[num] - context = {'M': 'format', 'L': 'stand-alone'}[char] - return get_month_names(width, context, self.locale)[self.value.month] - - def format_week(self, char: str, num: int) -> str: - if char.islower(): # week of year - week = self.get_week_of_year() - return self.format(week, num) - else: # week of month - week = self.get_week_of_month() - return str(week) - - def format_weekday(self, char: str = 'E', num: int = 4) -> str: - """ - Return weekday from parsed datetime according to format pattern. - - >>> from datetime import date - >>> format = DateTimeFormat(date(2016, 2, 28), Locale.parse('en_US')) - >>> format.format_weekday() - '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) - '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) - 'ip.' - >>> format.format_period('b', 1) - 'iltap.' - >>> format.format_period('b', 4) - 'iltapäivä' - >>> format.format_period('B', 4) - 'iltapäivällä' - >>> format.format_period('B', 5) - 'ip.' - - >>> format = DateTimeFormat(datetime(2022, 4, 28, 6, 27), 'zh_Hant') - >>> format.format_period('a', 1) - '上午' - >>> format.format_period('B', 1) - '清晨' - - :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, 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) # fmt: skip - return get_timezone_location(value.tzinfo, locale=self.locale) - elif char in 'Xx': - return_z = char == 'X' - if num == 1: - width = 'iso8601_short' - elif num in (2, 4): - width = 'short' - elif num in (3, 5): - width = 'iso8601' - return get_timezone_gmt(value, width=width, locale=self.locale, return_z=return_z) # fmt: skip - - def format(self, value: SupportsInt, length: int) -> str: - return '%0*d' % (length, value) - - def get_day_of_year(self, date: datetime.date | None = None) -> int: - if date is None: - date = self.value - return (date - date.replace(month=1, day=1)).days + 1 - - def get_week_of_year(self) -> int: - """Return the week of the year.""" - day_of_year = self.get_day_of_year(self.value) - week = self.get_week_number(day_of_year) - if week == 0: - date = datetime.date(self.value.year - 1, 12, 31) - week = self.get_week_number(self.get_day_of_year(date), date.weekday()) - elif week > 52: - weekday = datetime.date(self.value.year + 1, 1, 1).weekday() - if ( - self.get_week_number(1, weekday) == 1 - and 32 - (weekday - self.locale.first_week_day) % 7 <= self.value.day - ): - week = 1 - return week - - def get_week_of_month(self) -> int: - """Return the week of the month.""" - return self.get_week_number(self.value.day) - - def get_week_number(self, day_of_period: int, day_of_week: int | None = None) -> int: - """Return the number of the week of a day within a period. This may be - the week number in a year or the week number in a month. - - Usually this will return a value equal to or greater than 1, but if the - first week of the period is so short that it actually counts as the last - week of the previous period, this function will return 0. - - >>> date = datetime.date(2006, 1, 8) - >>> DateTimeFormat(date, 'de_DE').get_week_number(6) - 1 - >>> DateTimeFormat(date, 'en_US').get_week_number(6) - 2 - - :param day_of_period: the number of the day in the period (usually - either the day of month or the day of year) - :param day_of_week: the week day; if omitted, the week day of the - current date is assumed - """ - if day_of_week is None: - day_of_week = self.value.weekday() - first_day = (day_of_week - self.locale.first_week_day - day_of_period + 1) % 7 - if first_day < 0: - first_day += 7 - week_number = (day_of_period + first_day - 1) // 7 - if 7 - first_day >= self.locale.min_week_days: - week_number += 1 - return week_number - - -PATTERN_CHARS: dict[str, list[int] | None] = { - 'G': [1, 2, 3, 4, 5], # era - 'y': None, 'Y': None, 'u': None, # year - 'Q': [1, 2, 3, 4, 5], 'q': [1, 2, 3, 4, 5], # quarter - 'M': [1, 2, 3, 4, 5], 'L': [1, 2, 3, 4, 5], # month - 'w': [1, 2], 'W': [1], # week - 'd': [1, 2], 'D': [1, 2, 3], 'F': [1], 'g': None, # day - 'E': [1, 2, 3, 4, 5, 6], 'e': [1, 2, 3, 4, 5, 6], 'c': [1, 3, 4, 5, 6], # week day - 'a': [1, 2, 3, 4, 5], 'b': [1, 2, 3, 4, 5], 'B': [1, 2, 3, 4, 5], # period - 'h': [1, 2], 'H': [1, 2], 'K': [1, 2], 'k': [1, 2], # hour - 'm': [1, 2], # minute - 's': [1, 2], 'S': None, 'A': None, # second - 'z': [1, 2, 3, 4], 'Z': [1, 2, 3, 4, 5], 'O': [1, 4], 'v': [1, 4], # zone - 'V': [1, 2, 3, 4], 'x': [1, 2, 3, 4, 5], 'X': [1, 2, 3, 4, 5], # zone -} # fmt: skip - -#: 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 - '%(MMMM)s%(d)s' - >>> parse_pattern("MMM d, yyyy").format - '%(MMM)s %(d)s, %(yyyy)s' - - Pattern can contain literal strings in single quotes: - - >>> parse_pattern("H:mm' Uhr 'z").format - '%(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 - "%(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('E d.M. – E d.M.') - ['E d.M. – ', 'E d.M.'] - >>> split_interval_pattern("Y 'text' Y 'more text'") - ["Y 'text '", "Y 'more text'"] - >>> split_interval_pattern('E, MMM d – E') - ['E, MMM d – ', 'E'] - >>> split_interval_pattern("MMM d") - ['MMM d'] - >>> split_interval_pattern("y G") - ['y G'] - >>> split_interval_pattern('MMM d – d') - ['MMM d – ', 'd'] - - :param pattern: Interval pattern string - :return: list of "subpatterns" - """ - - seen_fields = set() - parts = [[]] - - for tok_type, tok_value in tokenize_pattern(pattern): - if tok_type == "field": - if tok_value[0] in seen_fields: # Repeated field - parts.append([]) - seen_fields.clear() - seen_fields.add(tok_value[0]) - parts[-1].append((tok_type, tok_value)) - - return [untokenize_pattern(tokens) for tokens in parts] - - -def match_skeleton( - skeleton: str, - options: Iterable[str], - allow_different_fields: bool = False, -) -> str | None: - """ - Find the closest match for the given datetime skeleton among the options given. - - This uses the rules outlined in the TR35 document. - - >>> match_skeleton('yMMd', ('yMd', 'yMMMd')) - 'yMd' - - >>> match_skeleton('yMMd', ('jyMMd',), allow_different_fields=True) - 'jyMMd' - - >>> match_skeleton('yMMd', ('qyMMd',), allow_different_fields=False) - - >>> match_skeleton('hmz', ('hmv',)) - 'hmv' - - :param skeleton: The skeleton to match - :type skeleton: str - :param options: An iterable of other skeletons to match against - :type options: Iterable[str] - :param allow_different_fields: Whether to allow a match that uses different fields - than the skeleton requested. - :type allow_different_fields: bool - - :return: The closest skeleton match, or if no match was found, None. - :rtype: str|None - """ - - # TODO: maybe implement pattern expansion? - - # Based on the implementation in - # https://github.com/unicode-org/icu/blob/main/icu4j/main/core/src/main/java/com/ibm/icu/text/DateIntervalInfo.java - - # Filter out falsy values and sort for stability; when `interval_formats` is passed in, there may be a None key. - options = sorted(option for option in options if option) - - if 'z' in skeleton and not any('z' in option for option in options): - skeleton = skeleton.replace('z', 'v') - if 'k' in skeleton and not any('k' in option for option in options): - skeleton = skeleton.replace('k', 'H') - if 'K' in skeleton and not any('K' in option for option in options): - skeleton = skeleton.replace('K', 'h') - if 'a' in skeleton and not any('a' in option for option in options): - skeleton = skeleton.replace('a', '') - if 'b' in skeleton and not any('b' in option for option in options): - skeleton = skeleton.replace('b', '') - - get_input_field_width = dict(t[1] for t in tokenize_pattern(skeleton) if t[0] == "field").get # fmt: skip - 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 # fmt: skip - 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 - # Magic weight constant for "entirely different fields" - distance += 0x1000 - elif field == 'M' and ( - (input_width > 2 and opt_width <= 2) or (input_width <= 2 and opt_width > 2) - ): - # Magic weight constant for "text turns into a number" - distance += 0x100 - else: - distance += abs(input_width - opt_width) - - if not option: - # We lost the option along the way (probably due to "allow_different_fields") - continue - - if not best_skeleton or distance < best_distance: - best_skeleton = option - best_distance = distance - - if distance == 0: # Found a perfect match! - break - - return best_skeleton diff --git a/.venv/lib/python3.12/site-packages/babel/global.dat b/.venv/lib/python3.12/site-packages/babel/global.dat deleted file mode 100644 index 22549945..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/global.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/languages.py b/.venv/lib/python3.12/site-packages/babel/languages.py deleted file mode 100644 index 5b2396c8..00000000 --- a/.venv/lib/python3.12/site-packages/babel/languages.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import annotations - -from babel.core import get_global - - -def get_official_languages( - territory: str, - regional: bool = False, - de_facto: bool = False, -) -> tuple[str, ...]: - """ - Get the official language(s) for the given territory. - - The language codes, if any are known, are returned in order of descending popularity. - - If the `regional` flag is set, then languages which are regionally official are also returned. - - If the `de_facto` flag is set, then languages which are "de facto" official are also returned. - - .. warning:: Note that the data is as up to date as the current version of the CLDR used - by Babel. If you need scientifically accurate information, use another source! - - :param territory: Territory code - :type territory: str - :param regional: Whether to return regionally official languages too - :type regional: bool - :param de_facto: Whether to return de-facto official languages too - :type de_facto: bool - :return: Tuple of language codes - :rtype: tuple[str] - """ - - territory = str(territory).upper() - allowed_stati = {"official"} - if regional: - allowed_stati.add("official_regional") - if de_facto: - allowed_stati.add("de_facto_official") - - languages = get_global("territory_languages").get(territory, {}) - pairs = [ - (info['population_percent'], language) - for language, info in languages.items() - if info.get('official_status') in allowed_stati - ] - pairs.sort(reverse=True) - return tuple(lang for _, lang in pairs) - - -def get_territory_language_info( - territory: str, -) -> dict[str, dict[str, float | str | None]]: - """ - Get a dictionary of language information for a territory. - - The dictionary is keyed by language code; the values are dicts with more information. - - The following keys are currently known for the values: - - * `population_percent`: The percentage of the territory's population speaking the - language. - * `official_status`: An optional string describing the officiality status of the language. - Known values are "official", "official_regional" and "de_facto_official". - - .. warning:: Note that the data is as up to date as the current version of the CLDR used - by Babel. If you need scientifically accurate information, use another source! - - .. note:: Note that the format of the dict returned may change between Babel versions. - - See https://www.unicode.org/cldr/charts/latest/supplemental/territory_language_information.html - - :param territory: Territory code - :type territory: str - :return: Language information dictionary - :rtype: dict[str, dict] - """ - territory = str(territory).upper() - return get_global("territory_languages").get(territory, {}).copy() diff --git a/.venv/lib/python3.12/site-packages/babel/lists.py b/.venv/lib/python3.12/site-packages/babel/lists.py deleted file mode 100644 index b6c85980..00000000 --- a/.venv/lib/python3.12/site-packages/babel/lists.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -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-2026 by the Babel Team. -:license: BSD, see LICENSE for more details. -""" - -from __future__ import annotations - -import warnings -from collections.abc import Sequence -from typing import Literal - -from babel.core import Locale, default_locale - -_DEFAULT_LOCALE = default_locale() # TODO(3.0): Remove this. - - -def __getattr__(name): - if name == "DEFAULT_LOCALE": - warnings.warn( - "The babel.lists.DEFAULT_LOCALE constant is deprecated and will be removed.", - DeprecationWarning, - stacklevel=2, - ) - return _DEFAULT_LOCALE - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") - - -def format_list( - lst: Sequence[str], - style: Literal[ - 'standard', - 'standard-short', - 'or', - 'or-short', - 'unit', - 'unit-short', - 'unit-narrow', - ] = 'standard', - locale: Locale | str | None = None, -) -> str: - """ - Format the items in `lst` as a list. - - >>> format_list(['apples', 'oranges', 'pears'], locale='en') - 'apples, oranges, and pears' - >>> format_list(['apples', 'oranges', 'pears'], locale='zh') - 'apples、oranges和pears' - >>> format_list(['omena', 'peruna', 'aplari'], style='or', locale='fi') - 'omena, peruna tai aplari' - - Not all styles are necessarily available in all locales. - The function will attempt to fall back to replacement styles according to the rules - set forth in the CLDR root XML file, and raise a ValueError if no suitable replacement - can be found. - - The following text is verbatim from the Unicode TR35-49 spec [1]. - - * standard: - A typical 'and' list for arbitrary placeholders. - eg. "January, February, and March" - * standard-short: - A short version of an 'and' list, suitable for use with short or abbreviated placeholder values. - eg. "Jan., Feb., and Mar." - * or: - A typical 'or' list for arbitrary placeholders. - eg. "January, February, or March" - * or-short: - A short version of an 'or' list. - eg. "Jan., Feb., or Mar." - * unit: - A list suitable for wide units. - eg. "3 feet, 7 inches" - * unit-short: - A list suitable for short units - eg. "3 ft, 7 in" - * unit-narrow: - A list suitable for narrow units, where space on the screen is very limited. - eg. "3′ 7″" - - [1]: https://www.unicode.org/reports/tr35/tr35-49/tr35-general.html#ListPatterns - - :param lst: a sequence of items to format in to a list - :param style: the style to format the list with. See above for description. - :param locale: the locale. Defaults to the system locale. - """ - locale = Locale.parse(locale or _DEFAULT_LOCALE) - if not lst: - return '' - if len(lst) == 1: - return lst[0] - - patterns = _resolve_list_style(locale, style) - - if len(lst) == 2 and '2' in patterns: - return patterns['2'].format(*lst) - - result = patterns['start'].format(lst[0], lst[1]) - for elem in lst[2:-1]: - result = patterns['middle'].format(result, elem) - result = patterns['end'].format(result, lst[-1]) - - return result - - -# Based on CLDR 45's root.xml file's ``es. -# The root file defines both `standard` and `or`, -# so they're always available. -# TODO: It would likely be better to use the -# babel.localedata.Alias mechanism for this, -# but I'm not quite sure how it's supposed to -# work with inheritance and data in the root. -_style_fallbacks = { - "or-narrow": ["or-short", "or"], - "or-short": ["or"], - "standard-narrow": ["standard-short", "standard"], - "standard-short": ["standard"], - "unit": ["unit-short", "standard"], - "unit-narrow": ["unit-short", "unit", "standard"], - "unit-short": ["standard"], -} - - -def _resolve_list_style(locale: Locale, style: str): - for style in (style, *(_style_fallbacks.get(style, []))): # noqa: B020 - if style in locale.list_patterns: - return locale.list_patterns[style] - raise ValueError( - f"Locale {locale} does not support list formatting style {style!r} " - f"(supported are {sorted(locale.list_patterns)})", - ) diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/LICENSE.unicode b/.venv/lib/python3.12/site-packages/babel/locale-data/LICENSE.unicode deleted file mode 100644 index 861b74f3..00000000 --- a/.venv/lib/python3.12/site-packages/babel/locale-data/LICENSE.unicode +++ /dev/null @@ -1,41 +0,0 @@ -UNICODE LICENSE V3 - -COPYRIGHT AND PERMISSION NOTICE - -Copyright © 2004-2025 Unicode, Inc. - -NOTICE TO USER: Carefully read the following legal agreement. BY -DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR -SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE -TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT -DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of data files and any associated documentation (the "Data Files") or -software and any associated documentation (the "Software") to deal in the -Data Files or Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, and/or sell -copies of the Data Files or Software, and to permit persons to whom the -Data Files or Software are furnished to do so, provided that either (a) -this copyright and permission notice appear with all copies of the Data -Files or Software, or (b) this copyright and permission notice appear in -associated Documentation. - -THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF -THIRD PARTY RIGHTS. - -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE -BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, -OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA -FILES OR SOFTWARE. - -Except as contained in this notice, the name of a copyright holder shall -not be used in advertising or otherwise to promote the sale, use or other -dealings in these Data Files or Software without prior written -authorization of the copyright holder. - -SPDX-License-Identifier: Unicode-3.0 diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/aa.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/aa.dat deleted file mode 100644 index 96b76a00..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/aa.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/aa_DJ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/aa_DJ.dat deleted file mode 100644 index a3cb2563..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/aa_DJ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/aa_ER.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/aa_ER.dat deleted file mode 100644 index a01edfca..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/aa_ER.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/aa_ET.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/aa_ET.dat deleted file mode 100644 index 119b891b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/aa_ET.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ab.dat deleted file mode 100644 index e1fbc6e6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ab_GE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ab_GE.dat deleted file mode 100644 index 15a980a7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ab_GE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/af.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/af.dat deleted file mode 100644 index a1e347f6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/af.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/af_NA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/af_NA.dat deleted file mode 100644 index d5ff2a31..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/af_NA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/af_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/af_ZA.dat deleted file mode 100644 index 34779331..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/af_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/agq.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/agq.dat deleted file mode 100644 index a4eb919c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/agq.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/agq_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/agq_CM.dat deleted file mode 100644 index 57701c90..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/agq_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ak.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ak.dat deleted file mode 100644 index 6c937699..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ak.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ak_GH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ak_GH.dat deleted file mode 100644 index a7c63dae..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ak_GH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/am.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/am.dat deleted file mode 100644 index 3647c436..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/am.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/am_ET.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/am_ET.dat deleted file mode 100644 index 050cc76c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/am_ET.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/an.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/an.dat deleted file mode 100644 index 1678ebce..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/an.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/an_ES.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/an_ES.dat deleted file mode 100644 index a11156aa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/an_ES.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ann.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ann.dat deleted file mode 100644 index cee6e2bb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ann.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ann_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ann_NG.dat deleted file mode 100644 index 612c3f89..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ann_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/apc.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/apc.dat deleted file mode 100644 index 644ec2a1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/apc.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/apc_SY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/apc_SY.dat deleted file mode 100644 index 6c5e15a7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/apc_SY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar.dat deleted file mode 100644 index 30da0f40..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_001.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_001.dat deleted file mode 100644 index bdc0475f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_001.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_AE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_AE.dat deleted file mode 100644 index f8386597..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_AE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_BH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_BH.dat deleted file mode 100644 index 1792ea6b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_BH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_DJ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_DJ.dat deleted file mode 100644 index 579056a5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_DJ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_DZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_DZ.dat deleted file mode 100644 index 2d61edcd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_DZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_EG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_EG.dat deleted file mode 100644 index d9be03e1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_EG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_EH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_EH.dat deleted file mode 100644 index df44a58b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_EH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_ER.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_ER.dat deleted file mode 100644 index cd036430..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_ER.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_IL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_IL.dat deleted file mode 100644 index 557c47f4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_IL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_IQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_IQ.dat deleted file mode 100644 index 5b3d21c4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_IQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_JO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_JO.dat deleted file mode 100644 index 8bf13f24..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_JO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_KM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_KM.dat deleted file mode 100644 index 88f1ac48..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_KM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_KW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_KW.dat deleted file mode 100644 index 80762a47..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_KW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_LB.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_LB.dat deleted file mode 100644 index 060dfa49..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_LB.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_LY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_LY.dat deleted file mode 100644 index a870e157..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_LY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_MA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_MA.dat deleted file mode 100644 index ecdc22a5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_MA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_MR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_MR.dat deleted file mode 100644 index 7d66b5cf..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_MR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_OM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_OM.dat deleted file mode 100644 index d318d99f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_OM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_PS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_PS.dat deleted file mode 100644 index 2c3ef839..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_PS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_QA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_QA.dat deleted file mode 100644 index d187cc66..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_QA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SA.dat deleted file mode 100644 index ae1ecaed..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SD.dat deleted file mode 100644 index 0ce23d8a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SO.dat deleted file mode 100644 index ed6648bc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SS.dat deleted file mode 100644 index a2cadef7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SY.dat deleted file mode 100644 index 701de7ad..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_SY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_TD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_TD.dat deleted file mode 100644 index deb0d9c4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_TD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_TN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_TN.dat deleted file mode 100644 index 46853cec..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_TN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_YE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ar_YE.dat deleted file mode 100644 index 8c2212b5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ar_YE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/arn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/arn.dat deleted file mode 100644 index 5abcc770..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/arn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/arn_CL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/arn_CL.dat deleted file mode 100644 index 4aadebb1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/arn_CL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/as.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/as.dat deleted file mode 100644 index 4f1f6cc4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/as.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/as_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/as_IN.dat deleted file mode 100644 index 9f24d230..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/as_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/asa.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/asa.dat deleted file mode 100644 index 6c4f84f4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/asa.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/asa_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/asa_TZ.dat deleted file mode 100644 index 0b2adf25..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/asa_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ast.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ast.dat deleted file mode 100644 index 11a786a0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ast.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ast_ES.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ast_ES.dat deleted file mode 100644 index af96de06..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ast_ES.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/az.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/az.dat deleted file mode 100644 index 5ee74802..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/az.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab.dat deleted file mode 100644 index d18ccb51..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IQ.dat deleted file mode 100644 index 393175fc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IR.dat deleted file mode 100644 index 8df9df25..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_IR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_TR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_TR.dat deleted file mode 100644 index 99fa84c4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Arab_TR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl.dat deleted file mode 100644 index 2f79fbd0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl_AZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl_AZ.dat deleted file mode 100644 index 75ee5220..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Cyrl_AZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/az_Latn.dat deleted file mode 100644 index 88c306fe..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Latn_AZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/az_Latn_AZ.dat deleted file mode 100644 index 75ee5220..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/az_Latn_AZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ba.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ba.dat deleted file mode 100644 index 99bc134f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ba.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ba_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ba_RU.dat deleted file mode 100644 index 93dadc0f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ba_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bal.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bal.dat deleted file mode 100644 index 6135e5a3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bal.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab.dat deleted file mode 100644 index 9fc6e82e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab_PK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab_PK.dat deleted file mode 100644 index 37f77e20..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Arab_PK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn.dat deleted file mode 100644 index 8a7c8c09..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn_PK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn_PK.dat deleted file mode 100644 index 37f77e20..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bal_Latn_PK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bas.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bas.dat deleted file mode 100644 index 6afbc89d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bas.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bas_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bas_CM.dat deleted file mode 100644 index 992e8632..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bas_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/be.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/be.dat deleted file mode 100644 index 160a752f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/be.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/be_BY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/be_BY.dat deleted file mode 100644 index 759f67a1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/be_BY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/be_TARASK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/be_TARASK.dat deleted file mode 100644 index 7e895362..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/be_TARASK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bem.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bem.dat deleted file mode 100644 index 35c138fc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bem.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bem_ZM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bem_ZM.dat deleted file mode 100644 index 7f393a8b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bem_ZM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bew.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bew.dat deleted file mode 100644 index 798011d5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bew.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bew_ID.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bew_ID.dat deleted file mode 100644 index 12cfbff6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bew_ID.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bez.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bez.dat deleted file mode 100644 index 04e40d18..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bez.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bez_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bez_TZ.dat deleted file mode 100644 index 76974eb1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bez_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bg.dat deleted file mode 100644 index dfd5660a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bg_BG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bg_BG.dat deleted file mode 100644 index b69fe979..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bg_BG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bgc.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bgc.dat deleted file mode 100644 index 1a3cdd55..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bgc.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bgc_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bgc_IN.dat deleted file mode 100644 index bc2e0217..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bgc_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bgn.dat deleted file mode 100644 index f8297eb3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_AE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_AE.dat deleted file mode 100644 index 0b17e78f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_AE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_AF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_AF.dat deleted file mode 100644 index 4062c74d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_AF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_IR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_IR.dat deleted file mode 100644 index 99a1b500..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_IR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_OM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_OM.dat deleted file mode 100644 index f3962683..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_OM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_PK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_PK.dat deleted file mode 100644 index e15afea2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bgn_PK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bho.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bho.dat deleted file mode 100644 index 15f164ee..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bho.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bho_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bho_IN.dat deleted file mode 100644 index 4f7eb58e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bho_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/blo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/blo.dat deleted file mode 100644 index 6952c5e3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/blo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/blo_BJ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/blo_BJ.dat deleted file mode 100644 index fbc0ae51..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/blo_BJ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/blt.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/blt.dat deleted file mode 100644 index fee95211..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/blt.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/blt_VN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/blt_VN.dat deleted file mode 100644 index 1cf8aa30..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/blt_VN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bm.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bm.dat deleted file mode 100644 index 35a8748f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bm.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bm_ML.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bm_ML.dat deleted file mode 100644 index db406084..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bm_ML.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo.dat deleted file mode 100644 index 318955fe..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo_ML.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo_ML.dat deleted file mode 100644 index db406084..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bm_Nkoo_ML.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bn.dat deleted file mode 100644 index d6d5dadf..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bn_BD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bn_BD.dat deleted file mode 100644 index 0814ec91..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bn_BD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bn_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bn_IN.dat deleted file mode 100644 index ea4c51c3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bn_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bo.dat deleted file mode 100644 index 3e914649..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bo_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bo_CN.dat deleted file mode 100644 index d9e62431..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bo_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bo_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bo_IN.dat deleted file mode 100644 index 900a19e7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bo_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/br.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/br.dat deleted file mode 100644 index 6ee99829..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/br.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/br_FR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/br_FR.dat deleted file mode 100644 index 5964d661..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/br_FR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/brx.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/brx.dat deleted file mode 100644 index db3f702a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/brx.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/brx_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/brx_IN.dat deleted file mode 100644 index 6dafcc92..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/brx_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bs.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bs.dat deleted file mode 100644 index 9e9a5e5e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bs.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl.dat deleted file mode 100644 index 2047a88b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl_BA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl_BA.dat deleted file mode 100644 index 4a56da8f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Cyrl_BA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn.dat deleted file mode 100644 index 767af2e9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn_BA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn_BA.dat deleted file mode 100644 index 4a56da8f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bs_Latn_BA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bss.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bss.dat deleted file mode 100644 index 9b59977c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bss.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/bss_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/bss_CM.dat deleted file mode 100644 index c02e93f2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/bss_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/byn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/byn.dat deleted file mode 100644 index c8a14266..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/byn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/byn_ER.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/byn_ER.dat deleted file mode 100644 index ef7364c0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/byn_ER.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ca.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ca.dat deleted file mode 100644 index 89fa5d88..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ca.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ca_AD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ca_AD.dat deleted file mode 100644 index 417de470..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ca_AD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ca_ES.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ca_ES.dat deleted file mode 100644 index 1262e7c7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ca_ES.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ca_ES_VALENCIA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ca_ES_VALENCIA.dat deleted file mode 100644 index f9a67d74..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ca_ES_VALENCIA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ca_FR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ca_FR.dat deleted file mode 100644 index 1e13497d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ca_FR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ca_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ca_IT.dat deleted file mode 100644 index 18daef70..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ca_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cad.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cad.dat deleted file mode 100644 index e0751fea..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cad.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cad_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cad_US.dat deleted file mode 100644 index 649a1277..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cad_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cch.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cch.dat deleted file mode 100644 index 6bfb265e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cch.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cch_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cch_NG.dat deleted file mode 100644 index 7ca50d06..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cch_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ccp.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ccp.dat deleted file mode 100644 index 3c5578b2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ccp.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ccp_BD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ccp_BD.dat deleted file mode 100644 index 23248d87..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ccp_BD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ccp_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ccp_IN.dat deleted file mode 100644 index e4fe9a01..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ccp_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ce.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ce.dat deleted file mode 100644 index a37c7047..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ce.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ce_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ce_RU.dat deleted file mode 100644 index 613de660..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ce_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ceb.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ceb.dat deleted file mode 100644 index d6b8f188..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ceb.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ceb_PH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ceb_PH.dat deleted file mode 100644 index f5a88a13..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ceb_PH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cgg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cgg.dat deleted file mode 100644 index 5687c38b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cgg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cgg_UG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cgg_UG.dat deleted file mode 100644 index 3ed7de5e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cgg_UG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cho.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cho.dat deleted file mode 100644 index 8d978b7c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cho.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cho_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cho_US.dat deleted file mode 100644 index 927cd451..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cho_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/chr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/chr.dat deleted file mode 100644 index 557280d4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/chr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/chr_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/chr_US.dat deleted file mode 100644 index 30579852..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/chr_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cic.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cic.dat deleted file mode 100644 index 87877563..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cic.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cic_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cic_US.dat deleted file mode 100644 index a2f944fb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cic_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ckb.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ckb.dat deleted file mode 100644 index e8f1be3e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ckb.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ckb_IQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ckb_IQ.dat deleted file mode 100644 index 68ae78b5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ckb_IQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ckb_IR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ckb_IR.dat deleted file mode 100644 index 407a6058..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ckb_IR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/co.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/co.dat deleted file mode 100644 index cf9e7bc6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/co.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/co_FR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/co_FR.dat deleted file mode 100644 index ac968523..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/co_FR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cop.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cop.dat deleted file mode 100644 index 01dd6959..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cop.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cop_EG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cop_EG.dat deleted file mode 100644 index c6811e71..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cop_EG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cs.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cs.dat deleted file mode 100644 index a00782bc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cs.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cs_CZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cs_CZ.dat deleted file mode 100644 index 0c492c42..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cs_CZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/csw.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/csw.dat deleted file mode 100644 index 1224a99e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/csw.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/csw_CA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/csw_CA.dat deleted file mode 100644 index 01b6c607..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/csw_CA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cu.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cu.dat deleted file mode 100644 index 19443c52..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cu.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cu_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cu_RU.dat deleted file mode 100644 index a9d3101e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cu_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cv.dat deleted file mode 100644 index 6e21fed3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cv_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cv_RU.dat deleted file mode 100644 index 59759914..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cv_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cy.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cy.dat deleted file mode 100644 index d0d1e3c4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cy.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/cy_GB.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/cy_GB.dat deleted file mode 100644 index f3febac6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/cy_GB.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/da.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/da.dat deleted file mode 100644 index 43df93f9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/da.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/da_DK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/da_DK.dat deleted file mode 100644 index dbd06720..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/da_DK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/da_GL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/da_GL.dat deleted file mode 100644 index ce46da12..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/da_GL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dav.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dav.dat deleted file mode 100644 index 8b63c2b6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dav.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dav_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dav_KE.dat deleted file mode 100644 index 5e3fbf48..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dav_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/de.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/de.dat deleted file mode 100644 index 7cf17cc2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/de.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/de_AT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/de_AT.dat deleted file mode 100644 index c8b9b62e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/de_AT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/de_BE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/de_BE.dat deleted file mode 100644 index 806b2914..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/de_BE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/de_CH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/de_CH.dat deleted file mode 100644 index 9f50b2e1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/de_CH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/de_DE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/de_DE.dat deleted file mode 100644 index 1d769ac8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/de_DE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/de_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/de_IT.dat deleted file mode 100644 index 7d7b9d2d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/de_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/de_LI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/de_LI.dat deleted file mode 100644 index f4f1a353..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/de_LI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/de_LU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/de_LU.dat deleted file mode 100644 index ed9d8fb7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/de_LU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dje.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dje.dat deleted file mode 100644 index aafbdbda..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dje.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dje_NE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dje_NE.dat deleted file mode 100644 index 605bb83a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dje_NE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/doi.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/doi.dat deleted file mode 100644 index 44b79c89..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/doi.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/doi_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/doi_IN.dat deleted file mode 100644 index 550ec0ec..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/doi_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dsb.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dsb.dat deleted file mode 100644 index 4a954bde..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dsb.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dsb_DE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dsb_DE.dat deleted file mode 100644 index f1a8d4bc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dsb_DE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dua.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dua.dat deleted file mode 100644 index 24ee4953..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dua.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dua_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dua_CM.dat deleted file mode 100644 index 5eab8c9b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dua_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dv.dat deleted file mode 100644 index f8c88ff8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dv_MV.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dv_MV.dat deleted file mode 100644 index 6a6048b5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dv_MV.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dyo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dyo.dat deleted file mode 100644 index 836d954d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dyo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dyo_SN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dyo_SN.dat deleted file mode 100644 index 734192ce..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dyo_SN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dz.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dz.dat deleted file mode 100644 index a75ae5bd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dz.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/dz_BT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/dz_BT.dat deleted file mode 100644 index 34b2803b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/dz_BT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ebu.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ebu.dat deleted file mode 100644 index d02c22a0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ebu.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ebu_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ebu_KE.dat deleted file mode 100644 index 0abfe0e4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ebu_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ee.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ee.dat deleted file mode 100644 index 9348aa2e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ee.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ee_GH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ee_GH.dat deleted file mode 100644 index 3702d427..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ee_GH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ee_TG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ee_TG.dat deleted file mode 100644 index 5f19ac9f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ee_TG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/el.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/el.dat deleted file mode 100644 index dee09ba5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/el.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/el_CY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/el_CY.dat deleted file mode 100644 index 22fb8748..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/el_CY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/el_GR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/el_GR.dat deleted file mode 100644 index ee095c95..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/el_GR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/el_POLYTON.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/el_POLYTON.dat deleted file mode 100644 index 24a37da5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/el_POLYTON.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en.dat deleted file mode 100644 index 10bee2fa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_001.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_001.dat deleted file mode 100644 index e799826a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_001.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_150.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_150.dat deleted file mode 100644 index 82dd2718..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_150.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_AE.dat deleted file mode 100644 index bd96c499..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_AG.dat deleted file mode 100644 index 16d7fa5a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_AI.dat deleted file mode 100644 index 6f0437d6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_AS.dat deleted file mode 100644 index d309d365..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_AT.dat deleted file mode 100644 index ff190b61..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_AU.dat deleted file mode 100644 index a27ea0f6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_AU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BB.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_BB.dat deleted file mode 100644 index fbcfdd8a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BB.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_BE.dat deleted file mode 100644 index 481689fe..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_BI.dat deleted file mode 100644 index e2ef90f8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_BM.dat deleted file mode 100644 index f4e57b6a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_BS.dat deleted file mode 100644 index aafa96f0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_BW.dat deleted file mode 100644 index 2d083923..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_BZ.dat deleted file mode 100644 index 26326dcb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_BZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_CA.dat deleted file mode 100644 index 4bcd2d24..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_CC.dat deleted file mode 100644 index 1102d2b5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_CH.dat deleted file mode 100644 index bce7db95..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_CK.dat deleted file mode 100644 index e4870c84..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_CM.dat deleted file mode 100644 index 1816a83f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CX.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_CX.dat deleted file mode 100644 index b0a5620b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CX.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_CY.dat deleted file mode 100644 index 54fb2d1a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_CZ.dat deleted file mode 100644 index 9fd95110..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_CZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_DE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_DE.dat deleted file mode 100644 index 3d7ded02..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_DE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_DG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_DG.dat deleted file mode 100644 index 1bdea430..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_DG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_DK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_DK.dat deleted file mode 100644 index cae041ed..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_DK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_DM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_DM.dat deleted file mode 100644 index 00ce5e14..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_DM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt.dat deleted file mode 100644 index 341ac66d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt_US.dat deleted file mode 100644 index 663e8261..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_Dsrt_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ER.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_ER.dat deleted file mode 100644 index 349173e3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ER.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ES.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_ES.dat deleted file mode 100644 index aad2b6d9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ES.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_FI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_FI.dat deleted file mode 100644 index 90a35e72..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_FI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_FJ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_FJ.dat deleted file mode 100644 index e2013194..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_FJ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_FK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_FK.dat deleted file mode 100644 index 3b586dd8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_FK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_FM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_FM.dat deleted file mode 100644 index 4a4d6ab9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_FM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_FR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_FR.dat deleted file mode 100644 index 1141d02e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_FR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GB.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_GB.dat deleted file mode 100644 index ce4687f5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GB.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_GD.dat deleted file mode 100644 index fbd024b4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_GG.dat deleted file mode 100644 index 6195e7d8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_GH.dat deleted file mode 100644 index bfcd6f57..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_GI.dat deleted file mode 100644 index 98b852ea..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_GM.dat deleted file mode 100644 index 4e7daad8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_GS.dat deleted file mode 100644 index 3438bbd5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_GU.dat deleted file mode 100644 index 6d184675..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_GY.dat deleted file mode 100644 index 4192d41b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_GY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_HK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_HK.dat deleted file mode 100644 index 39a6d7f6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_HK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_HU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_HU.dat deleted file mode 100644 index 5c6cf340..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_HU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ID.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_ID.dat deleted file mode 100644 index 763ffdf5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ID.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_IE.dat deleted file mode 100644 index 36b4ffb1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_IL.dat deleted file mode 100644 index 9782e31a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_IM.dat deleted file mode 100644 index a3789694..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_IN.dat deleted file mode 100644 index 03987cb1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_IO.dat deleted file mode 100644 index ce4dba27..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_IT.dat deleted file mode 100644 index cd577914..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_JE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_JE.dat deleted file mode 100644 index 59fad126..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_JE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_JM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_JM.dat deleted file mode 100644 index 8bb03e32..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_JM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_KE.dat deleted file mode 100644 index 5c181679..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_KI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_KI.dat deleted file mode 100644 index 2c57a160..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_KI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_KN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_KN.dat deleted file mode 100644 index 79c9a818..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_KN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_KY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_KY.dat deleted file mode 100644 index 013312f6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_KY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_LC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_LC.dat deleted file mode 100644 index 0368fe36..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_LC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_LR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_LR.dat deleted file mode 100644 index 80d77d8c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_LR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_LS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_LS.dat deleted file mode 100644 index b3cbbfb6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_LS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_MG.dat deleted file mode 100644 index cf3db95a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_MH.dat deleted file mode 100644 index f6bc501c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_MO.dat deleted file mode 100644 index 6635dc53..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MP.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_MP.dat deleted file mode 100644 index ede9c2bb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MP.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_MS.dat deleted file mode 100644 index 53115eb5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_MT.dat deleted file mode 100644 index 87c2b953..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_MU.dat deleted file mode 100644 index 524b3ac9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MV.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_MV.dat deleted file mode 100644 index 852a33d9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MV.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_MW.dat deleted file mode 100644 index d0dcd6a9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_MY.dat deleted file mode 100644 index 9479385f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_MY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_NA.dat deleted file mode 100644 index a3d88293..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_NF.dat deleted file mode 100644 index 8003f609..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_NG.dat deleted file mode 100644 index 27bae075..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_NL.dat deleted file mode 100644 index 8cd57735..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_NO.dat deleted file mode 100644 index 0917f9e9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_NR.dat deleted file mode 100644 index 0c788a8d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_NU.dat deleted file mode 100644 index f8607f64..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_NZ.dat deleted file mode 100644 index 7220c4cc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_NZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_PG.dat deleted file mode 100644 index 4269e331..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_PH.dat deleted file mode 100644 index 9ce4fc0b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_PK.dat deleted file mode 100644 index f198d9ec..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_PL.dat deleted file mode 100644 index 02e4c0f5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_PN.dat deleted file mode 100644 index 9977c743..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_PR.dat deleted file mode 100644 index cdb83dbe..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_PT.dat deleted file mode 100644 index 4d7952e7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_PW.dat deleted file mode 100644 index e878083f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_PW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_RO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_RO.dat deleted file mode 100644 index ca1e262f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_RO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_RW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_RW.dat deleted file mode 100644 index c46733b0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_RW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SB.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SB.dat deleted file mode 100644 index b81625e3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SB.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SC.dat deleted file mode 100644 index 4816bb86..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SD.dat deleted file mode 100644 index 985e3e6b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SE.dat deleted file mode 100644 index 5f89bad5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SG.dat deleted file mode 100644 index a21a72a7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SH.dat deleted file mode 100644 index 006487c3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SI.dat deleted file mode 100644 index 97b42d04..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SK.dat deleted file mode 100644 index 1bda1791..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SL.dat deleted file mode 100644 index 127d1285..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SS.dat deleted file mode 100644 index 7192fd13..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SX.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SX.dat deleted file mode 100644 index 5dfff7d8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SX.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_SZ.dat deleted file mode 100644 index 90a9f7a1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_SZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw.dat deleted file mode 100644 index 60936ea8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw_GB.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw_GB.dat deleted file mode 100644 index 214e5c61..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_Shaw_GB.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_TC.dat deleted file mode 100644 index d1edd041..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_TK.dat deleted file mode 100644 index d81803f7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_TO.dat deleted file mode 100644 index 4512ee44..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_TT.dat deleted file mode 100644 index e704f065..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TV.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_TV.dat deleted file mode 100644 index c936c695..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TV.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_TZ.dat deleted file mode 100644 index 62091cee..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_UG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_UG.dat deleted file mode 100644 index dd077cca..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_UG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_UM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_UM.dat deleted file mode 100644 index 3a027b2a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_UM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_US.dat deleted file mode 100644 index 663e8261..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_US_POSIX.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_US_POSIX.dat deleted file mode 100644 index a9ecf5b4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_US_POSIX.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_VC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_VC.dat deleted file mode 100644 index 86fb17d9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_VC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_VG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_VG.dat deleted file mode 100644 index 3fdd46cb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_VG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_VI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_VI.dat deleted file mode 100644 index c4520cc6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_VI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_VU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_VU.dat deleted file mode 100644 index 12647a8c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_VU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_WS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_WS.dat deleted file mode 100644 index 8531d311..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_WS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_ZA.dat deleted file mode 100644 index 8cc6b0e0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ZM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_ZM.dat deleted file mode 100644 index 50039250..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ZM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ZW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/en_ZW.dat deleted file mode 100644 index 6a8835f2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/en_ZW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/eo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/eo.dat deleted file mode 100644 index 84ba5ece..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/eo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/eo_001.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/eo_001.dat deleted file mode 100644 index ec48fb1f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/eo_001.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es.dat deleted file mode 100644 index 0a7e6ed7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_419.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_419.dat deleted file mode 100644 index 38c8aa5b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_419.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_AR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_AR.dat deleted file mode 100644 index 42787bdf..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_AR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_BO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_BO.dat deleted file mode 100644 index df6a1299..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_BO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_BR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_BR.dat deleted file mode 100644 index 7d1f1023..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_BR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_BZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_BZ.dat deleted file mode 100644 index ff0ff6ab..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_BZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_CL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_CL.dat deleted file mode 100644 index 87cd6243..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_CL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_CO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_CO.dat deleted file mode 100644 index 09e03e3a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_CO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_CR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_CR.dat deleted file mode 100644 index 041296d1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_CR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_CU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_CU.dat deleted file mode 100644 index ced5b381..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_CU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_DO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_DO.dat deleted file mode 100644 index cbec7812..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_DO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_EA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_EA.dat deleted file mode 100644 index dfd0d910..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_EA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_EC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_EC.dat deleted file mode 100644 index 0c6f37b6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_EC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_ES.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_ES.dat deleted file mode 100644 index 3414dff2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_ES.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_GQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_GQ.dat deleted file mode 100644 index 775e43b0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_GQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_GT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_GT.dat deleted file mode 100644 index bedab31c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_GT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_HN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_HN.dat deleted file mode 100644 index c5223aae..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_HN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_IC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_IC.dat deleted file mode 100644 index 3a9f9fd9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_IC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_MX.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_MX.dat deleted file mode 100644 index 91919c15..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_MX.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_NI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_NI.dat deleted file mode 100644 index 9e1ab78f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_NI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_PA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_PA.dat deleted file mode 100644 index edfcd545..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_PA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_PE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_PE.dat deleted file mode 100644 index 82c50ad2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_PE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_PH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_PH.dat deleted file mode 100644 index 91e24f13..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_PH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_PR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_PR.dat deleted file mode 100644 index c45d5911..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_PR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_PY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_PY.dat deleted file mode 100644 index 3f365705..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_PY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_SV.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_SV.dat deleted file mode 100644 index 2c98032c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_SV.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_US.dat deleted file mode 100644 index 03388117..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_UY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_UY.dat deleted file mode 100644 index 292fa0cc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_UY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/es_VE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/es_VE.dat deleted file mode 100644 index 42cba455..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/es_VE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/et.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/et.dat deleted file mode 100644 index c242fb8e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/et.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/et_EE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/et_EE.dat deleted file mode 100644 index a08c6824..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/et_EE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/eu.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/eu.dat deleted file mode 100644 index e01ff5be..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/eu.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/eu_ES.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/eu_ES.dat deleted file mode 100644 index 62f7cc61..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/eu_ES.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ewo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ewo.dat deleted file mode 100644 index 5f7d570b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ewo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ewo_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ewo_CM.dat deleted file mode 100644 index ae6b0f7a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ewo_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fa.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fa.dat deleted file mode 100644 index 5a0663af..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fa.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fa_AF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fa_AF.dat deleted file mode 100644 index 689b4c78..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fa_AF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fa_IR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fa_IR.dat deleted file mode 100644 index b3dd8eb5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fa_IR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff.dat deleted file mode 100644 index 531fffa9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm.dat deleted file mode 100644 index 5c7dc646..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_BF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_BF.dat deleted file mode 100644 index 84f0ffbd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_BF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_CM.dat deleted file mode 100644 index 78bf6208..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GH.dat deleted file mode 100644 index 6667263b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GM.dat deleted file mode 100644 index e641b279..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GN.dat deleted file mode 100644 index 216df0ba..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GW.dat deleted file mode 100644 index 63799caa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_GW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_LR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_LR.dat deleted file mode 100644 index f5e3cdeb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_LR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_MR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_MR.dat deleted file mode 100644 index 716f23fd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_MR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NE.dat deleted file mode 100644 index 12098834..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NG.dat deleted file mode 100644 index a17d85c5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SL.dat deleted file mode 100644 index d8825521..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SN.dat deleted file mode 100644 index 125d9994..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Adlm_SN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn.dat deleted file mode 100644 index df806dd4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_BF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_BF.dat deleted file mode 100644 index bb3a2e0e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_BF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_CM.dat deleted file mode 100644 index a3229cdc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GH.dat deleted file mode 100644 index 58222300..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GM.dat deleted file mode 100644 index 82022dfa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GN.dat deleted file mode 100644 index 1a99d48f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GW.dat deleted file mode 100644 index a2b55bf7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_GW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_LR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_LR.dat deleted file mode 100644 index 5dce447e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_LR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_MR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_MR.dat deleted file mode 100644 index e6178b86..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_MR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NE.dat deleted file mode 100644 index 82151e8f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NG.dat deleted file mode 100644 index 086889e6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SL.dat deleted file mode 100644 index e50753de..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SN.dat deleted file mode 100644 index faeff87c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ff_Latn_SN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fi.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fi.dat deleted file mode 100644 index 113e510d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fi.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fi_FI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fi_FI.dat deleted file mode 100644 index fa20daec..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fi_FI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fil.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fil.dat deleted file mode 100644 index 03bcac01..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fil.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fil_PH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fil_PH.dat deleted file mode 100644 index dc08338c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fil_PH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fo.dat deleted file mode 100644 index 7a0b53f6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fo_DK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fo_DK.dat deleted file mode 100644 index 669088d7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fo_DK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fo_FO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fo_FO.dat deleted file mode 100644 index 7d8a8145..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fo_FO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr.dat deleted file mode 100644 index fb8c3a1d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BE.dat deleted file mode 100644 index cc842fe0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BF.dat deleted file mode 100644 index ba1ecc57..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BI.dat deleted file mode 100644 index d4036bed..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BJ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BJ.dat deleted file mode 100644 index 562758e8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BJ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BL.dat deleted file mode 100644 index e311ee66..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_BL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CA.dat deleted file mode 100644 index da870066..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CD.dat deleted file mode 100644 index 24827887..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CF.dat deleted file mode 100644 index 2468e1ed..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CG.dat deleted file mode 100644 index b7df1a92..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CH.dat deleted file mode 100644 index fa5b7b47..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CI.dat deleted file mode 100644 index 3d92afcf..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CM.dat deleted file mode 100644 index a779b4a7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_DJ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_DJ.dat deleted file mode 100644 index 79aa6e67..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_DJ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_DZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_DZ.dat deleted file mode 100644 index 3f56d231..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_DZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_FR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_FR.dat deleted file mode 100644 index 553d5e4a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_FR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GA.dat deleted file mode 100644 index beb0dd56..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GF.dat deleted file mode 100644 index eb9ffc90..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GN.dat deleted file mode 100644 index c4f9f808..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GP.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GP.dat deleted file mode 100644 index e2834c7d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GP.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GQ.dat deleted file mode 100644 index a04de173..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_GQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_HT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_HT.dat deleted file mode 100644 index 33b39563..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_HT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_KM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_KM.dat deleted file mode 100644 index 9ece5936..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_KM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_LU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_LU.dat deleted file mode 100644 index 062b9ab0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_LU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MA.dat deleted file mode 100644 index 6ee8eb9f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MC.dat deleted file mode 100644 index 52315a3d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MF.dat deleted file mode 100644 index e5a1df0e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MG.dat deleted file mode 100644 index c01d050a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_ML.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_ML.dat deleted file mode 100644 index 721771ea..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_ML.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MQ.dat deleted file mode 100644 index 90becb76..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MR.dat deleted file mode 100644 index 28ef3f14..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MU.dat deleted file mode 100644 index 76fc2ab4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_MU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_NC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_NC.dat deleted file mode 100644 index 6aaf0342..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_NC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_NE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_NE.dat deleted file mode 100644 index 143ac4fd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_NE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_PF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_PF.dat deleted file mode 100644 index e2f11dcd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_PF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_PM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_PM.dat deleted file mode 100644 index 4f9cdf7b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_PM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_RE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_RE.dat deleted file mode 100644 index 214ad4fa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_RE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_RW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_RW.dat deleted file mode 100644 index 6739c522..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_RW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_SC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_SC.dat deleted file mode 100644 index 1fedefe5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_SC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_SN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_SN.dat deleted file mode 100644 index 48207856..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_SN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_SY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_SY.dat deleted file mode 100644 index 8dd53967..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_SY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_TD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_TD.dat deleted file mode 100644 index 6abebabd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_TD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_TG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_TG.dat deleted file mode 100644 index 9395eaaf..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_TG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_TN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_TN.dat deleted file mode 100644 index bf1e5515..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_TN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_VU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_VU.dat deleted file mode 100644 index f29eeed9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_VU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_WF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_WF.dat deleted file mode 100644 index 2d38ac6f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_WF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_YT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fr_YT.dat deleted file mode 100644 index 9e9ba28c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fr_YT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/frr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/frr.dat deleted file mode 100644 index a6b2c748..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/frr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/frr_DE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/frr_DE.dat deleted file mode 100644 index ecab3304..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/frr_DE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fur.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fur.dat deleted file mode 100644 index 81a28ed2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fur.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fur_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fur_IT.dat deleted file mode 100644 index 2c40e5c0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fur_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fy.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fy.dat deleted file mode 100644 index 1036ee09..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fy.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/fy_NL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/fy_NL.dat deleted file mode 100644 index 1a8f5811..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/fy_NL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ga.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ga.dat deleted file mode 100644 index 087b6029..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ga.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ga_GB.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ga_GB.dat deleted file mode 100644 index 7432317e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ga_GB.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ga_IE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ga_IE.dat deleted file mode 100644 index 422b05a5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ga_IE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gaa.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gaa.dat deleted file mode 100644 index 5028b29c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gaa.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gaa_GH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gaa_GH.dat deleted file mode 100644 index 7312993b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gaa_GH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gd.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gd.dat deleted file mode 100644 index 785f8fbf..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gd.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gd_GB.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gd_GB.dat deleted file mode 100644 index e3705785..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gd_GB.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gez.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gez.dat deleted file mode 100644 index c3fe514f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gez.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gez_ER.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gez_ER.dat deleted file mode 100644 index 20c18a52..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gez_ER.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gez_ET.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gez_ET.dat deleted file mode 100644 index a70d43dd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gez_ET.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gl.dat deleted file mode 100644 index 48f3577d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gl_ES.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gl_ES.dat deleted file mode 100644 index d432bb25..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gl_ES.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gn.dat deleted file mode 100644 index 2d6eb9f5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gn_PY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gn_PY.dat deleted file mode 100644 index a217cc7a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gn_PY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gsw.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gsw.dat deleted file mode 100644 index 0d241c69..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gsw.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gsw_CH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gsw_CH.dat deleted file mode 100644 index 39ed5a8c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gsw_CH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gsw_FR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gsw_FR.dat deleted file mode 100644 index 8687c5c1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gsw_FR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gsw_LI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gsw_LI.dat deleted file mode 100644 index 2e72e060..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gsw_LI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gu.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gu.dat deleted file mode 100644 index ceefbb29..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gu.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gu_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gu_IN.dat deleted file mode 100644 index a7071f70..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gu_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/guz.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/guz.dat deleted file mode 100644 index 386e37bc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/guz.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/guz_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/guz_KE.dat deleted file mode 100644 index 3b2c102d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/guz_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gv.dat deleted file mode 100644 index b493494c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/gv_IM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/gv_IM.dat deleted file mode 100644 index 0793d838..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/gv_IM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ha.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ha.dat deleted file mode 100644 index 74e86b98..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ha.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab.dat deleted file mode 100644 index 1395d9e0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_NG.dat deleted file mode 100644 index 0a98ac88..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_SD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_SD.dat deleted file mode 100644 index e33c2b3c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_Arab_SD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_GH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ha_GH.dat deleted file mode 100644 index 74d57805..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_GH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_NE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ha_NE.dat deleted file mode 100644 index d06afa95..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_NE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ha_NG.dat deleted file mode 100644 index 0a98ac88..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ha_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/haw.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/haw.dat deleted file mode 100644 index 8b2b47cd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/haw.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/haw_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/haw_US.dat deleted file mode 100644 index 18706f16..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/haw_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/he.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/he.dat deleted file mode 100644 index defc3ea7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/he.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/he_IL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/he_IL.dat deleted file mode 100644 index 3a0da6c0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/he_IL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hi.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hi.dat deleted file mode 100644 index f4ae1eae..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hi.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hi_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hi_IN.dat deleted file mode 100644 index 9fbfd57d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hi_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn.dat deleted file mode 100644 index 17b23a7d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn_IN.dat deleted file mode 100644 index 9fbfd57d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hi_Latn_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hnj.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hnj.dat deleted file mode 100644 index 07eeb516..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hnj.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp.dat deleted file mode 100644 index c8935a2e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp_US.dat deleted file mode 100644 index 46c37147..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hnj_Hmnp_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hr.dat deleted file mode 100644 index e2a005e8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hr_BA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hr_BA.dat deleted file mode 100644 index 44129abf..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hr_BA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hr_HR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hr_HR.dat deleted file mode 100644 index a0ae43dd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hr_HR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hsb.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hsb.dat deleted file mode 100644 index 40b68464..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hsb.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hsb_DE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hsb_DE.dat deleted file mode 100644 index 5de4a2c3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hsb_DE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ht.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ht.dat deleted file mode 100644 index 153d09f6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ht.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ht_HT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ht_HT.dat deleted file mode 100644 index 48f31169..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ht_HT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hu.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hu.dat deleted file mode 100644 index 143d1f0a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hu.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hu_HU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hu_HU.dat deleted file mode 100644 index 113faede..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hu_HU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hy.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hy.dat deleted file mode 100644 index a331f211..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hy.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/hy_AM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/hy_AM.dat deleted file mode 100644 index fa2eaf7d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/hy_AM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ia.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ia.dat deleted file mode 100644 index c9dab730..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ia.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ia_001.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ia_001.dat deleted file mode 100644 index b0a1770b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ia_001.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/id.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/id.dat deleted file mode 100644 index d9f0472f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/id.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/id_ID.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/id_ID.dat deleted file mode 100644 index cf7c8e5e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/id_ID.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ie.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ie.dat deleted file mode 100644 index a6ae1fe3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ie.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ie_EE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ie_EE.dat deleted file mode 100644 index 91566139..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ie_EE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ig.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ig.dat deleted file mode 100644 index 4f4627c0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ig.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ig_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ig_NG.dat deleted file mode 100644 index 4b5635b8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ig_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ii.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ii.dat deleted file mode 100644 index 93158e63..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ii.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ii_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ii_CN.dat deleted file mode 100644 index ef6effaa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ii_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/io.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/io.dat deleted file mode 100644 index 33aa1fae..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/io.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/io_001.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/io_001.dat deleted file mode 100644 index 964a6ab6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/io_001.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/is.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/is.dat deleted file mode 100644 index b0b107d7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/is.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/is_IS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/is_IS.dat deleted file mode 100644 index b3649a76..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/is_IS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/it.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/it.dat deleted file mode 100644 index f1e55e7f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/it.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/it_CH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/it_CH.dat deleted file mode 100644 index cddfb6b5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/it_CH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/it_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/it_IT.dat deleted file mode 100644 index b9a944aa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/it_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/it_SM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/it_SM.dat deleted file mode 100644 index db8432aa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/it_SM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/it_VA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/it_VA.dat deleted file mode 100644 index 30ecca00..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/it_VA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/iu.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/iu.dat deleted file mode 100644 index ca5b1f7a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/iu.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/iu_CA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/iu_CA.dat deleted file mode 100644 index 61bb5448..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/iu_CA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn.dat deleted file mode 100644 index 20b7cedc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn_CA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn_CA.dat deleted file mode 100644 index 61bb5448..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/iu_Latn_CA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ja.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ja.dat deleted file mode 100644 index 769686f4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ja.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ja_JP.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ja_JP.dat deleted file mode 100644 index f0d4479b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ja_JP.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/jbo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/jbo.dat deleted file mode 100644 index 1bf2dbb9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/jbo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/jbo_001.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/jbo_001.dat deleted file mode 100644 index dcc98aef..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/jbo_001.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/jgo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/jgo.dat deleted file mode 100644 index 1b6230ad..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/jgo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/jgo_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/jgo_CM.dat deleted file mode 100644 index 7a523f3b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/jgo_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/jmc.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/jmc.dat deleted file mode 100644 index a4abf969..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/jmc.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/jmc_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/jmc_TZ.dat deleted file mode 100644 index 6abba327..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/jmc_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/jv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/jv.dat deleted file mode 100644 index db18e761..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/jv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/jv_ID.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/jv_ID.dat deleted file mode 100644 index b60dc4db..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/jv_ID.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ka.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ka.dat deleted file mode 100644 index c6dba51a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ka.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ka_GE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ka_GE.dat deleted file mode 100644 index 73278fab..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ka_GE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kaa.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kaa.dat deleted file mode 100644 index e6b6abdb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kaa.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl.dat deleted file mode 100644 index 085710f6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl_UZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl_UZ.dat deleted file mode 100644 index b2e4590f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Cyrl_UZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn.dat deleted file mode 100644 index 085710f6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn_UZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn_UZ.dat deleted file mode 100644 index b2e4590f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kaa_Latn_UZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kab.dat deleted file mode 100644 index b95fcc72..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kab_DZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kab_DZ.dat deleted file mode 100644 index c0cbad8b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kab_DZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kaj.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kaj.dat deleted file mode 100644 index 8d304e93..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kaj.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kaj_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kaj_NG.dat deleted file mode 100644 index a94d6f7c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kaj_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kam.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kam.dat deleted file mode 100644 index 2ef195ab..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kam.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kam_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kam_KE.dat deleted file mode 100644 index d16ca2a1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kam_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kcg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kcg.dat deleted file mode 100644 index 3ff14c35..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kcg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kcg_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kcg_NG.dat deleted file mode 100644 index 1e6dc36b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kcg_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kde.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kde.dat deleted file mode 100644 index 6d51d911..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kde.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kde_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kde_TZ.dat deleted file mode 100644 index 2fcf1d47..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kde_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kea.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kea.dat deleted file mode 100644 index 42d05295..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kea.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kea_CV.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kea_CV.dat deleted file mode 100644 index da97962e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kea_CV.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ken.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ken.dat deleted file mode 100644 index 57e91a15..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ken.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ken_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ken_CM.dat deleted file mode 100644 index 9ef61800..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ken_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kgp.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kgp.dat deleted file mode 100644 index cfe272f7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kgp.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kgp_BR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kgp_BR.dat deleted file mode 100644 index 44b499db..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kgp_BR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/khq.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/khq.dat deleted file mode 100644 index 61cc45f5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/khq.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/khq_ML.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/khq_ML.dat deleted file mode 100644 index 91f7da18..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/khq_ML.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ki.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ki.dat deleted file mode 100644 index 0a7eb8d1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ki.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ki_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ki_KE.dat deleted file mode 100644 index 901b9214..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ki_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kk.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kk.dat deleted file mode 100644 index 19133c62..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kk.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab.dat deleted file mode 100644 index 8dce5fc9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab_CN.dat deleted file mode 100644 index d8b9432a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Arab_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl.dat deleted file mode 100644 index 082c7545..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl_KZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl_KZ.dat deleted file mode 100644 index ca827ac1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kk_Cyrl_KZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kk_KZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kk_KZ.dat deleted file mode 100644 index ca827ac1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kk_KZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kkj.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kkj.dat deleted file mode 100644 index 529c4175..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kkj.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kkj_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kkj_CM.dat deleted file mode 100644 index 231f08bd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kkj_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kl.dat deleted file mode 100644 index d82a25bb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kl_GL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kl_GL.dat deleted file mode 100644 index a8fb28dc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kl_GL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kln.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kln.dat deleted file mode 100644 index 65e435cb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kln.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kln_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kln_KE.dat deleted file mode 100644 index 9a12beb4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kln_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/km.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/km.dat deleted file mode 100644 index 52d3fc4f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/km.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/km_KH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/km_KH.dat deleted file mode 100644 index 2ccaba66..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/km_KH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kn.dat deleted file mode 100644 index 85aaaec0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kn_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kn_IN.dat deleted file mode 100644 index 1f0d6462..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kn_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ko.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ko.dat deleted file mode 100644 index 765f2fcb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ko.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ko_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ko_CN.dat deleted file mode 100644 index 170698df..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ko_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ko_KP.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ko_KP.dat deleted file mode 100644 index 7c76309a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ko_KP.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ko_KR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ko_KR.dat deleted file mode 100644 index 4af3b628..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ko_KR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kok.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kok.dat deleted file mode 100644 index f01857f2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kok.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva.dat deleted file mode 100644 index e92bf9d5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva_IN.dat deleted file mode 100644 index 8fff8b19..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Deva_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn.dat deleted file mode 100644 index d3fbcce6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn_IN.dat deleted file mode 100644 index 8fff8b19..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kok_Latn_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kpe.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kpe.dat deleted file mode 100644 index 48cf0bf5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kpe.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kpe_GN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kpe_GN.dat deleted file mode 100644 index d333167e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kpe_GN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kpe_LR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kpe_LR.dat deleted file mode 100644 index 8afe47b9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kpe_LR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ks.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ks.dat deleted file mode 100644 index 96146c2c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ks.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab.dat deleted file mode 100644 index 6441efed..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab_IN.dat deleted file mode 100644 index 02a5d116..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Arab_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva.dat deleted file mode 100644 index e6380926..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva_IN.dat deleted file mode 100644 index 02a5d116..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ks_Deva_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ksb.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ksb.dat deleted file mode 100644 index 7ced57e8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ksb.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ksb_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ksb_TZ.dat deleted file mode 100644 index 6837e375..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ksb_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ksf.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ksf.dat deleted file mode 100644 index 7fba2526..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ksf.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ksf_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ksf_CM.dat deleted file mode 100644 index fc29c498..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ksf_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ksh.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ksh.dat deleted file mode 100644 index b7bb1f17..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ksh.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ksh_DE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ksh_DE.dat deleted file mode 100644 index 9318d48d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ksh_DE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ku.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ku.dat deleted file mode 100644 index 1bc6fa68..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ku.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ku_TR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ku_TR.dat deleted file mode 100644 index 206426aa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ku_TR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kw.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kw.dat deleted file mode 100644 index 3d103e37..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kw.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kw_GB.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kw_GB.dat deleted file mode 100644 index da81c10f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kw_GB.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kxv.dat deleted file mode 100644 index 2849d6cc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva.dat deleted file mode 100644 index 184addcd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva_IN.dat deleted file mode 100644 index 8a1e2898..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Deva_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn.dat deleted file mode 100644 index 35b2695d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn_IN.dat deleted file mode 100644 index 8a1e2898..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Latn_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya.dat deleted file mode 100644 index 98172abd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya_IN.dat deleted file mode 100644 index 8a1e2898..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Orya_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu.dat deleted file mode 100644 index 1b3ac8b8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu_IN.dat deleted file mode 100644 index 8a1e2898..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/kxv_Telu_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ky.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ky.dat deleted file mode 100644 index 5bd19e57..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ky.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ky_KG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ky_KG.dat deleted file mode 100644 index c61073fe..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ky_KG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/la.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/la.dat deleted file mode 100644 index 1ccac42f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/la.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/la_VA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/la_VA.dat deleted file mode 100644 index 31a68ac9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/la_VA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lag.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lag.dat deleted file mode 100644 index f8da045f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lag.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lag_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lag_TZ.dat deleted file mode 100644 index 7893a0ed..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lag_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lb.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lb.dat deleted file mode 100644 index 0d3dc7d0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lb.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lb_LU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lb_LU.dat deleted file mode 100644 index bcb56532..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lb_LU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lg.dat deleted file mode 100644 index cd4e5a9d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lg_UG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lg_UG.dat deleted file mode 100644 index 6ee321dc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lg_UG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lij.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lij.dat deleted file mode 100644 index 8a7414e9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lij.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lij_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lij_IT.dat deleted file mode 100644 index 6969b528..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lij_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lkt.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lkt.dat deleted file mode 100644 index 515ac6c4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lkt.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lkt_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lkt_US.dat deleted file mode 100644 index 44a5969a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lkt_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lld.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lld.dat deleted file mode 100644 index 04c08068..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lld.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lld_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lld_IT.dat deleted file mode 100644 index f8d12f0f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lld_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lmo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lmo.dat deleted file mode 100644 index c7388c7d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lmo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lmo_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lmo_IT.dat deleted file mode 100644 index 50dd5927..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lmo_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ln.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ln.dat deleted file mode 100644 index cc8edb15..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ln.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ln_AO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ln_AO.dat deleted file mode 100644 index 60dd0ba8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ln_AO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ln_CD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ln_CD.dat deleted file mode 100644 index b87aad7d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ln_CD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ln_CF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ln_CF.dat deleted file mode 100644 index b5a9a827..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ln_CF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ln_CG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ln_CG.dat deleted file mode 100644 index 4059b369..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ln_CG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lo.dat deleted file mode 100644 index 754556b8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lo_LA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lo_LA.dat deleted file mode 100644 index 6f12ab55..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lo_LA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lrc.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lrc.dat deleted file mode 100644 index 4452aa52..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lrc.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lrc_IQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lrc_IQ.dat deleted file mode 100644 index b7fdaf27..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lrc_IQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lrc_IR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lrc_IR.dat deleted file mode 100644 index 75d89752..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lrc_IR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lt.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lt.dat deleted file mode 100644 index 3c64a6d8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lt.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lt_LT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lt_LT.dat deleted file mode 100644 index abd28f77..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lt_LT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ltg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ltg.dat deleted file mode 100644 index 8937b943..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ltg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ltg_LV.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ltg_LV.dat deleted file mode 100644 index 6afb1517..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ltg_LV.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lu.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lu.dat deleted file mode 100644 index 438bb41b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lu.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lu_CD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lu_CD.dat deleted file mode 100644 index b2f405cc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lu_CD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/luo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/luo.dat deleted file mode 100644 index 1fbbe68e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/luo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/luo_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/luo_KE.dat deleted file mode 100644 index 9ad4a6a4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/luo_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/luy.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/luy.dat deleted file mode 100644 index c82c8929..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/luy.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/luy_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/luy_KE.dat deleted file mode 100644 index e1b346e4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/luy_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lv.dat deleted file mode 100644 index 9d4a804a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/lv_LV.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/lv_LV.dat deleted file mode 100644 index 37bb4e26..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/lv_LV.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mai.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mai.dat deleted file mode 100644 index bde3b353..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mai.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mai_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mai_IN.dat deleted file mode 100644 index 288b92f8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mai_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mas.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mas.dat deleted file mode 100644 index 7e254ed9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mas.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mas_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mas_KE.dat deleted file mode 100644 index b264c837..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mas_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mas_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mas_TZ.dat deleted file mode 100644 index d5c71c37..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mas_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mdf.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mdf.dat deleted file mode 100644 index 5d4d3f83..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mdf.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mdf_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mdf_RU.dat deleted file mode 100644 index 94571359..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mdf_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mer.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mer.dat deleted file mode 100644 index f6657fe1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mer.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mer_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mer_KE.dat deleted file mode 100644 index a7c286f7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mer_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mfe.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mfe.dat deleted file mode 100644 index 10eef776..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mfe.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mfe_MU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mfe_MU.dat deleted file mode 100644 index 2f4715ba..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mfe_MU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mg.dat deleted file mode 100644 index f91a1dd1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mg_MG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mg_MG.dat deleted file mode 100644 index 9e18e6cb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mg_MG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mgh.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mgh.dat deleted file mode 100644 index e8a28060..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mgh.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mgh_MZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mgh_MZ.dat deleted file mode 100644 index 999e8efb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mgh_MZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mgo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mgo.dat deleted file mode 100644 index edbda979..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mgo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mgo_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mgo_CM.dat deleted file mode 100644 index 667c0172..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mgo_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mhn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mhn.dat deleted file mode 100644 index 1a3303d9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mhn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mhn_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mhn_IT.dat deleted file mode 100644 index c6472055..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mhn_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mi.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mi.dat deleted file mode 100644 index 79eb095a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mi.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mi_NZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mi_NZ.dat deleted file mode 100644 index 7c8b4d19..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mi_NZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mic.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mic.dat deleted file mode 100644 index 3ca1892e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mic.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mic_CA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mic_CA.dat deleted file mode 100644 index f43465a9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mic_CA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mk.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mk.dat deleted file mode 100644 index 81cc0072..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mk.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mk_MK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mk_MK.dat deleted file mode 100644 index 33207d9a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mk_MK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ml.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ml.dat deleted file mode 100644 index f5e230e3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ml.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ml_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ml_IN.dat deleted file mode 100644 index 5f89f111..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ml_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mn.dat deleted file mode 100644 index e314523d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mn_MN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mn_MN.dat deleted file mode 100644 index 096cb104..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mn_MN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong.dat deleted file mode 100644 index 287f8526..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_CN.dat deleted file mode 100644 index 284cbee8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_MN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_MN.dat deleted file mode 100644 index ad54681c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mn_Mong_MN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mni.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mni.dat deleted file mode 100644 index 4ebcfc59..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mni.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng.dat deleted file mode 100644 index de357a87..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng_IN.dat deleted file mode 100644 index 28787ccd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Beng_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei.dat deleted file mode 100644 index 654042f8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei_IN.dat deleted file mode 100644 index 28787ccd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mni_Mtei_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/moh.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/moh.dat deleted file mode 100644 index c301f3f7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/moh.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/moh_CA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/moh_CA.dat deleted file mode 100644 index ba2eddea..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/moh_CA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mr.dat deleted file mode 100644 index 2c7d0d47..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mr_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mr_IN.dat deleted file mode 100644 index a57af72d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mr_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ms.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ms.dat deleted file mode 100644 index b7fed4f6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ms.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab.dat deleted file mode 100644 index 7ee5bee0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_BN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_BN.dat deleted file mode 100644 index 1785baa7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_BN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_MY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_MY.dat deleted file mode 100644 index 5df0cb75..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_Arab_MY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_BN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ms_BN.dat deleted file mode 100644 index 1785baa7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_BN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_ID.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ms_ID.dat deleted file mode 100644 index 07b0e769..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_ID.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_MY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ms_MY.dat deleted file mode 100644 index 5df0cb75..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_MY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_SG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ms_SG.dat deleted file mode 100644 index 33205113..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ms_SG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mt.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mt.dat deleted file mode 100644 index 502594b2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mt.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mt_MT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mt_MT.dat deleted file mode 100644 index 9f4731e4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mt_MT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mua.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mua.dat deleted file mode 100644 index 7d28adf1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mua.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mua_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mua_CM.dat deleted file mode 100644 index 5b71a89b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mua_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mus.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mus.dat deleted file mode 100644 index b323269d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mus.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mus_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mus_US.dat deleted file mode 100644 index a506b8bd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mus_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/my.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/my.dat deleted file mode 100644 index 8bb745b2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/my.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/my_MM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/my_MM.dat deleted file mode 100644 index 33bc9111..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/my_MM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/myv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/myv.dat deleted file mode 100644 index 896bdd71..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/myv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/myv_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/myv_RU.dat deleted file mode 100644 index de64fee6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/myv_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mzn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mzn.dat deleted file mode 100644 index 51d9f132..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mzn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/mzn_IR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/mzn_IR.dat deleted file mode 100644 index 73990744..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/mzn_IR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/naq.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/naq.dat deleted file mode 100644 index 786b2ffe..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/naq.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/naq_NA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/naq_NA.dat deleted file mode 100644 index 82c109a0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/naq_NA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nb.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nb.dat deleted file mode 100644 index 5abc2536..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nb.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nb_NO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nb_NO.dat deleted file mode 100644 index fc57a39a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nb_NO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nb_SJ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nb_SJ.dat deleted file mode 100644 index 9af3a275..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nb_SJ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nd.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nd.dat deleted file mode 100644 index c13781ab..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nd.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nd_ZW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nd_ZW.dat deleted file mode 100644 index bb86d3f6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nd_ZW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nds.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nds.dat deleted file mode 100644 index e1c76cae..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nds.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nds_DE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nds_DE.dat deleted file mode 100644 index 771fba3e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nds_DE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nds_NL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nds_NL.dat deleted file mode 100644 index 14b9b123..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nds_NL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ne.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ne.dat deleted file mode 100644 index 228d625f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ne.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ne_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ne_IN.dat deleted file mode 100644 index 647efe6d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ne_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ne_NP.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ne_NP.dat deleted file mode 100644 index 5c513db9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ne_NP.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nl.dat deleted file mode 100644 index ae4d8576..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_AW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nl_AW.dat deleted file mode 100644 index 0bd826c4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_AW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_BE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nl_BE.dat deleted file mode 100644 index 4023b7a2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_BE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_BQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nl_BQ.dat deleted file mode 100644 index 8f686f79..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_BQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_CW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nl_CW.dat deleted file mode 100644 index ea3eb94f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_CW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_NL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nl_NL.dat deleted file mode 100644 index c686473e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_NL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_SR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nl_SR.dat deleted file mode 100644 index 6ffb0f92..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_SR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_SX.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nl_SX.dat deleted file mode 100644 index 6932aa19..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nl_SX.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nmg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nmg.dat deleted file mode 100644 index 1e508106..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nmg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nmg_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nmg_CM.dat deleted file mode 100644 index f675485d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nmg_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nn.dat deleted file mode 100644 index fde5bfc9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nn_NO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nn_NO.dat deleted file mode 100644 index 0e233b7d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nn_NO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nnh.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nnh.dat deleted file mode 100644 index 23896790..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nnh.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nnh_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nnh_CM.dat deleted file mode 100644 index 7722c099..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nnh_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/no.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/no.dat deleted file mode 100644 index 6fad909d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/no.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nqo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nqo.dat deleted file mode 100644 index 1f24a4ec..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nqo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nqo_GN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nqo_GN.dat deleted file mode 100644 index 3cdbcbcd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nqo_GN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nr.dat deleted file mode 100644 index 1306c925..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nr_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nr_ZA.dat deleted file mode 100644 index ff0ba0b0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nr_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nso.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nso.dat deleted file mode 100644 index 25529c1a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nso.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nso_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nso_ZA.dat deleted file mode 100644 index 42f9efca..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nso_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nus.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nus.dat deleted file mode 100644 index 6bc93828..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nus.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nus_SS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nus_SS.dat deleted file mode 100644 index b8b86575..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nus_SS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nv.dat deleted file mode 100644 index 146e037b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nv_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nv_US.dat deleted file mode 100644 index 2c78aa76..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nv_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ny.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ny.dat deleted file mode 100644 index e1fc0613..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ny.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ny_MW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ny_MW.dat deleted file mode 100644 index 00392558..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ny_MW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nyn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nyn.dat deleted file mode 100644 index ccd34096..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nyn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/nyn_UG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/nyn_UG.dat deleted file mode 100644 index 2e51c77b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/nyn_UG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/oc.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/oc.dat deleted file mode 100644 index b678c283..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/oc.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/oc_ES.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/oc_ES.dat deleted file mode 100644 index 936a4bac..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/oc_ES.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/oc_FR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/oc_FR.dat deleted file mode 100644 index 613abbe7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/oc_FR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/om.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/om.dat deleted file mode 100644 index c83455ff..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/om.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/om_ET.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/om_ET.dat deleted file mode 100644 index 82c438b4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/om_ET.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/om_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/om_KE.dat deleted file mode 100644 index daf3734d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/om_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/or.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/or.dat deleted file mode 100644 index 7368025a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/or.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/or_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/or_IN.dat deleted file mode 100644 index 12b4f1bc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/or_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/os.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/os.dat deleted file mode 100644 index a90e8198..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/os.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/os_GE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/os_GE.dat deleted file mode 100644 index 205ddbf2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/os_GE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/os_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/os_RU.dat deleted file mode 100644 index f51d3d91..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/os_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/osa.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/osa.dat deleted file mode 100644 index eb1d4ff4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/osa.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/osa_US.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/osa_US.dat deleted file mode 100644 index 03c11002..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/osa_US.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pa.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pa.dat deleted file mode 100644 index 1186ec2f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pa.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab.dat deleted file mode 100644 index 0d02acbd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab_PK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab_PK.dat deleted file mode 100644 index eefdeba1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Arab_PK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru.dat deleted file mode 100644 index ed8c6b6f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru_IN.dat deleted file mode 100644 index 6af3517d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pa_Guru_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pap.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pap.dat deleted file mode 100644 index 68b8dc46..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pap.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pap_AW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pap_AW.dat deleted file mode 100644 index 973cc763..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pap_AW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pap_CW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pap_CW.dat deleted file mode 100644 index 3c481d47..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pap_CW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pcm.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pcm.dat deleted file mode 100644 index 97447ca5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pcm.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pcm_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pcm_NG.dat deleted file mode 100644 index 7ee82746..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pcm_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pis.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pis.dat deleted file mode 100644 index 55726a02..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pis.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pis_SB.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pis_SB.dat deleted file mode 100644 index 3b1f17cd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pis_SB.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pl.dat deleted file mode 100644 index 65ac0a48..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pl_PL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pl_PL.dat deleted file mode 100644 index 7a4339ab..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pl_PL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/prg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/prg.dat deleted file mode 100644 index 06219914..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/prg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/prg_PL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/prg_PL.dat deleted file mode 100644 index 7b197051..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/prg_PL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ps.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ps.dat deleted file mode 100644 index ea7e2e52..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ps.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ps_AF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ps_AF.dat deleted file mode 100644 index 94a50bff..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ps_AF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ps_PK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ps_PK.dat deleted file mode 100644 index 691557ea..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ps_PK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt.dat deleted file mode 100644 index aefae529..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_AO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_AO.dat deleted file mode 100644 index 4cd1fbda..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_AO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_BR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_BR.dat deleted file mode 100644 index 22952c77..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_BR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_CH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_CH.dat deleted file mode 100644 index 26842865..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_CH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_CV.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_CV.dat deleted file mode 100644 index 78e2bbcf..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_CV.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_GQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_GQ.dat deleted file mode 100644 index 88968337..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_GQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_GW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_GW.dat deleted file mode 100644 index d44108a1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_GW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_LU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_LU.dat deleted file mode 100644 index af0b45b3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_LU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_MO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_MO.dat deleted file mode 100644 index 4fcee099..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_MO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_MZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_MZ.dat deleted file mode 100644 index 198075e8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_MZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_PT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_PT.dat deleted file mode 100644 index aa6a6ccb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_PT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_ST.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_ST.dat deleted file mode 100644 index cdccab77..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_ST.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_TL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/pt_TL.dat deleted file mode 100644 index 7bb756cd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/pt_TL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/qu.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/qu.dat deleted file mode 100644 index 85cc9eca..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/qu.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/qu_BO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/qu_BO.dat deleted file mode 100644 index bc994ff7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/qu_BO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/qu_EC.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/qu_EC.dat deleted file mode 100644 index 8f7f46ad..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/qu_EC.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/qu_PE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/qu_PE.dat deleted file mode 100644 index 07aacf94..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/qu_PE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/quc.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/quc.dat deleted file mode 100644 index f7e02d8a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/quc.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/quc_GT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/quc_GT.dat deleted file mode 100644 index c14f849c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/quc_GT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/raj.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/raj.dat deleted file mode 100644 index 8f4477fa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/raj.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/raj_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/raj_IN.dat deleted file mode 100644 index e56e7502..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/raj_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rhg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rhg.dat deleted file mode 100644 index 192bbff0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rhg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg.dat deleted file mode 100644 index 01b82ecf..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_BD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_BD.dat deleted file mode 100644 index cc14c7f8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_BD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_MM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_MM.dat deleted file mode 100644 index 6cfde861..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rhg_Rohg_MM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rif.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rif.dat deleted file mode 100644 index b5efc905..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rif.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rif_MA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rif_MA.dat deleted file mode 100644 index 9b7f4342..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rif_MA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rm.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rm.dat deleted file mode 100644 index 2398d907..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rm.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rm_CH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rm_CH.dat deleted file mode 100644 index 26eccfc3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rm_CH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rn.dat deleted file mode 100644 index f21edd4d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rn_BI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rn_BI.dat deleted file mode 100644 index d1ef9755..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rn_BI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ro.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ro.dat deleted file mode 100644 index efa69b04..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ro.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ro_MD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ro_MD.dat deleted file mode 100644 index 82b8bb6d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ro_MD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ro_RO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ro_RO.dat deleted file mode 100644 index 3517a610..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ro_RO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rof.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rof.dat deleted file mode 100644 index 6f0e5514..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rof.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rof_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rof_TZ.dat deleted file mode 100644 index d7ffaeb0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rof_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/root.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/root.dat deleted file mode 100644 index 441bbdc9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/root.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ru.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ru.dat deleted file mode 100644 index 50c78459..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ru.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_BY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ru_BY.dat deleted file mode 100644 index e8ef98d4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_BY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_KG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ru_KG.dat deleted file mode 100644 index f3e59502..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_KG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_KZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ru_KZ.dat deleted file mode 100644 index b66765a5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_KZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_MD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ru_MD.dat deleted file mode 100644 index d7041994..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_MD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ru_RU.dat deleted file mode 100644 index a1183570..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_UA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ru_UA.dat deleted file mode 100644 index cebd8c23..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ru_UA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rw.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rw.dat deleted file mode 100644 index d767b309..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rw.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rw_RW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rw_RW.dat deleted file mode 100644 index 4ade0dd6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rw_RW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rwk.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rwk.dat deleted file mode 100644 index fd69cc07..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rwk.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/rwk_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/rwk_TZ.dat deleted file mode 100644 index fedafc3e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/rwk_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sa.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sa.dat deleted file mode 100644 index 76f67c4d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sa.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sa_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sa_IN.dat deleted file mode 100644 index 31781751..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sa_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sah.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sah.dat deleted file mode 100644 index bad972f9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sah.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sah_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sah_RU.dat deleted file mode 100644 index 44075f9f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sah_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/saq.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/saq.dat deleted file mode 100644 index 95320833..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/saq.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/saq_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/saq_KE.dat deleted file mode 100644 index ffd91c35..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/saq_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sat.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sat.dat deleted file mode 100644 index 3a63c743..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sat.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva.dat deleted file mode 100644 index 9a1af369..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva_IN.dat deleted file mode 100644 index 1781be05..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Deva_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck.dat deleted file mode 100644 index 444b3584..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck_IN.dat deleted file mode 100644 index 1781be05..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sat_Olck_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sbp.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sbp.dat deleted file mode 100644 index e1ea58b9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sbp.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sbp_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sbp_TZ.dat deleted file mode 100644 index 2292ffb6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sbp_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sc.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sc.dat deleted file mode 100644 index f3609d5c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sc.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sc_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sc_IT.dat deleted file mode 100644 index 12c8c563..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sc_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/scn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/scn.dat deleted file mode 100644 index c8ebaec1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/scn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/scn_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/scn_IT.dat deleted file mode 100644 index 0e8b90ec..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/scn_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sd.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sd.dat deleted file mode 100644 index 410ea581..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sd.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab.dat deleted file mode 100644 index 8003f26f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab_PK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab_PK.dat deleted file mode 100644 index 42f6290f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Arab_PK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva.dat deleted file mode 100644 index 5193db74..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva_IN.dat deleted file mode 100644 index 21aee01c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sd_Deva_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sdh.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sdh.dat deleted file mode 100644 index f51767d1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sdh.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sdh_IQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sdh_IQ.dat deleted file mode 100644 index 774d04d5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sdh_IQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sdh_IR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sdh_IR.dat deleted file mode 100644 index 6e83d1f1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sdh_IR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/se.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/se.dat deleted file mode 100644 index 8da45922..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/se.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/se_FI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/se_FI.dat deleted file mode 100644 index 2f927646..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/se_FI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/se_NO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/se_NO.dat deleted file mode 100644 index 09eca0d2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/se_NO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/se_SE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/se_SE.dat deleted file mode 100644 index 632b54e7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/se_SE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/seh.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/seh.dat deleted file mode 100644 index e61f09ba..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/seh.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/seh_MZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/seh_MZ.dat deleted file mode 100644 index ba30e958..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/seh_MZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ses.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ses.dat deleted file mode 100644 index f9858d96..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ses.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ses_ML.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ses_ML.dat deleted file mode 100644 index a0010889..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ses_ML.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sg.dat deleted file mode 100644 index 8ce0d409..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sg_CF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sg_CF.dat deleted file mode 100644 index 0ca555a6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sg_CF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/shi.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/shi.dat deleted file mode 100644 index f3d10667..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/shi.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn.dat deleted file mode 100644 index fecc8217..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn_MA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn_MA.dat deleted file mode 100644 index 14955853..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Latn_MA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng.dat deleted file mode 100644 index 63ebefe7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng_MA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng_MA.dat deleted file mode 100644 index 14955853..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/shi_Tfng_MA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/shn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/shn.dat deleted file mode 100644 index 7eb56999..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/shn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/shn_MM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/shn_MM.dat deleted file mode 100644 index 3cace292..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/shn_MM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/shn_TH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/shn_TH.dat deleted file mode 100644 index cc5364a2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/shn_TH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/si.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/si.dat deleted file mode 100644 index fba9aa9f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/si.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/si_LK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/si_LK.dat deleted file mode 100644 index eb323029..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/si_LK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sid.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sid.dat deleted file mode 100644 index 3445845d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sid.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sid_ET.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sid_ET.dat deleted file mode 100644 index 1be7a4a9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sid_ET.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sk.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sk.dat deleted file mode 100644 index 413a7038..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sk.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sk_SK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sk_SK.dat deleted file mode 100644 index 4ba6c841..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sk_SK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/skr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/skr.dat deleted file mode 100644 index d9fc4290..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/skr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/skr_PK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/skr_PK.dat deleted file mode 100644 index 8db57bc5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/skr_PK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sl.dat deleted file mode 100644 index 9e805fb2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sl_SI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sl_SI.dat deleted file mode 100644 index b4955823..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sl_SI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sma.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sma.dat deleted file mode 100644 index 925c9565..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sma.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sma_NO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sma_NO.dat deleted file mode 100644 index 325e16cc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sma_NO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sma_SE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sma_SE.dat deleted file mode 100644 index b9cb4e75..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sma_SE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/smj.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/smj.dat deleted file mode 100644 index 377b17c7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/smj.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/smj_NO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/smj_NO.dat deleted file mode 100644 index 7cc36547..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/smj_NO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/smj_SE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/smj_SE.dat deleted file mode 100644 index 78aa9de9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/smj_SE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/smn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/smn.dat deleted file mode 100644 index f63177af..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/smn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/smn_FI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/smn_FI.dat deleted file mode 100644 index a88e29d9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/smn_FI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sms.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sms.dat deleted file mode 100644 index 590f25ad..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sms.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sms_FI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sms_FI.dat deleted file mode 100644 index 91bdf45e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sms_FI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sn.dat deleted file mode 100644 index cb8e1bd8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sn_ZW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sn_ZW.dat deleted file mode 100644 index 4dd1932d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sn_ZW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/so.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/so.dat deleted file mode 100644 index cfae4764..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/so.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/so_DJ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/so_DJ.dat deleted file mode 100644 index 756644b6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/so_DJ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/so_ET.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/so_ET.dat deleted file mode 100644 index abd6c125..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/so_ET.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/so_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/so_KE.dat deleted file mode 100644 index 2c422f93..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/so_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/so_SO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/so_SO.dat deleted file mode 100644 index aa7955c5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/so_SO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sq.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sq.dat deleted file mode 100644 index aa0a941b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sq.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sq_AL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sq_AL.dat deleted file mode 100644 index 5a5320fc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sq_AL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sq_MK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sq_MK.dat deleted file mode 100644 index 32565ce7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sq_MK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sq_XK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sq_XK.dat deleted file mode 100644 index bbe0062e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sq_XK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr.dat deleted file mode 100644 index 0075f8e1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl.dat deleted file mode 100644 index 89154355..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_BA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_BA.dat deleted file mode 100644 index 6a0aaf69..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_BA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_ME.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_ME.dat deleted file mode 100644 index 357ab293..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_ME.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_RS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_RS.dat deleted file mode 100644 index b2633098..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_RS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_XK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_XK.dat deleted file mode 100644 index b574d058..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Cyrl_XK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn.dat deleted file mode 100644 index efd38936..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_BA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_BA.dat deleted file mode 100644 index d1be6b89..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_BA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_ME.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_ME.dat deleted file mode 100644 index b8b9a5b4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_ME.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_RS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_RS.dat deleted file mode 100644 index b2633098..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_RS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_XK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_XK.dat deleted file mode 100644 index 778db676..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sr_Latn_XK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ss.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ss.dat deleted file mode 100644 index 91c10e2c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ss.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ss_SZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ss_SZ.dat deleted file mode 100644 index b4fcf779..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ss_SZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ss_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ss_ZA.dat deleted file mode 100644 index 67315698..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ss_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ssy.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ssy.dat deleted file mode 100644 index a42b5e83..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ssy.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ssy_ER.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ssy_ER.dat deleted file mode 100644 index 1d28855a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ssy_ER.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/st.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/st.dat deleted file mode 100644 index 26d9b21a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/st.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/st_LS.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/st_LS.dat deleted file mode 100644 index 50eef784..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/st_LS.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/st_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/st_ZA.dat deleted file mode 100644 index 9539d624..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/st_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/su.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/su.dat deleted file mode 100644 index 011036d5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/su.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/su_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/su_Latn.dat deleted file mode 100644 index 79e460ed..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/su_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/su_Latn_ID.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/su_Latn_ID.dat deleted file mode 100644 index 5c45bdce..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/su_Latn_ID.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sv.dat deleted file mode 100644 index 1bb77229..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sv_AX.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sv_AX.dat deleted file mode 100644 index bfb414d6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sv_AX.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sv_FI.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sv_FI.dat deleted file mode 100644 index 49deaf31..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sv_FI.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sv_SE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sv_SE.dat deleted file mode 100644 index 57a602a0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sv_SE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sw.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sw.dat deleted file mode 100644 index df6f886f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sw.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sw_CD.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sw_CD.dat deleted file mode 100644 index b1a7f0e5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sw_CD.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sw_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sw_KE.dat deleted file mode 100644 index f19305af..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sw_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sw_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sw_TZ.dat deleted file mode 100644 index a267886d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sw_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/sw_UG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/sw_UG.dat deleted file mode 100644 index 0d160d55..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/sw_UG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/syr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/syr.dat deleted file mode 100644 index 3b0a2453..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/syr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/syr_IQ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/syr_IQ.dat deleted file mode 100644 index 7821e924..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/syr_IQ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/syr_SY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/syr_SY.dat deleted file mode 100644 index 1edf041c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/syr_SY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/szl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/szl.dat deleted file mode 100644 index 4a767b48..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/szl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/szl_PL.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/szl_PL.dat deleted file mode 100644 index f0b99548..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/szl_PL.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ta.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ta.dat deleted file mode 100644 index 7559ff89..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ta.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ta_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ta_IN.dat deleted file mode 100644 index cd08ee8f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ta_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ta_LK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ta_LK.dat deleted file mode 100644 index f473b057..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ta_LK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ta_MY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ta_MY.dat deleted file mode 100644 index 8e791e92..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ta_MY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ta_SG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ta_SG.dat deleted file mode 100644 index 9f1131cc..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ta_SG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/te.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/te.dat deleted file mode 100644 index 436255df..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/te.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/te_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/te_IN.dat deleted file mode 100644 index d76a4160..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/te_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/teo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/teo.dat deleted file mode 100644 index d4cfa1af..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/teo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/teo_KE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/teo_KE.dat deleted file mode 100644 index 66b33cad..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/teo_KE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/teo_UG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/teo_UG.dat deleted file mode 100644 index e81befad..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/teo_UG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tg.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tg.dat deleted file mode 100644 index 03a72512..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tg.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tg_TJ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tg_TJ.dat deleted file mode 100644 index 1982383d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tg_TJ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/th.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/th.dat deleted file mode 100644 index e6884133..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/th.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/th_TH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/th_TH.dat deleted file mode 100644 index c2a4c994..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/th_TH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ti.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ti.dat deleted file mode 100644 index ef311739..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ti.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ti_ER.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ti_ER.dat deleted file mode 100644 index 41ca554c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ti_ER.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ti_ET.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ti_ET.dat deleted file mode 100644 index 50a447b5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ti_ET.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tig.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tig.dat deleted file mode 100644 index ed1b2a79..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tig.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tig_ER.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tig_ER.dat deleted file mode 100644 index 5632fa6f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tig_ER.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tk.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tk.dat deleted file mode 100644 index 46c4af00..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tk.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tk_TM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tk_TM.dat deleted file mode 100644 index 01e7cf61..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tk_TM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tn.dat deleted file mode 100644 index 65c0267f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tn_BW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tn_BW.dat deleted file mode 100644 index 5da0cc5d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tn_BW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tn_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tn_ZA.dat deleted file mode 100644 index a559a128..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tn_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/to.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/to.dat deleted file mode 100644 index 7268ff20..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/to.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/to_TO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/to_TO.dat deleted file mode 100644 index 8cdedf55..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/to_TO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tok.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tok.dat deleted file mode 100644 index a8b7a886..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tok.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tok_001.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tok_001.dat deleted file mode 100644 index ea39a455..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tok_001.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tpi.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tpi.dat deleted file mode 100644 index 0434b6fa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tpi.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tpi_PG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tpi_PG.dat deleted file mode 100644 index 9057c6e4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tpi_PG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tr.dat deleted file mode 100644 index 1bd0819f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tr_CY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tr_CY.dat deleted file mode 100644 index a0bbd6f0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tr_CY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tr_TR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tr_TR.dat deleted file mode 100644 index 8c20ebb6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tr_TR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/trv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/trv.dat deleted file mode 100644 index 1b784bc8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/trv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/trv_TW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/trv_TW.dat deleted file mode 100644 index da4e716d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/trv_TW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/trw.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/trw.dat deleted file mode 100644 index e4a66bd8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/trw.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/trw_PK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/trw_PK.dat deleted file mode 100644 index 593e957e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/trw_PK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ts.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ts.dat deleted file mode 100644 index 900383d3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ts.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ts_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ts_ZA.dat deleted file mode 100644 index 53640320..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ts_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tt.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tt.dat deleted file mode 100644 index 28fe370c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tt.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tt_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tt_RU.dat deleted file mode 100644 index 88ba72f3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tt_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/twq.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/twq.dat deleted file mode 100644 index 2f99122f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/twq.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/twq_NE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/twq_NE.dat deleted file mode 100644 index be95079d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/twq_NE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tyv.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tyv.dat deleted file mode 100644 index fe1564b9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tyv.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tyv_RU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tyv_RU.dat deleted file mode 100644 index 92ab47b5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tyv_RU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tzm.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tzm.dat deleted file mode 100644 index 14f6276b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tzm.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/tzm_MA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/tzm_MA.dat deleted file mode 100644 index b74c9377..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/tzm_MA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ug.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ug.dat deleted file mode 100644 index 28cd5d09..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ug.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ug_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ug_CN.dat deleted file mode 100644 index 997752b7..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ug_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/uk.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/uk.dat deleted file mode 100644 index 908f4870..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/uk.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/uk_UA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/uk_UA.dat deleted file mode 100644 index e1ff80ca..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/uk_UA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ur.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ur.dat deleted file mode 100644 index dd035ccd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ur.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ur_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ur_IN.dat deleted file mode 100644 index 582a1fc3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ur_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ur_PK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ur_PK.dat deleted file mode 100644 index 5a3d6cec..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ur_PK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/uz.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/uz.dat deleted file mode 100644 index 267dd64e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/uz.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab.dat deleted file mode 100644 index 0ed5c3ae..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab_AF.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab_AF.dat deleted file mode 100644 index d7355f66..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Arab_AF.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl.dat deleted file mode 100644 index 382b3cc0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl_UZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl_UZ.dat deleted file mode 100644 index 0e9fb275..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Cyrl_UZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn.dat deleted file mode 100644 index 217235e4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn_UZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn_UZ.dat deleted file mode 100644 index 0e9fb275..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/uz_Latn_UZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vai.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vai.dat deleted file mode 100644 index c69f2cba..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vai.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn.dat deleted file mode 100644 index 849c491d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn_LR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn_LR.dat deleted file mode 100644 index f3bc7f03..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Latn_LR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii.dat deleted file mode 100644 index 6519e24e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii_LR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii_LR.dat deleted file mode 100644 index f3bc7f03..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vai_Vaii_LR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ve.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ve.dat deleted file mode 100644 index 517c7a44..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ve.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/ve_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/ve_ZA.dat deleted file mode 100644 index 81c087b3..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/ve_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vec.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vec.dat deleted file mode 100644 index 59dcdec1..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vec.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vec_IT.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vec_IT.dat deleted file mode 100644 index a1f0238c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vec_IT.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vi.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vi.dat deleted file mode 100644 index e887dca5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vi.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vi_VN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vi_VN.dat deleted file mode 100644 index d1942c8e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vi_VN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vmw.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vmw.dat deleted file mode 100644 index 7116de27..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vmw.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vmw_MZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vmw_MZ.dat deleted file mode 100644 index 21999745..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vmw_MZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vo.dat deleted file mode 100644 index 00137183..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vo_001.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vo_001.dat deleted file mode 100644 index e06ed1c8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vo_001.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vun.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vun.dat deleted file mode 100644 index 8fd175bb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vun.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/vun_TZ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/vun_TZ.dat deleted file mode 100644 index a29794a6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/vun_TZ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/wa.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/wa.dat deleted file mode 100644 index 6699fd21..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/wa.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/wa_BE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/wa_BE.dat deleted file mode 100644 index 63f30c15..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/wa_BE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/wae.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/wae.dat deleted file mode 100644 index 7deede2e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/wae.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/wae_CH.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/wae_CH.dat deleted file mode 100644 index 7e10feea..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/wae_CH.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/wal.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/wal.dat deleted file mode 100644 index 99770a06..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/wal.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/wal_ET.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/wal_ET.dat deleted file mode 100644 index fa5fc6d0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/wal_ET.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/wbp.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/wbp.dat deleted file mode 100644 index 22bcdd77..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/wbp.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/wbp_AU.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/wbp_AU.dat deleted file mode 100644 index 3b6b5b1e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/wbp_AU.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/wo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/wo.dat deleted file mode 100644 index ed881e93..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/wo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/wo_SN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/wo_SN.dat deleted file mode 100644 index ce673ad4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/wo_SN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/xh.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/xh.dat deleted file mode 100644 index 487e18a0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/xh.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/xh_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/xh_ZA.dat deleted file mode 100644 index 68d71668..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/xh_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/xnr.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/xnr.dat deleted file mode 100644 index 3b0118bd..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/xnr.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/xnr_IN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/xnr_IN.dat deleted file mode 100644 index 360f58c4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/xnr_IN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/xog.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/xog.dat deleted file mode 100644 index 365dd8aa..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/xog.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/xog_UG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/xog_UG.dat deleted file mode 100644 index b5fb4da0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/xog_UG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yav.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yav.dat deleted file mode 100644 index 547b6b91..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yav.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yav_CM.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yav_CM.dat deleted file mode 100644 index 2a0cbb26..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yav_CM.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yi.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yi.dat deleted file mode 100644 index c39639f0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yi.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yi_UA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yi_UA.dat deleted file mode 100644 index 9f967db0..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yi_UA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yo.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yo.dat deleted file mode 100644 index a59c6ef9..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yo.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yo_BJ.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yo_BJ.dat deleted file mode 100644 index b9d79c3e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yo_BJ.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yo_NG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yo_NG.dat deleted file mode 100644 index e473a96c..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yo_NG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yrl.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yrl.dat deleted file mode 100644 index 946ffccf..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yrl.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yrl_BR.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yrl_BR.dat deleted file mode 100644 index bf1012ee..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yrl_BR.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yrl_CO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yrl_CO.dat deleted file mode 100644 index c8a8c22e..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yrl_CO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yrl_VE.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yrl_VE.dat deleted file mode 100644 index d8ebd8ed..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yrl_VE.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yue.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yue.dat deleted file mode 100644 index e2fbe58b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yue.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans.dat deleted file mode 100644 index 73446b87..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans_CN.dat deleted file mode 100644 index 1ba61971..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hans_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant.dat deleted file mode 100644 index d6740346..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_CN.dat deleted file mode 100644 index 6ca866c4..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_HK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_HK.dat deleted file mode 100644 index 853efcbb..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_HK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_MO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_MO.dat deleted file mode 100644 index fca6b29d..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/yue_Hant_MO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/za.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/za.dat deleted file mode 100644 index 174e6839..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/za.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/za_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/za_CN.dat deleted file mode 100644 index f0cbb5db..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/za_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zgh.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zgh.dat deleted file mode 100644 index a0c342c2..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zgh.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zgh_MA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zgh_MA.dat deleted file mode 100644 index 8475afed..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zgh_MA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh.dat deleted file mode 100644 index ddf76651..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans.dat deleted file mode 100644 index c5c94bf8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_CN.dat deleted file mode 100644 index 09d08c10..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_HK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_HK.dat deleted file mode 100644 index 2396da0a..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_HK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MO.dat deleted file mode 100644 index 4f0232e5..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MY.dat deleted file mode 100644 index bddaab92..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_MY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_SG.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_SG.dat deleted file mode 100644 index 646b57d6..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hans_SG.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant.dat deleted file mode 100644 index fbfcef27..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_HK.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_HK.dat deleted file mode 100644 index d8ef238f..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_HK.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MO.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MO.dat deleted file mode 100644 index 2e468357..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MO.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MY.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MY.dat deleted file mode 100644 index 1198fb41..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_MY.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_TW.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_TW.dat deleted file mode 100644 index 657e8885..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Hant_TW.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn.dat deleted file mode 100644 index c5c94bf8..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn_CN.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn_CN.dat deleted file mode 100644 index 09d08c10..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zh_Latn_CN.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zu.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zu.dat deleted file mode 100644 index 8c98018b..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zu.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/locale-data/zu_ZA.dat b/.venv/lib/python3.12/site-packages/babel/locale-data/zu_ZA.dat deleted file mode 100644 index 026694ca..00000000 Binary files a/.venv/lib/python3.12/site-packages/babel/locale-data/zu_ZA.dat and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/babel/localedata.py b/.venv/lib/python3.12/site-packages/babel/localedata.py deleted file mode 100644 index 2b225a14..00000000 --- a/.venv/lib/python3.12/site-packages/babel/localedata.py +++ /dev/null @@ -1,284 +0,0 @@ -""" -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-2026 by the Babel Team. -:license: BSD, see LICENSE for more details. -""" - -from __future__ import annotations - -import os -import pickle -import re -import sys -import threading -from collections import abc -from collections.abc import Iterator, Mapping, MutableMapping -from functools import lru_cache -from itertools import chain -from typing import Any - -_cache: dict[str, Any] = {} -_cache_lock = threading.RLock() -_dirname = os.path.join(os.path.dirname(__file__), 'locale-data') -_windows_reserved_name_re = re.compile("^(con|prn|aux|nul|com[0-9]|lpt[0-9])$", re.I) - - -def normalize_locale(name: str) -> str | None: - """Normalize a locale ID by stripping spaces and apply proper casing. - - Returns the normalized locale ID string or `None` if the ID is not - recognized. - """ - if not name or not isinstance(name, str): - return None - name = name.strip().lower() - for locale_id in chain.from_iterable([_cache, locale_identifiers()]): - if name == locale_id.lower(): - return locale_id - - -def resolve_locale_filename(name: os.PathLike[str] | str) -> str: - """ - Resolve a locale identifier to a `.dat` path on disk. - """ - - # Clean up any possible relative paths. - name = os.path.basename(name) - - # Ensure we're not left with one of the Windows reserved names. - if sys.platform == "win32" and _windows_reserved_name_re.match(os.path.splitext(name)[0]): - raise ValueError(f"Name {name} is invalid on Windows") - - # Build the path. - return os.path.join(_dirname, f"{name}.dat") - - -def exists(name: str) -> bool: - """Check whether locale data is available for the given locale. - - Returns `True` if it exists, `False` otherwise. - - :param name: the locale identifier string - """ - if not name or not isinstance(name, str): - return False - if name in _cache: - return True - file_found = os.path.exists(resolve_locale_filename(name)) - return True if file_found else bool(normalize_locale(name)) - - -@lru_cache(maxsize=None) -def locale_identifiers() -> list[str]: - """Return a list of all locale identifiers for which locale data is - available. - - This data is cached after the first invocation. - You can clear the cache by calling `locale_identifiers.cache_clear()`. - - .. versionadded:: 0.8.1 - - :return: a list of locale identifiers (strings) - """ - return [ - stem - for stem, extension in ( - os.path.splitext(filename) for filename in os.listdir(_dirname) - ) - if extension == '.dat' and stem != 'root' - ] - - -def _is_non_likely_script(name: str) -> bool: - """Return whether the locale is of the form ``lang_Script``, - and the script is not the likely script for the language. - - This implements the behavior of the ``nonlikelyScript`` value of the - ``localRules`` attribute for parent locales added in CLDR 45. - """ - from babel.core import get_global, parse_locale - - try: - lang, territory, script, variant, *rest = parse_locale(name) - except ValueError: - return False - - if lang and script and not territory and not variant and not rest: - likely_subtag = get_global('likely_subtags').get(lang) - _, _, likely_script, *_ = parse_locale(likely_subtag) - return script != likely_script - return False - - -def load(name: os.PathLike[str] | str, merge_inherited: bool = True) -> dict[str, Any]: - """Load the locale data for the given locale. - - The locale data is a dictionary that contains much of the data defined by - the Common Locale Data Repository (CLDR). This data is stored as a - collection of pickle files inside the ``babel`` package. - - >>> d = load('en_US') - >>> d['languages']['sv'] - 'Swedish' - - Note that the results are cached, and subsequent requests for the same - locale return the same dictionary: - - >>> d1 = load('en_US') - >>> d2 = load('en_US') - >>> d1 is d2 - True - - :param name: the locale identifier string (or "root") - :param merge_inherited: whether the inherited data should be merged into - the data of the requested locale - :raise `IOError`: if no locale data file is found for the given locale - identifier, or one of the locales it inherits from - """ - name = os.path.basename(name) - _cache_lock.acquire() - try: - data = _cache.get(name) - if not data: - # Load inherited data - if name == 'root' or not merge_inherited: - data = {} - else: - from babel.core import get_global - - parent = get_global('parent_exceptions').get(name) - if not parent: - if _is_non_likely_script(name): - parent = 'root' - else: - parts = name.split('_') - parent = "root" if len(parts) == 1 else "_".join(parts[:-1]) - data = load(parent).copy() - filename = resolve_locale_filename(name) - with open(filename, 'rb') as fileobj: - if name != 'root' and merge_inherited: - merge(data, pickle.load(fileobj)) - else: - data = pickle.load(fileobj) - _cache[name] = data - return data - finally: - _cache_lock.release() - - -def merge(dict1: MutableMapping[Any, Any], dict2: Mapping[Any, Any]) -> None: - """Merge the data from `dict2` into the `dict1` dictionary, making copies - of nested dictionaries. - - >>> d = {1: 'foo', 3: 'baz'} - >>> merge(d, {1: 'Foo', 2: 'Bar'}) - >>> sorted(d.items()) - [(1, 'Foo'), (2, 'Bar'), (3, 'baz')] - - :param dict1: the dictionary to merge into - :param dict2: the dictionary containing the data that should be merged - """ - for key, val2 in dict2.items(): - if val2 is not None: - val1 = dict1.get(key) - if isinstance(val2, dict): - if val1 is None: - val1 = {} - if isinstance(val1, Alias): - val1 = (val1, val2) - elif isinstance(val1, tuple): - alias, others = val1 - others = others.copy() - merge(others, val2) - val1 = (alias, others) - else: - val1 = val1.copy() - merge(val1, val2) - else: - val1 = val2 - dict1[key] = val1 - - -class Alias: - """Representation of an alias in the locale data. - - An alias is a value that refers to some other part of the locale data, - as specified by the `keys`. - """ - - def __init__(self, keys: tuple[str, ...]) -> None: - self.keys = tuple(keys) - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.keys!r}>" - - def resolve(self, data: Mapping[str | int | None, Any]) -> Mapping[str | int | None, Any]: - """Resolve the alias based on the given data. - - This is done recursively, so if one alias resolves to a second alias, - that second alias will also be resolved. - - :param data: the locale data - :type data: `dict` - """ - base = data - for key in self.keys: - data = data[key] - if isinstance(data, Alias): - data = data.resolve(base) - elif isinstance(data, tuple): - alias, others = data - data = alias.resolve(base) - return data - - -class LocaleDataDict(abc.MutableMapping): - """Dictionary wrapper that automatically resolves aliases to the actual - values. - """ - - def __init__( - self, - data: MutableMapping[str | int | None, Any], - base: Mapping[str | int | None, Any] | None = None, - ): - self._data = data - if base is None: - base = data - self.base = base - - def __len__(self) -> int: - return len(self._data) - - def __iter__(self) -> Iterator[str | int | None]: - return iter(self._data) - - def __getitem__(self, key: str | int | None) -> Any: - orig = val = self._data[key] - if isinstance(val, Alias): # resolve an alias - val = val.resolve(self.base) - if isinstance(val, tuple): # Merge a partial dict with an alias - alias, others = val - val = alias.resolve(self.base).copy() - merge(val, others) - if isinstance(val, dict): # Return a nested alias-resolving dict - val = LocaleDataDict(val, base=self.base) - if val is not orig: - self._data[key] = val - return val - - def __setitem__(self, key: str | int | None, value: Any) -> None: - self._data[key] = value - - def __delitem__(self, key: str | int | None) -> None: - del self._data[key] - - def copy(self) -> LocaleDataDict: - return LocaleDataDict(self._data.copy(), base=self.base) diff --git a/.venv/lib/python3.12/site-packages/babel/localtime/__init__.py b/.venv/lib/python3.12/site-packages/babel/localtime/__init__.py deleted file mode 100644 index 9eb95ab2..00000000 --- a/.venv/lib/python3.12/site-packages/babel/localtime/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -babel.localtime -~~~~~~~~~~~~~~~ - -Babel specific fork of tzlocal to determine the local timezone -of the system. - -:copyright: (c) 2013-2026 by the Babel Team. -:license: BSD, see LICENSE for more details. -""" - -import datetime -import sys - -if sys.platform == 'win32': - from babel.localtime._win32 import _get_localzone -else: - from babel.localtime._unix import _get_localzone - - -# TODO(3.0): the offset constants are not part of the public API -# and should be removed -from babel.localtime._fallback import ( - DSTDIFF, # noqa: F401 - DSTOFFSET, # noqa: F401 - STDOFFSET, # noqa: F401 - ZERO, # noqa: F401 - _FallbackLocalTimezone, -) - - -def get_localzone() -> datetime.tzinfo: - """Returns the current underlying local timezone object. - Generally this function does not need to be used, it's a - better idea to use the :data:`LOCALTZ` singleton instead. - """ - return _get_localzone() - - -try: - LOCALTZ = get_localzone() -except LookupError: - LOCALTZ = _FallbackLocalTimezone() diff --git a/.venv/lib/python3.12/site-packages/babel/localtime/_fallback.py b/.venv/lib/python3.12/site-packages/babel/localtime/_fallback.py deleted file mode 100644 index 21881390..00000000 --- a/.venv/lib/python3.12/site-packages/babel/localtime/_fallback.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -babel.localtime._fallback -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Emulated fallback local timezone when all else fails. - -:copyright: (c) 2013-2026 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) # fmt: skip - stamp = time.mktime(tt) - tt = time.localtime(stamp) - return tt.tm_isdst > 0 diff --git a/.venv/lib/python3.12/site-packages/babel/localtime/_helpers.py b/.venv/lib/python3.12/site-packages/babel/localtime/_helpers.py deleted file mode 100644 index e7e67052..00000000 --- a/.venv/lib/python3.12/site-packages/babel/localtime/_helpers.py +++ /dev/null @@ -1,57 +0,0 @@ -try: - import pytz -except ModuleNotFoundError: - pytz = None - -try: - import zoneinfo -except ModuleNotFoundError: - zoneinfo = None - - -def _get_tzinfo(tzenv: str): - """Get the tzinfo from `zoneinfo` or `pytz` - - :param tzenv: timezone in the form of Continent/City - :return: tzinfo object or None if not found - """ - if pytz: - try: - return pytz.timezone(tzenv) - except pytz.UnknownTimeZoneError: - pass - else: - try: - return zoneinfo.ZoneInfo(tzenv) - except ValueError as ve: - # This is somewhat hacky, but since _validate_tzfile_path() doesn't - # raise a specific error type, we'll need to check the message to be - # one we know to be from that function. - # If so, we pretend it meant that the TZ didn't exist, for the benefit - # of `babel.localtime` catching the `LookupError` raised by - # `_get_tzinfo_or_raise()`. - # See https://github.com/python-babel/babel/issues/1092 - if str(ve).startswith("ZoneInfo keys "): - return None - except zoneinfo.ZoneInfoNotFoundError: - pass - - return None - - -def _get_tzinfo_or_raise(tzenv: str): - tzinfo = _get_tzinfo(tzenv) - if tzinfo is None: - raise LookupError( - f"Can not find timezone {tzenv}. \n" - "Timezone names are generally in the form `Continent/City`.", - ) - return tzinfo - - -def _get_tzinfo_from_file(tzfilename: str): - with open(tzfilename, 'rb') as tzfile: - if pytz: - return pytz.tzfile.build_tzinfo('local', tzfile) - else: - return zoneinfo.ZoneInfo.from_file(tzfile) diff --git a/.venv/lib/python3.12/site-packages/babel/localtime/_unix.py b/.venv/lib/python3.12/site-packages/babel/localtime/_unix.py deleted file mode 100644 index 70dd2322..00000000 --- a/.venv/lib/python3.12/site-packages/babel/localtime/_unix.py +++ /dev/null @@ -1,104 +0,0 @@ -import datetime -import os -import re - -from babel.localtime._helpers import ( - _get_tzinfo, - _get_tzinfo_from_file, - _get_tzinfo_or_raise, -) - - -def _tz_from_env(tzenv: str) -> datetime.tzinfo: - if tzenv[0] == ':': - tzenv = tzenv[1:] - - # TZ specifies a file - if os.path.exists(tzenv): - return _get_tzinfo_from_file(tzenv) - - # TZ specifies a zoneinfo zone. - return _get_tzinfo_or_raise(tzenv) - - -def _get_localzone(_root: str = '/') -> datetime.tzinfo: - """Tries to find the local timezone configuration. - This method prefers finding the timezone name and passing that to - zoneinfo or pytz, over passing in the localtime file, as in the later - case the zoneinfo name is unknown. - The parameter _root makes the function look for files like /etc/localtime - beneath the _root directory. This is primarily used by the tests. - In normal usage you call the function without parameters. - """ - - tzenv = os.environ.get('TZ') - if tzenv: - return _tz_from_env(tzenv) - - # This is actually a pretty reliable way to test for the local time - # zone on operating systems like OS X. On OS X especially this is the - # only one that actually works. - try: - link_dst = os.readlink('/etc/localtime') - except OSError: - pass - else: - pos = link_dst.find('/zoneinfo/') - if pos >= 0: - # On occasion, the `/etc/localtime` symlink has a double slash, e.g. - # "/usr/share/zoneinfo//UTC", which would make `zoneinfo.ZoneInfo` - # complain (no absolute paths allowed), and we'd end up returning - # `None` (as a fix for #1092). - # Instead, let's just "fix" the double slash symlink by stripping - # leading slashes before passing the assumed zone name forward. - zone_name = link_dst[pos + 10 :].lstrip("/") - tzinfo = _get_tzinfo(zone_name) - if tzinfo is not None: - return tzinfo - - # Now look for distribution specific configuration files - # that contain the timezone name. - tzpath = os.path.join(_root, 'etc/timezone') - if os.path.exists(tzpath): - with open(tzpath, 'rb') as tzfile: - data = tzfile.read() - - # Issue #3 in tzlocal was that /etc/timezone was a zoneinfo file. - # That's a misconfiguration, but we need to handle it gracefully: - if data[:5] != b'TZif2': - etctz = data.strip().decode() - # Get rid of host definitions and comments: - if ' ' in etctz: - etctz, dummy = etctz.split(' ', 1) - if '#' in etctz: - etctz, dummy = etctz.split('#', 1) - - return _get_tzinfo_or_raise(etctz.replace(' ', '_')) - - # CentOS has a ZONE setting in /etc/sysconfig/clock, - # OpenSUSE has a TIMEZONE setting in /etc/sysconfig/clock and - # Gentoo has a TIMEZONE setting in /etc/conf.d/clock - # We look through these files for a timezone: - timezone_re = re.compile(r'\s*(TIME)?ZONE\s*=\s*"(?P.+)"') - - for filename in ('etc/sysconfig/clock', 'etc/conf.d/clock'): - tzpath = os.path.join(_root, filename) - if not os.path.exists(tzpath): - continue - with open(tzpath) as tzfile: - for line in tzfile: - match = timezone_re.match(line) - if match is not None: - # We found a timezone - etctz = match.group("etctz") - return _get_tzinfo_or_raise(etctz.replace(' ', '_')) - - # No explicit setting existed. Use localtime - for filename in ('etc/localtime', 'usr/local/etc/localtime'): - tzpath = os.path.join(_root, filename) - - if not os.path.exists(tzpath): - continue - return _get_tzinfo_from_file(tzpath) - - raise LookupError('Can not find any timezone configuration') diff --git a/.venv/lib/python3.12/site-packages/babel/localtime/_win32.py b/.venv/lib/python3.12/site-packages/babel/localtime/_win32.py deleted file mode 100644 index 0fb625ba..00000000 --- a/.venv/lib/python3.12/site-packages/babel/localtime/_win32.py +++ /dev/null @@ -1,97 +0,0 @@ -from __future__ import annotations - -try: - import winreg -except ImportError: - winreg = None - -import datetime -from typing import Any, Dict, cast - -from babel.core import get_global -from babel.localtime._helpers import _get_tzinfo_or_raise - -# When building the cldr data on windows this module gets imported. -# Because at that point there is no global.dat yet this call will -# fail. We want to catch it down in that case then and just assume -# the mapping was empty. -try: - tz_names: dict[str, str] = cast(Dict[str, str], get_global('windows_zone_mapping')) -except RuntimeError: - tz_names = {} - - -def valuestodict(key) -> dict[str, Any]: - """Convert a registry key's values to a dictionary.""" - dict = {} - size = winreg.QueryInfoKey(key)[1] - for i in range(size): - data = winreg.EnumValue(key, i) - dict[data[0]] = data[1] - return dict - - -def get_localzone_name() -> str: - # Windows is special. It has unique time zone names (in several - # meanings of the word) available, but unfortunately, they can be - # translated to the language of the operating system, so we need to - # do a backwards lookup, by going through all time zones and see which - # one matches. - handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - - TZLOCALKEYNAME = r'SYSTEM\CurrentControlSet\Control\TimeZoneInformation' - localtz = winreg.OpenKey(handle, TZLOCALKEYNAME) - keyvalues = valuestodict(localtz) - localtz.Close() - if 'TimeZoneKeyName' in keyvalues: - # Windows 7 (and Vista?) - - # For some reason this returns a string with loads of NUL bytes at - # least on some systems. I don't know if this is a bug somewhere, I - # just work around it. - tzkeyname = keyvalues['TimeZoneKeyName'].split('\x00', 1)[0] - else: - # Windows 2000 or XP - - # This is the localized name: - tzwin = keyvalues['StandardName'] - - # Open the list of timezones to look up the real name: - TZKEYNAME = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones' - tzkey = winreg.OpenKey(handle, TZKEYNAME) - - # Now, match this value to Time Zone information - tzkeyname = None - for i in range(winreg.QueryInfoKey(tzkey)[0]): - subkey = winreg.EnumKey(tzkey, i) - sub = winreg.OpenKey(tzkey, subkey) - data = valuestodict(sub) - sub.Close() - if data.get('Std', None) == tzwin: - tzkeyname = subkey - break - - tzkey.Close() - handle.Close() - - if tzkeyname is None: - raise LookupError('Can not find Windows timezone configuration') - - timezone = tz_names.get(tzkeyname) - if timezone is None: - # Nope, that didn't work. Try adding 'Standard Time', - # it seems to work a lot of times: - timezone = tz_names.get(f"{tzkeyname} Standard Time") - - # Return what we have. - if timezone is None: - raise LookupError(f"Can not find timezone {tzkeyname}") - - return timezone - - -def _get_localzone() -> datetime.tzinfo: - if winreg is None: - raise LookupError('Runtime support not available') - - return _get_tzinfo_or_raise(get_localzone_name()) diff --git a/.venv/lib/python3.12/site-packages/babel/messages/__init__.py b/.venv/lib/python3.12/site-packages/babel/messages/__init__.py deleted file mode 100644 index 8dde3f29..00000000 --- a/.venv/lib/python3.12/site-packages/babel/messages/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -babel.messages -~~~~~~~~~~~~~~ - -Support for ``gettext`` message catalogs. - -:copyright: (c) 2013-2026 by the Babel Team. -:license: BSD, see LICENSE for more details. -""" - -from babel.messages.catalog import ( - Catalog, - Message, - TranslationError, -) - -__all__ = [ - "Catalog", - "Message", - "TranslationError", -] diff --git a/.venv/lib/python3.12/site-packages/babel/messages/_compat.py b/.venv/lib/python3.12/site-packages/babel/messages/_compat.py deleted file mode 100644 index 319b545f..00000000 --- a/.venv/lib/python3.12/site-packages/babel/messages/_compat.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys -from functools import partial - - -def find_entrypoints(group_name: str): - """ - Find entrypoints of a given group using either `importlib.metadata` or the - older `pkg_resources` mechanism. - - Yields tuples of the entrypoint name and a callable function that will - load the actual entrypoint. - """ - if sys.version_info >= (3, 10): - # "Changed in version 3.10: importlib.metadata is no longer provisional." - try: - from importlib.metadata import entry_points - except ImportError: - pass - else: - eps = entry_points(group=group_name) - # Only do this if this implementation of `importlib.metadata` is - # modern enough to not return a dict. - if not isinstance(eps, dict): - for entry_point in eps: - yield (entry_point.name, entry_point.load) - return - - try: - from pkg_resources import working_set - except ImportError: - pass - else: - for entry_point in working_set.iter_entry_points(group_name): - yield (entry_point.name, partial(entry_point.load, require=True)) diff --git a/.venv/lib/python3.12/site-packages/babel/messages/catalog.py b/.venv/lib/python3.12/site-packages/babel/messages/catalog.py deleted file mode 100644 index 9a9739a7..00000000 --- a/.venv/lib/python3.12/site-packages/babel/messages/catalog.py +++ /dev/null @@ -1,1055 +0,0 @@ -""" -babel.messages.catalog -~~~~~~~~~~~~~~~~~~~~~~ - -Data structures for message catalogs. - -:copyright: (c) 2013-2026 by the Babel Team. -:license: BSD, see LICENSE for more details. -""" - -from __future__ import annotations - -import datetime -import re -from collections.abc import Iterable, Iterator -from copy import copy -from difflib import SequenceMatcher -from email import message_from_string -from heapq import nlargest -from string import Formatter -from typing import TYPE_CHECKING - -from babel import __version__ as VERSION -from babel.core import Locale, UnknownLocaleError -from babel.dates import format_datetime -from babel.messages.plurals import get_plural -from babel.util import LOCALTZ, _cmp - -if TYPE_CHECKING: - from typing_extensions import TypeAlias - - _MessageID: TypeAlias = str | tuple[str, ...] | list[str] - -__all__ = [ - 'DEFAULT_HEADER', - 'PYTHON_FORMAT', - 'Catalog', - 'Message', - 'TranslationError', -] - - -def get_close_matches(word, possibilities, n=3, cutoff=0.6): - """A modified version of ``difflib.get_close_matches``. - - It just passes ``autojunk=False`` to the ``SequenceMatcher``, to work - around https://github.com/python/cpython/issues/90825. - """ - if not n > 0: # pragma: no cover - raise ValueError(f"n must be > 0: {n!r}") - if not 0.0 <= cutoff <= 1.0: # pragma: no cover - raise ValueError(f"cutoff must be in [0.0, 1.0]: {cutoff!r}") - result = [] - s = SequenceMatcher(autojunk=False) # only line changed from difflib.py - s.set_seq2(word) - for x in possibilities: - s.set_seq1(x) - if ( - s.real_quick_ratio() >= cutoff - and s.quick_ratio() >= cutoff - and s.ratio() >= cutoff - ): - result.append((s.ratio(), x)) - - # Move the best scorers to head of list - result = nlargest(n, result) - # Strip scores for the best n matches - return [x for score, x in result] - - -PYTHON_FORMAT = re.compile( - r''' - \% - (?:\(([\w]*)\))? - ( - [-#0\ +]?(?:\*|[\d]+)? - (?:\.(?:\*|[\d]+))? - [hlL]? - ) - ([diouxXeEfFgGcrs%]) -''', - re.VERBOSE, -) - - -def _has_python_brace_format(string: str) -> bool: - if "{" not in string: - return False - fmt = Formatter() - try: - # `fmt.parse` returns 3-or-4-tuples of the form - # `(literal_text, field_name, format_spec, conversion)`; - # if `field_name` is set, this smells like brace format - field_name_seen = False - for t in fmt.parse(string): - if t[1] is not None: - field_name_seen = True - # We cannot break here, as we need to consume the whole string - # to ensure that it is a valid format string. - except ValueError: - return False - return field_name_seen - - -def _parse_datetime_header(value: str) -> datetime.datetime: - match = re.match(r'^(?P.*?)(?P[+-]\d{4})?$', value) - - dt = datetime.datetime.strptime(match.group('datetime'), '%Y-%m-%d %H:%M') - - # Separate the offset into a sign component, hours, and # minutes - tzoffset = match.group('tzoffset') - if tzoffset is not None: - plus_minus_s, rest = tzoffset[0], tzoffset[1:] - hours_offset_s, mins_offset_s = rest[:2], rest[2:] - - # Make them all integers - plus_minus = int(f"{plus_minus_s}1") - hours_offset = int(hours_offset_s) - mins_offset = int(mins_offset_s) - - # Calculate net offset - net_mins_offset = hours_offset * 60 - net_mins_offset += mins_offset - net_mins_offset *= plus_minus - - # Create an offset object - tzoffset = datetime.timezone( - offset=datetime.timedelta(minutes=net_mins_offset), - name=f'Etc/GMT{net_mins_offset:+d}', - ) - - # 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(dict.fromkeys(locations)) if locations else [] - self.flags = set(flags) - if id and self.python_format: - self.flags.add('python-format') - else: - self.flags.discard('python-format') - if id and self.python_brace_format: - self.flags.add('python-brace-format') - else: - self.flags.discard('python-brace-format') - self.auto_comments = list(dict.fromkeys(auto_comments)) if auto_comments else [] - self.user_comments = list(dict.fromkeys(user_comments)) if user_comments else [] - if previous_id: - if isinstance(previous_id, str): - self.previous_id = [previous_id] - else: - self.previous_id = list(previous_id) - else: - self.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( - id=copy(self.id), - string=copy(self.string), - locations=copy(self.locations), - flags=copy(self.flags), - auto_comments=copy(self.auto_comments), - user_comments=copy(self.user_comments), - previous_id=copy(self.previous_id), - lineno=self.lineno, # immutable (str/None) - context=self.context, # immutable (str/None) - ) - - 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 isinstance(ids, (list, tuple)): - for id in ids: # Explicit loop for performance reasons. - if PYTHON_FORMAT.search(id): - return True - return False - return bool(PYTHON_FORMAT.search(ids)) - - @property - def python_brace_format(self) -> bool: - """Whether the message contains Python f-string parameters. - - >>> Message('Hello, {name}!').python_brace_format - True - >>> Message(('One apple', '{count} apples')).python_brace_format - True - - :type: `bool`""" - ids = self.id - if isinstance(ids, (list, tuple)): - for id in ids: # Explicit loop for performance reasons. - if _has_python_brace_format(id): - return True - return False - return _has_python_brace_format(ids) - - -class TranslationError(Exception): - """Exception thrown by translation checkers when invalid message - translations are encountered.""" - - -DEFAULT_HEADER = """\ -# Translations template for PROJECT. -# Copyright (C) YEAR ORGANIZATION -# This file is distributed under the same license as the PROJECT project. -# FIRST AUTHOR , YEAR. -#""" - - -def parse_separated_header(value: str) -> dict[str, str]: - # Adapted from https://peps.python.org/pep-0594/#cgi - from email.message import Message - - m = Message() - m['content-type'] = value - return dict(m.get_params()) - - -def _force_text(s: str | bytes, encoding: str = 'utf-8', errors: str = 'strict') -> str: - if isinstance(s, str): - return s - if isinstance(s, bytes): - return s.decode(encoding, errors) - return str(s) - - -class Catalog: - """Representation of a message catalog.""" - - def __init__( - self, - locale: Locale | str | None = None, - domain: str | None = None, - header_comment: str | None = DEFAULT_HEADER, - project: str | None = None, - version: str | None = None, - copyright_holder: str | None = None, - msgid_bugs_address: str | None = None, - creation_date: datetime.datetime | str | None = None, - revision_date: datetime.datetime | datetime.time | float | str | None = None, - last_translator: str | None = None, - language_team: str | None = None, - charset: str | None = None, - fuzzy: bool = True, - ) -> None: - """Initialize the catalog object. - - :param locale: the locale identifier or `Locale` object, or `None` - if the catalog is not bound to a locale (which basically - means it's a template) - :param domain: the message domain - :param header_comment: the header comment as string, or `None` for the - default header - :param project: the project's name - :param version: the project's version - :param copyright_holder: the copyright holder of the catalog - :param msgid_bugs_address: the email address or URL to submit bug - reports to - :param creation_date: the date the catalog was created - :param revision_date: the date the catalog was revised - :param last_translator: the name and email of the last translator - :param language_team: the name and email of the language team - :param charset: the encoding to use in the output (defaults to utf-8) - :param fuzzy: the fuzzy bit on the catalog header - """ - self.domain = domain - self.locale = locale - self._header_comment = header_comment - self._messages: dict[str | tuple[str, str], Message] = {} - - self.project = project or 'PROJECT' - self.version = version or 'VERSION' - self.copyright_holder = copyright_holder or 'ORGANIZATION' - self.msgid_bugs_address = msgid_bugs_address or 'EMAIL@ADDRESS' - - self.last_translator = last_translator or 'FULL NAME ' - """Name and email address of the last translator.""" - self.language_team = language_team or 'LANGUAGE ' - """Name and email address of the language team.""" - - self.charset = charset or 'utf-8' - - if creation_date is None: - creation_date = datetime.datetime.now(LOCALTZ) - elif isinstance(creation_date, datetime.datetime) and not creation_date.tzinfo: - creation_date = creation_date.replace(tzinfo=LOCALTZ) - self.creation_date = creation_date - if revision_date is None: - revision_date = 'YEAR-MO-DA HO:MI+ZONE' - elif isinstance(revision_date, datetime.datetime) and not revision_date.tzinfo: - revision_date = revision_date.replace(tzinfo=LOCALTZ) - self.revision_date = revision_date - self.fuzzy = fuzzy - - # Dictionary of obsolete messages - self.obsolete: dict[str | tuple[str, str], Message] = {} - self._num_plurals = None - self._plural_expr = None - - def _set_locale(self, locale: Locale | str | None) -> None: - if locale is None: - self._locale_identifier = None - self._locale = None - return - - if isinstance(locale, Locale): - self._locale_identifier = str(locale) - self._locale = locale - return - - if isinstance(locale, str): - self._locale_identifier = str(locale) - try: - self._locale = Locale.parse(locale) - except UnknownLocaleError: - self._locale = None - return - - raise TypeError( - f"`locale` must be a Locale, a locale identifier string, or None; got {locale!r}", - ) - - def _get_locale(self) -> Locale | None: - return self._locale - - def _get_locale_identifier(self) -> str | None: - return self._locale_identifier - - locale = property(_get_locale, _set_locale) - locale_identifier = property(_get_locale_identifier) - - def _get_header_comment(self) -> str: - comment = self._header_comment - year = datetime.datetime.now(LOCALTZ).strftime('%Y') - if hasattr(self.revision_date, 'strftime'): - year = self.revision_date.strftime('%Y') - comment = ( - comment.replace('PROJECT', self.project) - .replace('VERSION', self.version) - .replace('YEAR', year) - .replace('ORGANIZATION', self.copyright_holder) - ) - locale_name = self.locale.english_name if self.locale else self.locale_identifier - if locale_name: - comment = comment.replace("Translations template", f"{locale_name} translations") - return comment - - def _set_header_comment(self, string: str | None) -> None: - self._header_comment = string - - header_comment = property( - _get_header_comment, - _set_header_comment, - doc="""\ - The header comment for the catalog. - - >>> catalog = Catalog(project='Foobar', version='1.0', - ... copyright_holder='Foo Company') - >>> print(catalog.header_comment) #doctest: +ELLIPSIS - # Translations template for Foobar. - # Copyright (C) ... Foo Company - # This file is distributed under the same license as the Foobar project. - # FIRST AUTHOR , .... - # - - The header can also be set from a string. Any known upper-case variables - will be replaced when the header is retrieved again: - - >>> catalog = Catalog(project='Foobar', version='1.0', - ... copyright_holder='Foo Company') - >>> catalog.header_comment = '''\\ - ... # The POT for my really cool PROJECT project. - ... # Copyright (C) 1990-2003 ORGANIZATION - ... # This file is distributed under the same license as the PROJECT - ... # project. - ... #''' - >>> print(catalog.header_comment) - # The POT for my really cool Foobar project. - # Copyright (C) 1990-2003 Foo Company - # This file is distributed under the same license as the Foobar - # project. - # - - :type: `unicode` - """, - ) - - def _get_mime_headers(self) -> list[tuple[str, str]]: - if isinstance(self.revision_date, (datetime.datetime, datetime.time, int, float)): - revision_date = format_datetime( - self.revision_date, - 'yyyy-MM-dd HH:mmZ', - locale='en', - ) - else: - revision_date = self.revision_date - - language_team = self.language_team - if self.locale_identifier and 'LANGUAGE' in language_team: - language_team = language_team.replace('LANGUAGE', str(self.locale_identifier)) - - headers: list[tuple[str, str]] = [ - ("Project-Id-Version", f"{self.project} {self.version}"), - ('Report-Msgid-Bugs-To', self.msgid_bugs_address), - ('POT-Creation-Date', format_datetime(self.creation_date, 'yyyy-MM-dd HH:mmZ', locale='en')), - ('PO-Revision-Date', revision_date), - ('Last-Translator', self.last_translator), - ] # fmt: skip - if self.locale_identifier: - headers.append(('Language', str(self.locale_identifier))) - headers.append(('Language-Team', language_team)) - if self.locale is not None: - headers.append(('Plural-Forms', self.plural_forms)) - headers += [ - ('MIME-Version', '1.0'), - ("Content-Type", f"text/plain; charset={self.charset}"), - ('Content-Transfer-Encoding', '8bit'), - ("Generated-By", f"Babel {VERSION}\n"), - ] - return headers - - def _set_mime_headers(self, headers: Iterable[tuple[str, str]]) -> None: - for name, value in headers: - name = _force_text(name.lower(), encoding=self.charset) - value = _force_text(value, encoding=self.charset) - if name == 'project-id-version': - parts = value.split(' ') - self.project = ' '.join(parts[:-1]) - self.version = parts[-1] - elif name == 'report-msgid-bugs-to': - self.msgid_bugs_address = value - elif name == 'last-translator': - self.last_translator = value - elif name == 'language': - value = value.replace('-', '_') - # The `or None` makes sure that the locale is set to None - # if the header's value is an empty string, which is what - # some tools generate (instead of eliding the empty Language - # header altogether). - self._set_locale(value or None) - elif name == 'language-team': - self.language_team = value - elif name == 'content-type': - params = parse_separated_header(value) - if 'charset' in params: - self.charset = params['charset'].lower() - elif name == 'plural-forms': - params = parse_separated_header(f" ;{value}") - self._num_plurals = int(params.get('nplurals', 2)) - self._plural_expr = params.get('plural', '(n != 1)') - elif name == 'pot-creation-date': - self.creation_date = _parse_datetime_header(value) - elif name == 'po-revision-date': - # Keep the value if it's not the default one - if 'YEAR' not in value: - self.revision_date = _parse_datetime_header(value) - - mime_headers = property( - _get_mime_headers, - _set_mime_headers, - doc="""\ - The MIME headers of the catalog, used for the special ``msgid ""`` entry. - - The behavior of this property changes slightly depending on whether a locale - is set or not, the latter indicating that the catalog is actually a template - for actual translations. - - Here's an example of the output for such a catalog template: - - >>> from babel.dates import UTC - >>> from datetime import datetime - >>> created = datetime(1990, 4, 1, 15, 30, tzinfo=UTC) - >>> catalog = Catalog(project='Foobar', version='1.0', - ... creation_date=created) - >>> for name, value in catalog.mime_headers: - ... print('%s: %s' % (name, value)) - Project-Id-Version: Foobar 1.0 - Report-Msgid-Bugs-To: EMAIL@ADDRESS - POT-Creation-Date: 1990-04-01 15:30+0000 - PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE - Last-Translator: FULL NAME - Language-Team: LANGUAGE - MIME-Version: 1.0 - Content-Type: text/plain; charset=utf-8 - Content-Transfer-Encoding: 8bit - Generated-By: Babel ... - - And here's an example of the output when the locale is set: - - >>> revised = datetime(1990, 8, 3, 12, 0, tzinfo=UTC) - >>> catalog = Catalog(locale='de_DE', project='Foobar', version='1.0', - ... creation_date=created, revision_date=revised, - ... last_translator='John Doe ', - ... language_team='de_DE ') - >>> for name, value in catalog.mime_headers: - ... print('%s: %s' % (name, value)) - Project-Id-Version: Foobar 1.0 - Report-Msgid-Bugs-To: EMAIL@ADDRESS - POT-Creation-Date: 1990-04-01 15:30+0000 - PO-Revision-Date: 1990-08-03 12:00+0000 - Last-Translator: John Doe - Language: de_DE - Language-Team: de_DE - Plural-Forms: nplurals=2; plural=(n != 1); - MIME-Version: 1.0 - Content-Type: text/plain; charset=utf-8 - Content-Transfer-Encoding: 8bit - Generated-By: Babel ... - - :type: `list` - """, - ) - - @property - def num_plurals(self) -> int: - """The number of plurals used by the catalog or locale. - - >>> Catalog(locale='en').num_plurals - 2 - >>> Catalog(locale='ga').num_plurals - 5 - - :type: `int`""" - if self._num_plurals is None: - num = 2 - if self.locale: - num = get_plural(self.locale)[0] - self._num_plurals = num - return self._num_plurals - - @property - def plural_expr(self) -> str: - """The plural expression used by the catalog or locale. - - >>> Catalog(locale='en').plural_expr - '(n != 1)' - >>> Catalog(locale='ga').plural_expr - '(n==1 ? 0 : n==2 ? 1 : n>=3 && n<=6 ? 2 : n>=7 && n<=10 ? 3 : 4)' - >>> Catalog(locale='ding').plural_expr # unknown locale - '(n != 1)' - - :type: `str`""" - if self._plural_expr is None: - expr = '(n != 1)' - if self.locale: - expr = get_plural(self.locale)[1] - self._plural_expr = expr - return self._plural_expr - - @property - def plural_forms(self) -> str: - """Return the plural forms declaration for the locale. - - >>> Catalog(locale='en').plural_forms - 'nplurals=2; plural=(n != 1);' - >>> Catalog(locale='pt_BR').plural_forms - 'nplurals=2; plural=(n > 1);' - - :type: `str`""" - return f"nplurals={self.num_plurals}; plural={self.plural_expr};" - - def __contains__(self, id: _MessageID) -> bool: - """Return whether the catalog has a message with the specified ID.""" - return self._key_for(id) in self._messages - - def __len__(self) -> int: - """The number of messages in the catalog. - - This does not include the special ``msgid ""`` entry.""" - return len(self._messages) - - def __iter__(self) -> Iterator[Message]: - """Iterates through all the entries in the catalog, in the order they - were added, yielding a `Message` object for every entry. - - :rtype: ``iterator``""" - buf = [] - for name, value in self.mime_headers: - buf.append(f"{name}: {value}") - flags = set() - if self.fuzzy: - flags |= {'fuzzy'} - yield Message('', '\n'.join(buf), flags=flags) - for key in self._messages: - yield self._messages[key] - - def __repr__(self) -> str: - locale = '' - if self.locale: - locale = f" {self.locale}" - return f"<{type(self).__name__} {self.domain!r}{locale}>" - - def __delitem__(self, id: _MessageID) -> None: - """Delete the message with the specified ID.""" - self.delete(id) - - def __getitem__(self, id: _MessageID) -> Message: - """Return the message with the specified ID. - - :param id: the message ID - """ - return self.get(id) - - def __setitem__(self, id: _MessageID, message: Message) -> None: - """Add or update the message with the specified ID. - - >>> catalog = Catalog() - >>> catalog['foo'] = Message('foo') - >>> catalog['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['foo'] = Message('foo', locations=[('main.py', 1)]) - >>> catalog['foo'].locations - [('main.py', 1)] - >>> catalog['foo'] = Message('foo', locations=[('utils.py', 5)]) - >>> catalog['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(dict.fromkeys([*current.locations, *message.locations])) - current.auto_comments = list(dict.fromkeys([*current.auto_comments, *message.auto_comments])) # fmt:skip - current.user_comments = list(dict.fromkeys([*current.user_comments, *message.user_comments])) # fmt:skip - current.flags |= message.flags - elif id == '': - # special treatment for the header message - self.mime_headers = message_from_string(message.string).items() - self.header_comment = "\n".join(f"# {c}".rstrip() for c in message.user_comments) - self.fuzzy = message.fuzzy - else: - if isinstance(id, (list, tuple)): - assert isinstance(message.string, (list, tuple)), ( - f"Expected sequence but got {type(message.string)}" - ) - self._messages[key] = message - - def add( - self, - id: _MessageID, - string: _MessageID | None = None, - locations: Iterable[tuple[str, int]] = (), - flags: Iterable[str] = (), - auto_comments: Iterable[str] = (), - user_comments: Iterable[str] = (), - previous_id: _MessageID = (), - lineno: int | None = None, - context: str | None = None, - ) -> Message: - """Add or update the message with the specified ID. - - >>> catalog = Catalog() - >>> catalog.add('foo') - - >>> catalog['foo'] - - - This method simply constructs a `Message` object with the given - arguments and invokes `__setitem__` with that object. - - :param id: the message ID, or a ``(singular, plural)`` tuple for - pluralizable messages - :param string: the translated message string, or a - ``(singular, plural)`` tuple for pluralizable messages - :param locations: a sequence of ``(filename, lineno)`` tuples - :param flags: a set or sequence of flags - :param auto_comments: a sequence of automatic comments - :param user_comments: a sequence of user comments - :param previous_id: the previous message ID, or a ``(singular, plural)`` - tuple for pluralizable messages - :param lineno: the line number on which the msgid line was found in the - PO file, if any - :param context: the message context - """ - message = Message( - id, - string, - list(locations), - flags, - auto_comments, - user_comments, - previous_id, - lineno=lineno, - context=context, - ) - self[id] = message - return message - - def check(self) -> Iterable[tuple[Message, list[TranslationError]]]: - """Run various validation checks on the translations in the catalog. - - For every message which fails validation, this method yield a - ``(message, errors)`` tuple, where ``message`` is the `Message` object - and ``errors`` is a sequence of `TranslationError` objects. - - :rtype: ``generator`` of ``(message, errors)`` - """ - for message in self._messages.values(): - errors = message.check(catalog=self) - if errors: - yield message, errors - - def get(self, id: _MessageID, context: str | None = None) -> Message | None: - """Return the message with the specified ID and context. - - :param id: the message ID - :param context: the message context, or ``None`` for no context - """ - return self._messages.get(self._key_for(id, context)) - - def delete(self, id: _MessageID, context: str | None = None) -> None: - """Delete the message with the specified ID and context. - - :param id: the message ID - :param context: the message context, or ``None`` for no context - """ - key = self._key_for(id, context) - if key in self._messages: - del self._messages[key] - - def update( - self, - template: Catalog, - no_fuzzy_matching: bool = False, - update_header_comment: bool = False, - keep_user_comments: bool = True, - update_creation_date: bool = True, - ) -> None: - """Update the catalog based on the given template catalog. - - >>> from babel.messages import Catalog - >>> template = Catalog() - >>> template.add('green', locations=[('main.py', 99)]) - - >>> template.add('blue', locations=[('main.py', 100)]) - - >>> template.add(('salad', 'salads'), locations=[('util.py', 42)]) - - >>> catalog = Catalog(locale='de_DE') - >>> catalog.add('blue', 'blau', locations=[('main.py', 98)]) - - >>> catalog.add('head', 'Kopf', locations=[('util.py', 33)]) - - >>> catalog.add(('salad', 'salads'), ('Salat', '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 - 'blau' - >>> msg2.locations - [('main.py', 100)] - - >>> msg3 = catalog['salad'] - >>> msg3.string - ('Salat', 'Salate') - >>> msg3.locations - [('util.py', 42)] - - Messages that are in the catalog but not in the template are removed - from the main collection, but can still be accessed via the `obsolete` - member: - - >>> 'head' in catalog - False - >>> list(catalog.obsolete.values()) - [] - - :param template: the reference catalog, usually read from a POT file - :param no_fuzzy_matching: whether to use fuzzy matching of message IDs - :param update_header_comment: whether to copy the header comment from the template - :param keep_user_comments: whether to keep user comments from the old catalog - :param update_creation_date: whether to copy the creation date from the template - """ - messages = self._messages - remaining = messages.copy() - self._messages = {} - - # Prepare for fuzzy matching - fuzzy_candidates = {} - if not no_fuzzy_matching: - for msgid in messages: - if msgid and messages[msgid].string: - key = self._key_for(msgid) - ctxt = messages[msgid].context - fuzzy_candidates[self._to_fuzzy_match_key(key)] = (key, ctxt) - fuzzy_matches = set() - - def _merge( - message: Message, - oldkey: tuple[str, str] | str, - newkey: tuple[str, str] | str, - ) -> None: - message = message.clone() - fuzzy = False - if oldkey != newkey: - fuzzy = True - fuzzy_matches.add(oldkey) - oldmsg = messages.get(oldkey) - assert oldmsg is not None - if isinstance(oldmsg.id, str): - message.previous_id = [oldmsg.id] - else: - message.previous_id = list(oldmsg.id) - else: - oldmsg = remaining.pop(oldkey, None) - assert oldmsg is not None - message.string = oldmsg.string - - if keep_user_comments and oldmsg.user_comments: - message.user_comments = list(dict.fromkeys(oldmsg.user_comments)) - - if isinstance(message.id, (list, tuple)): - if not isinstance(message.string, (list, tuple)): - fuzzy = True - message.string = tuple( - [message.string] + ([''] * (len(message.id) - 1)), - ) - elif len(message.string) != self.num_plurals: - fuzzy = True - message.string = tuple(message.string[: len(oldmsg.string)]) - elif isinstance(message.string, (list, tuple)): - fuzzy = True - message.string = message.string[0] - message.flags |= oldmsg.flags - if fuzzy: - message.flags |= {'fuzzy'} - self[message.id] = message - - for message in template: - if message.id: - key = self._key_for(message.id, message.context) - if key in messages: - _merge(message, key, key) - else: - if not no_fuzzy_matching: - # do some fuzzy matching with difflib - matches = get_close_matches( - self._to_fuzzy_match_key(key), - fuzzy_candidates.keys(), - 1, - ) - if matches: - modified_key = matches[0] - newkey, newctxt = fuzzy_candidates[modified_key] - if newctxt is not None: - newkey = newkey, newctxt - _merge(message, newkey, key) - continue - - self[message.id] = message - - for msgid in remaining: - if no_fuzzy_matching or msgid not in fuzzy_matches: - self.obsolete[msgid] = remaining[msgid] - - if update_header_comment: - # Allow the updated catalog's header to be rewritten based on the - # template's header - self.header_comment = template.header_comment - - # Make updated catalog's POT-Creation-Date equal to the template - # used to update the catalog - if update_creation_date: - self.creation_date = template.creation_date - - def _to_fuzzy_match_key(self, key: tuple[str, str] | str) -> str: - """Converts a message key to a string suitable for fuzzy matching.""" - if isinstance(key, tuple): - matchkey = key[0] # just the msgid, no context - else: - matchkey = key - return matchkey.lower().strip() - - def _key_for( - self, - id: _MessageID, - context: str | None = None, - ) -> tuple[str, str] | str: - """The key for a message is just the singular ID even for pluralizable - messages, but is a ``(msgid, msgctxt)`` tuple for context-specific - messages. - """ - key = id - if isinstance(key, (list, tuple)): - key = id[0] - if context is not None: - key = (key, context) - return key - - def is_identical(self, other: Catalog) -> bool: - """Checks if catalogs are identical, taking into account messages and - headers. - """ - assert isinstance(other, Catalog) - for key in self._messages.keys() | other._messages.keys(): - message_1 = self.get(key) - message_2 = other.get(key) - if message_1 is None or message_2 is None or not message_1.is_identical(message_2): - return False - return dict(self.mime_headers) == dict(other.mime_headers) diff --git a/.venv/lib/python3.12/site-packages/babel/messages/checkers.py b/.venv/lib/python3.12/site-packages/babel/messages/checkers.py deleted file mode 100644 index 4026ab1b..00000000 --- a/.venv/lib/python3.12/site-packages/babel/messages/checkers.py +++ /dev/null @@ -1,176 +0,0 @@ -""" -babel.messages.checkers -~~~~~~~~~~~~~~~~~~~~~~~ - -Various routines that help with validation of translations. - -:since: version 0.9 - -:copyright: (c) 2013-2026 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( - f"Wrong number of plural forms (expected {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,) - - if msgstrs[0]: - _validate_format(msgids[0], msgstrs[0]) - if message.pluralizable: - for msgstr in msgstrs[1:]: - if msgstr: - _validate_format(msgids[1], msgstr) - - -def _validate_format(format: str, alternative: str) -> None: - """Test format string `alternative` against `format`. `format` can be the - msgid of a message and `alternative` one of the `msgstr`\\s. The two - arguments are not interchangeable as `alternative` may contain less - placeholders if `format` uses named placeholders. - - If the string formatting of `alternative` is compatible to `format` the - function returns `None`, otherwise a `TranslationError` is raised. - - Examples for compatible format strings: - - >>> _validate_format('Hello %s!', 'Hallo %s!') - >>> _validate_format('Hello %i!', 'Hallo %d!') - - Example for an incompatible format strings: - - >>> _validate_format('Hello %(name)s!', 'Hallo %s!') - Traceback (most recent call last): - ... - TranslationError: the format strings are of different kinds - - This function is used by the `python_format` checker. - - :param format: The original format string - :param alternative: The alternative format string that should be checked - against format - :raises TranslationError: on formatting errors - """ - - def _parse(string: str) -> list[tuple[str, str]]: - result: list[tuple[str, str]] = [] - for match in PYTHON_FORMAT.finditer(string): - name, format, typechar = match.groups() - if typechar == '%' and name is None: - continue - result.append((name, str(typechar))) - return result - - def _compatible(a: str, b: str) -> bool: - if a == b: - return True - for set in _string_format_compatibilities: - if a in set and b in set: - return True - return False - - def _check_positional(results: list[tuple[str, str]]) -> bool: - positional = None - for name, _char in results: - if positional is None: - positional = name is None - else: - if (name is None) != positional: - raise TranslationError( - 'format string mixes positional and named placeholders', - ) - return bool(positional) - - a = _parse(format) - b = _parse(alternative) - - if not a: - return - - # now check if both strings are positional or named - a_positional = _check_positional(a) - b_positional = _check_positional(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( - f'incompatible format for placeholder {idx + 1:d}: ' - f'{first!r} and {second!r} are not compatible', - ) - - # otherwise the second string must not have names the first one - # doesn't have and the types of those included must be compatible - else: - type_map = dict(a) - for name, typechar in b: - if name not in type_map: - raise TranslationError(f'unknown named placeholder {name!r}') - elif not _compatible(typechar, type_map[name]): - raise TranslationError( - f'incompatible format for placeholder {name!r}: ' - f'{typechar!r} and {type_map[name]!r} are not compatible', - ) - - -def _find_checkers() -> list[Callable[[Catalog | None, Message], object]]: - from babel.messages._compat import find_entrypoints - - checkers: list[Callable[[Catalog | None, Message], object]] = [] - checkers.extend(load() for (name, load) in find_entrypoints('babel.checkers')) - if len(checkers) == 0: - # if entrypoints are not available or no usable egg-info was found - # (see #230), just resort to hard-coded checkers - return [num_plurals, python_format] - return checkers - - -checkers: list[Callable[[Catalog | None, Message], object]] = _find_checkers() diff --git a/.venv/lib/python3.12/site-packages/babel/messages/extract.py b/.venv/lib/python3.12/site-packages/babel/messages/extract.py deleted file mode 100644 index 6fad8430..00000000 --- a/.venv/lib/python3.12/site-packages/babel/messages/extract.py +++ /dev/null @@ -1,943 +0,0 @@ -""" -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-2026 by the Babel Team. -:license: BSD, see LICENSE for more details. -""" - -from __future__ import annotations - -import ast -import io -import os -import sys -import tokenize -import warnings -from collections.abc import ( - Callable, - Collection, - Generator, - Iterable, - Mapping, - MutableSequence, -) -from functools import lru_cache -from os.path import relpath -from textwrap import dedent -from tokenize import COMMENT, NAME, NL, OP, STRING, generate_tokens -from typing import TYPE_CHECKING, Any, TypedDict - -from babel.messages._compat import find_entrypoints -from babel.util import parse_encoding, parse_future_flags, pathmatch - -if TYPE_CHECKING: - from typing import IO, Final, Protocol - - from _typeshed import SupportsItems, SupportsRead, SupportsReadline - from typing_extensions import TypeAlias - - class _PyOptions(TypedDict, total=False): - encoding: str - - class _JSOptions(TypedDict, total=False): - encoding: str - jsx: bool - template_string: bool - parse_template_string: bool - - class _FileObj(SupportsRead[bytes], SupportsReadline[bytes], Protocol): - def seek(self, __offset: int, __whence: int = ...) -> int: ... - def tell(self) -> int: ... - - _SimpleKeyword: TypeAlias = tuple[int | tuple[int, int] | tuple[int, str], ...] | None - _Keyword: TypeAlias = dict[int | None, _SimpleKeyword] | _SimpleKeyword - - # 5-tuple of (filename, lineno, messages, comments, context) - _FileExtractionResult: TypeAlias = tuple[str, int, str | tuple[str, ...], list[str], str | None] # fmt: skip - - # 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], - ] # fmt: skip - - _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), - 'dpgettext': ((2, 'c'), 3), - 'N_': None, - 'pgettext': ((1, 'c'), 2), - 'npgettext': ((1, 'c'), 2, 3), - 'dnpgettext': ((2, 'c'), 3, 4), -} - -DEFAULT_MAPPING: list[tuple[str, str]] = [('**.py', 'python')] - -# New tokens in Python 3.12, or None on older versions -FSTRING_START = getattr(tokenize, "FSTRING_START", None) -FSTRING_MIDDLE = getattr(tokenize, "FSTRING_MIDDLE", None) -FSTRING_END = getattr(tokenize, "FSTRING_END", None) - - -def _strip_comment_tags(comments: MutableSequence[str], tags: Iterable[str]): - """Helper function for `extract` that strips comment tags from strings - in a list of comment lines. This functions operates in-place. - """ - - def _strip(line: str): - for tag in tags: - if line.startswith(tag): - return line[len(tag) :].strip() - return line - - comments[:] = [_strip(c) for c in comments] - - -def _make_default_directory_filter( - method_map: Iterable[tuple[str, str]], - root_dir: str | os.PathLike[str], -): - method_map = tuple(method_map) - - def directory_filter(dirpath: str | os.PathLike[str]) -> bool: - subdir = os.path.basename(dirpath) - # Legacy default behavior: ignore dot and underscore directories - if subdir.startswith('.') or subdir.startswith('_'): - return False - - dir_rel = os.path.relpath(dirpath, root_dir).replace(os.sep, '/') - - for pattern, method in method_map: - if method == "ignore" and pathmatch(pattern, dir_rel): - return False - - return True - - return directory_filter - - -def default_directory_filter(dirpath: str | os.PathLike[str]) -> bool: # pragma: no cover - warnings.warn( - "`default_directory_filter` is deprecated and will be removed in a future version of Babel.", - DeprecationWarning, - stacklevel=2, - ) - 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 = {} - - dirname = os.path.abspath(dirname) - - if directory_filter is None: - directory_filter = _make_default_directory_filter( - method_map=method_map, - root_dir=dirname, - ) - - for root, dirnames, filenames in os.walk(dirname): - 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=dirname, - ) - - -def check_and_call_extract_file( - filepath: str | os.PathLike[str], - method_map: Iterable[tuple[str, str]], - options_map: SupportsItems[str, dict[str, Any]], - callback: Callable[[str, str, dict[str, Any]], object] | None, - keywords: Mapping[str, _Keyword], - comment_tags: Collection[str], - strip_comment_tags: bool, - dirpath: str | os.PathLike[str] | None = None, -) -> Generator[_FileExtractionResult, None, None]: - """Checks if the given file matches an extraction method mapping, and if so, calls extract_from_file. - - Note that the extraction method mappings are based relative to dirpath. - So, given an absolute path to a file `filepath`, we want to check using - just the relative path from `dirpath` to `filepath`. - - Yields 5-tuples (filename, lineno, messages, comments, context). - - :param filepath: An absolute path to a file that exists. - :param method_map: a list of ``(pattern, method)`` tuples that maps of - extraction method names to extended glob patterns - :param options_map: a dictionary of additional options (optional) - :param callback: a function that is called for every file that message are - extracted from, just before the extraction itself is - performed; the function is passed the filename, the name - of the extraction method and and the options dictionary as - positional arguments, in that order - :param keywords: a dictionary mapping keywords (i.e. names of functions - that should be recognized as translation functions) to - tuples that specify which of their arguments contain - localizable strings - :param comment_tags: a list of tags of translator comments to search for - and include in the results - :param strip_comment_tags: a flag that if set to `True` causes all comment - tags to be removed from the collected comments. - :param dirpath: the path to the directory to extract messages from. - :return: iterable of 5-tuples (filename, lineno, messages, comments, context) - :rtype: Iterable[tuple[str, int, str|tuple[str], list[str], str|None] - """ - # filename is the relative path from dirpath to the actual file - filename = relpath(filepath, dirpath) - - for pattern, method in method_map: - if not pathmatch(pattern, filename): - continue - - options = {} - for opattern, odict in options_map.items(): - if pathmatch(opattern, filename): - options = odict - break - - # Merge keywords and comment_tags from per-format options if present. - file_keywords = keywords - file_comment_tags = comment_tags - if keywords_opt := options.get("keywords"): - if not isinstance(keywords_opt, dict): # pragma: no cover - raise TypeError( - f"The `keywords` option must be a dict of parsed keywords, not {keywords_opt!r}", - ) - file_keywords = {**keywords, **keywords_opt} - - if comments_opt := options.get("add_comments"): - if not isinstance(comments_opt, (list, tuple, set)): # pragma: no cover - raise TypeError( - f"The `add_comments` option must be a collection of comment tags, not {comments_opt!r}.", - ) - file_comment_tags = tuple(set(comment_tags) | set(comments_opt)) - - if callback: - callback(filename, method, options) - for message_tuple in extract_from_file( - method, - filepath, - keywords=file_keywords, - comment_tags=file_comment_tags, - options=options, - strip_comment_tags=strip_comment_tags, - ): - yield (filename, *message_tuple) - - break - - -def extract_from_file( - method: _ExtractionMethod, - filename: str | os.PathLike[str], - keywords: Mapping[str, _Keyword] = DEFAULT_KEYWORDS, - comment_tags: Collection[str] = (), - options: Mapping[str, Any] | None = None, - strip_comment_tags: bool = False, -) -> list[_ExtractionResult]: - """Extract messages from a specific file. - - This function returns a list of tuples of the form ``(lineno, message, comments, context)``. - - :param filename: the path to the file to extract messages from - :param method: a string specifying the extraction method (.e.g. "python") - :param keywords: a dictionary mapping keywords (i.e. names of functions - that should be recognized as translation functions) to - tuples that specify which of their arguments contain - localizable strings - :param comment_tags: a list of translator tags to search for and include - in the results - :param strip_comment_tags: a flag that if set to `True` causes all comment - tags to be removed from the collected comments. - :param options: a dictionary of additional options (optional) - :returns: list of tuples of the form ``(lineno, message, comments, context)`` - :rtype: list[tuple[int, str|tuple[str], list[str], str|None] - """ - if method == 'ignore': - return [] - - with open(filename, 'rb') as fileobj: - return list( - extract(method, fileobj, keywords, comment_tags, options, strip_comment_tags), - ) - - -def _match_messages_against_spec( - lineno: int, - messages: list[str | None], - comments: list[str], - fileobj: _FileObj, - spec: tuple[int | tuple[int, str], ...], -): - translatable = [] - context = None - - # last_index is 1 based like the keyword spec - last_index = len(messages) - for index in spec: - if isinstance(index, tuple): # (n, 'c') - context = messages[index[0] - 1] - continue - if last_index < index: - # Not enough arguments - return - message = messages[index - 1] - if message is None: - return - translatable.append(message) - - # keyword spec indexes are 1 based, therefore '-1' - if isinstance(spec[0], tuple): - # context-aware *gettext method - first_msg_index = spec[1] - 1 - else: - first_msg_index = spec[0] - 1 - # An empty string msgid isn't valid, emit a warning - if not messages[first_msg_index]: - filename = getattr(fileobj, "name", None) or "(unknown)" - sys.stderr.write( - f"{filename}:{lineno}: warning: Empty msgid. It is reserved by GNU gettext: gettext(\"\") " - f"returns the header entry with meta information, not the empty string.\n", - ) - return - - translatable = tuple(translatable) - if len(translatable) == 1: - translatable = translatable[0] - - return lineno, translatable, comments, context - - -@lru_cache(maxsize=None) -def _find_extractor(name: str): - for ep_name, load in find_entrypoints(GROUP_NAME): - if ep_name == name: - return load() - return None - - -def extract( - method: _ExtractionMethod, - fileobj: _FileObj, - keywords: Mapping[str, _Keyword] = DEFAULT_KEYWORDS, - comment_tags: Collection[str] = (), - options: Mapping[str, Any] | None = None, - strip_comment_tags: bool = False, -) -> Generator[_ExtractionResult, None, None]: - """Extract messages from the given file-like object using the specified - extraction method. - - This function returns tuples of the form ``(lineno, message, comments, context)``. - - The implementation dispatches the actual extraction to plugins, based on the - value of the ``method`` parameter. - - >>> source = b'''# foo module - ... def run(argv): - ... print(_('Hello, world!')) - ... ''' - - >>> from io import BytesIO - >>> for message in extract('python', BytesIO(source)): - ... print(message) - (3, 'Hello, world!', [], None) - - :param method: an extraction method (a callable), or - a string specifying the extraction method (.e.g. "python"); - if this is a simple name, the extraction function will be - looked up by entry point; if it is an explicit reference - to a function (of the form ``package.module:funcname`` or - ``package.module.funcname``), the corresponding function - will be imported and used - :param fileobj: the file-like object the messages should be extracted from - :param keywords: a dictionary mapping keywords (i.e. names of functions - that should be recognized as translation functions) to - tuples that specify which of their arguments contain - localizable strings - :param comment_tags: a list of translator tags to search for and include - in the results - :param options: a dictionary of additional options (optional) - :param strip_comment_tags: a flag that if set to `True` causes all comment - tags to be removed from the collected comments. - :raise ValueError: if the extraction method is not registered - :returns: iterable of tuples of the form ``(lineno, message, comments, context)`` - :rtype: Iterable[tuple[int, str|tuple[str], list[str], str|None] - """ - if callable(method): - func = method - elif ':' in method or '.' in method: - if ':' not in method: - lastdot = method.rfind('.') - module, attrname = method[:lastdot], method[lastdot + 1 :] - else: - module, attrname = method.split(':', 1) - func = getattr(__import__(module, {}, {}, [attrname]), attrname) - else: - func = _find_extractor(method) - if func is None: - # if no named entry point was found, - # we resort to looking up a builtin extractor - func = _BUILTIN_EXTRACTORS.get(method) - - if func is None: - raise ValueError(f"Unknown extraction method {method!r}") - - results = func(fileobj, keywords.keys(), comment_tags, options=options or {}) - - for lineno, funcname, messages, comments in results: - if not isinstance(messages, (list, tuple)): - messages = [messages] - if not messages: - continue - - specs = keywords[funcname] or None if funcname else None - # {None: x} may be collapsed into x for backwards compatibility. - if not isinstance(specs, dict): - specs = {None: specs} - - if strip_comment_tags: - _strip_comment_tags(comments, comment_tags) - - # None matches all arities. - for arity in (None, len(messages)): - try: - spec = specs[arity] - except KeyError: - continue - if spec is None: - spec = (1,) - result = _match_messages_against_spec(lineno, messages, comments, fileobj, spec) - if result is not None: - yield result - - -def extract_nothing( - fileobj: _FileObj, - keywords: Mapping[str, _Keyword], - comment_tags: Collection[str], - options: Mapping[str, Any], -) -> list[_ExtractionResult]: - """Pseudo extractor that does not actually extract anything, but simply - returns an empty list. - """ - return [] - - -def extract_python( - fileobj: IO[bytes], - keywords: Mapping[str, _Keyword], - comment_tags: Collection[str], - options: _PyOptions, -) -> Generator[_ExtractionResult, None, None]: - """Extract messages from Python source code. - - It returns an iterator yielding tuples in the following form ``(lineno, - funcname, message, comments)``. - - :param fileobj: the seekable, file-like object the messages should be - extracted from - :param keywords: a list of keywords (i.e. function names) that should be - recognized as translation functions - :param comment_tags: a list of translator tags to search for and include - in the results - :param options: a dictionary of additional options (optional) - :rtype: ``iterator`` - """ - funcname = lineno = message_lineno = None - call_stack = [] # line numbers of calls - buf = [] - messages = [] - translator_comments = [] - in_def = in_translator_comments = False - comment_tag = None - - encoding = parse_encoding(fileobj) or options.get('encoding', 'UTF-8') - future_flags = parse_future_flags(fileobj, encoding) - next_line = lambda: fileobj.readline().decode(encoding) - - tokens = generate_tokens(next_line) - - # Current prefix of a Python 3.12 (PEP 701) f-string, or None if we're not - # currently parsing one. - current_fstring_start = None - - for tok, value, (lineno, _), _, _ in tokens: - if not call_stack and tok == NAME and value in ('def', 'class'): - in_def = True - elif tok == OP and value == '(': - if in_def: - # Avoid false positives for declarations such as: - # def gettext(arg='message'): - in_def = False - continue - if funcname: - call_stack.append(lineno) - elif in_def and tok == OP and value == ':': - # End of a class definition without parens - in_def = False - continue - elif not call_stack 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 len(call_stack) == 1: - 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] - - if translator_comments: - last_comment_lineno = translator_comments[-1][0] - if last_comment_lineno < min(message_lineno, call_stack[-1]) - 1: - # Comments don't apply unless they immediately - # precede the message, or the line where the parenthesis token - # to start this message's translation call is. - translator_comments.clear() - - yield ( - message_lineno, - funcname, - messages, - [comment[1] for comment in translator_comments], - ) - - funcname = lineno = message_lineno = None - call_stack.clear() - messages = [] - translator_comments = [] - in_translator_comments = False - if nested: - funcname = value - elif tok == STRING: - val = _parse_python_string(value, encoding, future_flags) - if val is not None: - if not message_lineno: - message_lineno = lineno - buf.append(val) - - # Python 3.12+, see https://peps.python.org/pep-0701/#new-tokens - elif tok == FSTRING_START: - current_fstring_start = value - if not message_lineno: - message_lineno = lineno - elif tok == FSTRING_MIDDLE: - if current_fstring_start is not None: - current_fstring_start += value - elif tok == FSTRING_END: - if current_fstring_start is not None: - fstring = current_fstring_start + value - val = _parse_python_string(fstring, encoding, future_flags) - if val is not None: - buf.append(val) - - elif tok == OP and value == ',': - if buf: - messages.append(''.join(buf)) - del buf[:] - else: - messages.append(None) - if translator_comments: - # We have translator comments, and since we're on a - # comma(,) user is allowed to break into a new line - # Let's increase the last comment's lineno in order - # for the comment to still be a valid one - old_lineno, old_comment = translator_comments.pop() - translator_comments.append((old_lineno + 1, old_comment)) - - elif tok != NL and not message_lineno: - message_lineno = lineno - elif len(call_stack) > 1 and tok == OP and value == ')': - call_stack.pop() - elif funcname and not call_stack: - funcname = None - elif tok == NAME and value in keywords: - funcname = value - - if current_fstring_start is not None and tok not in {FSTRING_START, FSTRING_MIDDLE}: - # In Python 3.12, tokens other than FSTRING_* mean the - # f-string is dynamic, so we don't wan't to extract it. - # And if it's FSTRING_END, we've already handled it above. - # Let's forget that we're in an f-string. - current_fstring_start = None - - -def _parse_python_string(value: str, encoding: str, future_flags: int) -> str | None: - # Unwrap quotes in a safe manner, maintaining the string's encoding - # https://sourceforge.net/tracker/?func=detail&atid=355470&aid=617979&group_id=5470 - code = compile( - f'# coding={str(encoding)}\n{value}', - '', - 'eval', - ast.PyCF_ONLY_AST | future_flags, - ) - if isinstance(code, ast.Expression): - body = code.body - if isinstance(body, ast.Constant): - return body.value - if isinstance(body, ast.JoinedStr): # f-string - if all(isinstance(node, ast.Constant) for node in body.values): - return ''.join(node.value for node in body.values) - # TODO: we could raise an error or warning when not all nodes are constants - return None - - -def extract_javascript( - fileobj: _FileObj, - keywords: Mapping[str, _Keyword], - comment_tags: Collection[str], - options: _JSOptions, - lineno: int = 1, -) -> Generator[_ExtractionResult, None, None]: - """Extract messages from JavaScript source code. - - :param fileobj: the seekable, file-like object the messages should be - extracted from - :param keywords: a list of keywords (i.e. function names) that should be - recognized as translation functions - :param comment_tags: a list of translator tags to search for and include - in the results - :param options: a dictionary of additional options (optional) - Supported options are: - * `jsx` -- set to false to disable JSX/E4X support. - * `template_string` -- if `True`, supports gettext(`key`) - * `parse_template_string` -- if `True` will parse the - contents of javascript - template strings. - :param lineno: line number offset (for parsing embedded fragments) - """ - from babel.messages.jslexer import Token, tokenize, unquote_string - - funcname = message_lineno = None - messages = [] - last_argument = None - translator_comments = [] - concatenate_next = False - encoding = options.get('encoding', 'utf-8') - last_token = None - call_stack = -1 - dotted = any('.' in kw for kw in keywords) - for token in tokenize( - fileobj.read().decode(encoding), - jsx=options.get("jsx", True), - template_string=options.get("template_string", True), - dotted=dotted, - lineno=lineno, - ): - if ( # Turn keyword`foo` expressions into keyword("foo") calls: - # have a keyword... - funcname - # and we've seen nothing after the keyword... - and (last_token and last_token.type == 'name') - # and this is a template string - and token.type == 'template_string' - ): - message_lineno = token.lineno - messages = [unquote_string(token.value)] - call_stack = 0 - token = Token('operator', ')', token.lineno) - - if ( - options.get('parse_template_string') - and not funcname - and token.type == 'template_string' - ): - yield from parse_template_string( - token.value, - keywords, - comment_tags, - options, - token.lineno, - ) - - elif token.type == 'operator' and token.value == '(': - if funcname: - message_lineno = token.lineno - call_stack += 1 - - elif call_stack == -1 and token.type == 'linecomment': - value = token.value[2:].strip() - if translator_comments and translator_comments[-1][0] == token.lineno - 1: - translator_comments.append((token.lineno, value)) - continue - - for comment_tag in comment_tags: - if value.startswith(comment_tag): - translator_comments.append((token.lineno, value.strip())) - break - - elif token.type == 'multilinecomment': - # only one multi-line comment may precede a translation - translator_comments = [] - value = token.value[2:-2].strip() - for comment_tag in comment_tags: - if value.startswith(comment_tag): - lines = value.splitlines() - if lines: - lines[0] = lines[0].strip() - lines[1:] = dedent('\n'.join(lines[1:])).splitlines() - for offset, line in enumerate(lines): - translator_comments.append((token.lineno + offset, line)) - break - - elif funcname and call_stack == 0: - if token.type == 'operator' and token.value == ')': - if last_argument is not None: - messages.append(last_argument) - if len(messages) > 1: - messages = tuple(messages) - elif messages: - messages = messages[0] - else: - messages = None - - # Comments don't apply unless they immediately precede the - # message - if translator_comments and translator_comments[-1][0] < message_lineno - 1: - translator_comments = [] - - if messages is not None: - yield ( - message_lineno, - funcname, - messages, - [comment[1] for comment in translator_comments], - ) - - funcname = message_lineno = last_argument = None - concatenate_next = False - translator_comments = [] - messages = [] - call_stack = -1 - - elif token.type in ('string', 'template_string'): - new_value = unquote_string(token.value) - if concatenate_next: - last_argument = (last_argument or '') + new_value - concatenate_next = False - else: - last_argument = new_value - - elif token.type == 'operator': - if token.value == ',': - if last_argument is not None: - messages.append(last_argument) - last_argument = None - else: - messages.append(None) - concatenate_next = False - elif token.value == '+': - concatenate_next = True - - elif call_stack > 0 and token.type == 'operator' and token.value == ')': - call_stack -= 1 - - elif funcname and call_stack == -1: - funcname = None - - elif ( - call_stack == -1 - and token.type == 'name' - and token.value in keywords - and ( - last_token is None - or last_token.type != 'name' - or last_token.value != 'function' - ) - ): - funcname = token.value - - last_token = token - - -def parse_template_string( - template_string: str, - keywords: Mapping[str, _Keyword], - comment_tags: Collection[str], - options: _JSOptions, - lineno: int = 1, -) -> Generator[_ExtractionResult, None, None]: - """Parse JavaScript template string. - - :param template_string: the template string to be parsed - :param keywords: a list of keywords (i.e. function names) that should be - recognized as translation functions - :param comment_tags: a list of translator tags to search for and include - in the results - :param options: a dictionary of additional options (optional) - :param lineno: starting line number (optional) - """ - from babel.messages.jslexer import line_re - - prev_character = None - level = 0 - inside_str = False - expression_contents = '' - for character in template_string[1:-1]: - if not inside_str and character in ('"', "'", '`'): - inside_str = character - elif inside_str == character and prev_character != r'\\': - inside_str = False - if level: - expression_contents += character - if not inside_str: - if character == '{' and prev_character == '$': - level += 1 - elif level and character == '}': - level -= 1 - if level == 0 and expression_contents: - expression_contents = expression_contents[0:-1] - fake_file_obj = io.BytesIO(expression_contents.encode()) - yield from extract_javascript( - fake_file_obj, - keywords, - comment_tags, - options, - lineno, - ) - lineno += len(line_re.findall(expression_contents)) - expression_contents = '' - prev_character = character - - -_BUILTIN_EXTRACTORS = { - 'ignore': extract_nothing, - 'python': extract_python, - 'javascript': extract_javascript, -} diff --git a/.venv/lib/python3.12/site-packages/babel/messages/frontend.py b/.venv/lib/python3.12/site-packages/babel/messages/frontend.py deleted file mode 100644 index f63dd9de..00000000 --- a/.venv/lib/python3.12/site-packages/babel/messages/frontend.py +++ /dev/null @@ -1,1321 +0,0 @@ -""" -babel.messages.frontend -~~~~~~~~~~~~~~~~~~~~~~~ - -Frontends for the message extraction functionality. - -:copyright: (c) 2013-2026 by the Babel Team. -:license: BSD, see LICENSE for more details. -""" - -from __future__ import annotations - -import datetime -import fnmatch -import logging -import optparse -import os -import pathlib -import re -import shutil -import sys -import tempfile -import warnings -from configparser import RawConfigParser -from io import StringIO -from typing import Any, BinaryIO, Iterable, Literal - -from babel import Locale, localedata -from babel import __version__ as VERSION -from babel.core import UnknownLocaleError -from babel.messages.catalog import DEFAULT_HEADER, Catalog -from babel.messages.extract import ( - DEFAULT_KEYWORDS, - DEFAULT_MAPPING, - check_and_call_extract_file, - extract_from_dir, -) -from babel.messages.mofile import write_mo -from babel.messages.pofile import read_po, write_po -from babel.util import LOCALTZ - -log = logging.getLogger('babel') - - -class BaseError(Exception): - pass - - -class OptionError(BaseError): - pass - - -class SetupError(BaseError): - pass - - -class ConfigurationError(BaseError): - """ - Raised for errors in configuration files. - """ - - -def listify_value(arg, split=None): - """ - Make a list out of an argument. - - Values from `distutils` argument parsing are always single strings; - values from `optparse` parsing may be lists of strings that may need - to be further split. - - No matter the input, this function returns a flat list of whitespace-trimmed - strings, with `None` values filtered out. - - >>> listify_value("foo bar") - ['foo', 'bar'] - >>> listify_value(["foo bar"]) - ['foo', 'bar'] - >>> listify_value([["foo"], "bar"]) - ['foo', 'bar'] - >>> listify_value([["foo"], ["bar", None, "foo"]]) - ['foo', 'bar', 'foo'] - >>> listify_value("foo, bar, quux", ",") - ['foo', 'bar', 'quux'] - - :param arg: A string or a list of strings - :param split: The argument to pass to `str.split()`. - :return: - """ - out = [] - - if not isinstance(arg, (list, tuple)): - arg = [arg] - - for val in arg: - if val is None: - continue - if isinstance(val, (list, tuple)): - out.extend(listify_value(val, split=split)) - continue - out.extend(s.strip() for s in str(val).split(split)) - assert all(isinstance(val, str) for val in out) - return out - - -class CommandMixin: - # This class is a small shim between Distutils commands and - # optparse option parsing in the frontend command line. - - #: Option name to be input as `args` on the script command line. - as_args = None - - #: Options which allow multiple values. - #: This is used by the `optparse` transmogrification code. - multiple_value_options = () - - #: Options which are booleans. - #: This is used by the `optparse` transmogrification code. - # (This is actually used by distutils code too, but is never - # declared in the base class.) - boolean_options = () - - #: Option aliases, to retain standalone command compatibility. - #: Distutils does not support option aliases, but optparse does. - #: This maps the distutils argument name to an iterable of aliases - #: that are usable with optparse. - option_aliases = {} - - #: Choices for options that needed to be restricted to specific - #: list of choices. - option_choices = {} - - #: Log object. To allow replacement in the script command line runner. - log = log - - def __init__(self, dist=None): - # A less strict version of distutils' `__init__`. - self.distribution = dist - self.initialize_options() - self._dry_run = None - self.verbose = False - self.force = None - self.help = 0 - self.finalized = 0 - - def initialize_options(self): - pass - - def ensure_finalized(self): - if not self.finalized: - self.finalize_options() - self.finalized = 1 - - def finalize_options(self): - raise RuntimeError( - f"abstract method -- subclass {self.__class__} must override", - ) - - -class CompileCatalog(CommandMixin): - description = 'compile message catalogs to binary MO files' - user_options = [ - ('domain=', 'D', - "domains of PO files (space separated list, default 'messages')"), - ('directory=', 'd', - 'path to base directory containing the catalogs'), - ('input-file=', 'i', - 'name of the input file'), - ('output-file=', 'o', - "name of the output file (default " - "'//LC_MESSAGES/.mo')"), - ('locale=', 'l', - 'locale of the catalog to compile'), - ('use-fuzzy', 'f', - 'also include fuzzy translations'), - ('statistics', None, - 'print statistics about translations'), - ] # fmt: skip - 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 _get_po_mo_triples(self, domain: str): - if not self.input_file: - dir_path = pathlib.Path(self.directory) - if self.locale: - lc_messages_path = dir_path / self.locale / "LC_MESSAGES" - po_file = lc_messages_path / f"{domain}.po" - yield self.locale, po_file, po_file.with_suffix(".mo") - else: - for locale_path in dir_path.iterdir(): - po_file = locale_path / "LC_MESSAGES" / f"{domain}.po" - if po_file.exists(): - yield locale_path.name, po_file, po_file.with_suffix(".mo") - else: - po_file = pathlib.Path(self.input_file) - if self.output_file: - mo_file = pathlib.Path(self.output_file) - else: - mo_file = ( - pathlib.Path(self.directory) / self.locale / "LC_MESSAGES" / f"{domain}.mo" - ) - yield self.locale, po_file, mo_file - - def _run_domain(self, domain): - locale_po_mo_triples = list(self._get_po_mo_triples(domain)) - if not locale_po_mo_triples: - raise OptionError(f'no message catalogs found for domain {domain!r}') - - catalogs_and_errors = {} - - for locale, po_file, mo_file in locale_po_mo_triples: - with open(po_file, 'rb') as infile: - catalog = read_po(infile, locale) - - if self.statistics: - translated = 0 - for message in list(catalog)[1:]: - if message.string: - translated += 1 - percentage = 0 - if len(catalog): - percentage = translated * 100 // len(catalog) - self.log.info( - '%d of %d messages (%d%%) translated in %s', - translated, - len(catalog), - percentage, - po_file, - ) - - if catalog.fuzzy and not self.use_fuzzy: - self.log.info('catalog %s is marked as fuzzy, skipping', po_file) - continue - - catalogs_and_errors[catalog] = catalog_errors = list(catalog.check()) - for message, errors in catalog_errors: - for error in errors: - self.log.error('error: %s:%d: %s', po_file, message.lineno, error) - - self.log.info('compiling catalog %s to %s', po_file, mo_file) - - with open(mo_file, 'wb') as outfile: - write_mo(outfile, catalog, use_fuzzy=self.use_fuzzy) - - return catalogs_and_errors - - -def _make_directory_filter(ignore_patterns): - """ - Build a directory_filter function based on a list of ignore patterns. - """ - - def cli_directory_filter(dirname): - basename = os.path.basename(dirname) - return not any( - fnmatch.fnmatch(basename, ignore_pattern) for ignore_pattern in ignore_patterns - ) - - return cli_directory_filter - - -class ExtractMessages(CommandMixin): - description = 'extract localizable strings from the project code' - user_options = [ - ('charset=', None, - 'charset to use in the output file (default "utf-8")'), - ('keywords=', 'k', - 'space-separated list of keywords to look for in addition to the ' - 'defaults (may be repeated multiple times)'), - ('no-default-keywords', None, - 'do not include the default keywords'), - ('mapping-file=', 'F', - 'path to the mapping configuration file'), - ('no-location', None, - 'do not include location comments with filename and line number'), - ('add-location=', None, - 'location lines format. If it is not given or "full", it generates ' - 'the lines with both file name and line number. If it is "file", ' - 'the line number part is omitted. If it is "never", it completely ' - 'suppresses the lines (same as --no-location).'), - ('omit-header', None, - 'do not include msgid "" entry in header'), - ('output-file=', 'o', - 'name of the output file'), - ('width=', 'w', - 'set output line width (default 76)'), - ('no-wrap', None, - 'do not break long message lines, longer than the output line width, ' - 'into several lines'), - ('sort-output', None, - 'generate sorted output (default False)'), - ('sort-by-file', None, - 'sort output by file location (default False)'), - ('msgid-bugs-address=', None, - 'set report address for msgid'), - ('copyright-holder=', None, - 'set copyright holder in output'), - ('project=', None, - 'set project name in output'), - ('version=', None, - 'set project version in output'), - ('add-comments=', 'c', - 'place comment block with TAG (or those preceding keyword lines) in ' - 'output file. Separate multiple TAGs with commas(,)'), # TODO: Support repetition of this argument - ('strip-comments', 's', - 'strip the comment TAGs from the comments.'), - ('input-paths=', None, - 'files or directories that should be scanned for messages. Separate multiple ' - 'files or directories with commas(,)'), # TODO: Support repetition of this argument - ('input-dirs=', None, # TODO (3.x): Remove me. - 'alias for input-paths (does allow files as well as directories).'), - ('ignore-dirs=', None, - 'Patterns for directories to ignore when scanning for messages. ' - 'Separate multiple patterns with spaces (default ".* ._")'), - ('header-comment=', None, - 'header comment for the catalog'), - ('last-translator=', None, - 'set the name and email of the last translator in output'), - ] # fmt: skip - boolean_options = [ - 'no-default-keywords', - 'no-location', - 'omit-header', - 'no-wrap', - 'sort-output', - 'sort-by-file', - 'strip-comments', - ] - as_args = 'input-paths' - multiple_value_options = ( - 'add-comments', - 'keywords', - 'ignore-dirs', - ) - option_aliases = { - 'keywords': ('--keyword',), - 'mapping-file': ('--mapping',), - 'output-file': ('--output',), - 'strip-comments': ('--strip-comment-tags',), - 'last-translator': ('--last-translator',), - } - option_choices = { - 'add-location': ('full', 'file', 'never'), - } - - def initialize_options(self): - self.charset = 'utf-8' - self.keywords = None - self.no_default_keywords = False - self.mapping_file = None - self.no_location = False - self.add_location = None - self.omit_header = False - self.output_file = None - self.input_dirs = None - self.input_paths = None - self.width = None - self.no_wrap = False - self.sort_output = False - self.sort_by_file = False - self.msgid_bugs_address = None - self.copyright_holder = None - self.project = None - self.version = None - self.add_comments = None - self.strip_comments = False - self.include_lineno = True - self.ignore_dirs = None - self.header_comment = None - self.last_translator = None - - def finalize_options(self): - if self.input_dirs: - if not self.input_paths: - self.input_paths = self.input_dirs - else: - raise OptionError( - 'input-dirs and input-paths are mutually exclusive', - ) - - keywords = {} if self.no_default_keywords else DEFAULT_KEYWORDS.copy() - - keywords.update(parse_keywords(listify_value(self.keywords))) - - self.keywords = keywords - - if not self.keywords: - raise OptionError( - 'you must specify new keywords if you disable the default ones', - ) - - if not self.output_file: - raise OptionError('no output file specified') - if self.no_wrap and self.width: - raise OptionError( - "'--no-wrap' and '--width' are mutually exclusive", - ) - if not self.no_wrap and not self.width: - self.width = 76 - elif self.width is not None: - self.width = int(self.width) - - if self.sort_output and self.sort_by_file: - raise OptionError( - "'--sort-output' and '--sort-by-file' are mutually exclusive", - ) - - if self.input_paths: - if isinstance(self.input_paths, str): - self.input_paths = re.split(r',\s*', self.input_paths) - elif self.distribution is not None: - self.input_paths = list( - {k.split('.', 1)[0] for k in (self.distribution.packages or ())}, - ) - else: - self.input_paths = [] - - if not self.input_paths: - raise OptionError("no input files or directories specified") - - for path in self.input_paths: - if not os.path.exists(path): - raise OptionError(f"Input path: {path} does not exist") - - self.add_comments = listify_value(self.add_comments or (), ",") - - if self.distribution: - if not self.project: - self.project = self.distribution.get_name() - if not self.version: - self.version = self.distribution.get_version() - - if self.add_location == 'never': - self.no_location = True - elif self.add_location == 'file': - self.include_lineno = False - - ignore_dirs = listify_value(self.ignore_dirs) - if ignore_dirs: - self.directory_filter = _make_directory_filter(ignore_dirs) - else: - self.directory_filter = None - - def _build_callback(self, path: str): - def callback(filename: str, method: str, options: dict): - if method == 'ignore': - return - - # If we explicitly provide a full filepath, just use that. - # Otherwise, path will be the directory path and filename - # is the relative path from that dir to the file. - # So we can join those to get the full filepath. - if os.path.isfile(path): - filepath = path - else: - filepath = os.path.normpath(os.path.join(path, filename)) - - optstr = '' - if options: - opt_values = ", ".join(f'{k}="{v}"' for k, v in options.items()) - optstr = f" ({opt_values})" - self.log.info('extracting messages from %s%s', filepath, optstr) - - return callback - - def run(self): - mappings = self._get_mappings() - with open(self.output_file, 'wb') as outfile: - catalog = Catalog( - project=self.project, - version=self.version, - msgid_bugs_address=self.msgid_bugs_address, - copyright_holder=self.copyright_holder, - charset=self.charset, - header_comment=(self.header_comment or DEFAULT_HEADER), - last_translator=self.last_translator, - ) - - for path, method_map, options_map in mappings: - callback = self._build_callback(path) - if os.path.isfile(path): - current_dir = os.getcwd() - extracted = check_and_call_extract_file( - path, - method_map, - options_map, - callback=callback, - comment_tags=self.add_comments, - dirpath=current_dir, - keywords=self.keywords, - strip_comment_tags=self.strip_comments, - ) - else: - extracted = extract_from_dir( - path, - method_map, - options_map, - callback=callback, - comment_tags=self.add_comments, - directory_filter=self.directory_filter, - keywords=self.keywords, - strip_comment_tags=self.strip_comments, - ) - 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, - include_lineno=self.include_lineno, - no_location=self.no_location, - omit_header=self.omit_header, - sort_by_file=self.sort_by_file, - sort_output=self.sort_output, - width=self.width, - ) - - def _get_mappings(self): - mappings = [] - - if self.mapping_file: - if self.mapping_file.endswith(".toml"): - with open(self.mapping_file, "rb") as fileobj: - file_style = ( - "pyproject.toml" - if os.path.basename(self.mapping_file) == "pyproject.toml" - else "standalone" - ) - method_map, options_map = _parse_mapping_toml( - fileobj, - filename=self.mapping_file, - style=file_style, - ) - else: - with open(self.mapping_file) as fileobj: - method_map, options_map = parse_mapping_cfg( - fileobj, - filename=self.mapping_file, - ) - for path in self.input_paths: - mappings.append((path, method_map, options_map)) - - elif getattr(self.distribution, 'message_extractors', None): - message_extractors = self.distribution.message_extractors - for path, mapping in message_extractors.items(): - if isinstance(mapping, str): - method_map, options_map = parse_mapping_cfg(StringIO(mapping)) - else: - method_map, options_map = [], {} - for pattern, method, options in mapping: - method_map.append((pattern, method)) - options_map[pattern] = _parse_string_options(options or {}) - mappings.append((path, method_map, options_map)) - - else: - for path in self.input_paths: - mappings.append((path, DEFAULT_MAPPING, {})) - - return mappings - - -def _init_catalog(*, input_file, output_file, locale: Locale, width: int) -> None: - with open(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=locale) - - catalog.locale = locale - catalog.revision_date = datetime.datetime.now(LOCALTZ) - catalog.fuzzy = False - - if dirname := os.path.dirname(output_file): - os.makedirs(dirname, exist_ok=True) - - with open(output_file, 'wb') as outfile: - write_po(outfile, catalog, width=width) - - -class InitCatalog(CommandMixin): - description = 'create a new catalog based on a POT file' - user_options = [ - ('domain=', 'D', - "domain of PO file (default 'messages')"), - ('input-file=', 'i', - 'name of the input file'), - ('output-dir=', 'd', - 'path to output directory'), - ('output-file=', 'o', - "name of the output file (default " - "'//LC_MESSAGES/.po')"), - ('locale=', 'l', - 'locale for the new localized catalog'), - ('width=', 'w', - 'set output line width (default 76)'), - ('no-wrap', None, - 'do not break long message lines, longer than the output line width, ' - 'into several lines'), - ] # fmt: skip - 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: - lc_messages_path = pathlib.Path(self.output_dir) / self.locale / "LC_MESSAGES" - self.output_file = str(lc_messages_path / f"{self.domain}.po") - - 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, - ) - _init_catalog( - input_file=self.input_file, - output_file=self.output_file, - locale=self._locale, - width=self.width, - ) - - -class UpdateCatalog(CommandMixin): - description = 'update message catalogs from a POT file' - user_options = [ - ('domain=', 'D', - "domain of PO file (default 'messages')"), - ('input-file=', 'i', - 'name of the input file'), - ('output-dir=', 'd', - 'path to base directory containing the catalogs'), - ('output-file=', 'o', - "name of the output file (default " - "'//LC_MESSAGES/.po')"), - ('omit-header', None, - "do not include msgid "" entry in header"), - ('locale=', 'l', - 'locale of the catalog to compile'), - ('width=', 'w', - 'set output line width (default 76)'), - ('no-wrap', None, - 'do not break long message lines, longer than the output line width, ' - 'into several lines'), - ('ignore-obsolete=', None, - 'whether to omit obsolete messages from the output'), - ('init-missing=', None, - 'if any output files are missing, initialize them first'), - ('no-fuzzy-matching', 'N', - 'do not use fuzzy matching'), - ('update-header-comment', None, - 'update target header comment'), - ('previous', None, - 'keep previous msgids of translated messages'), - ('check=', None, - 'don\'t update the catalog, just return the status. Return code 0 ' - 'means nothing would change. Return code 1 means that the catalog ' - 'would be updated'), - ('ignore-pot-creation-date=', None, - 'ignore changes to POT-Creation-Date when updating or checking'), - ] # fmt: skip - boolean_options = [ - 'omit-header', - 'no-wrap', - 'ignore-obsolete', - 'init-missing', - 'no-fuzzy-matching', - 'previous', - 'update-header-comment', - 'check', - 'ignore-pot-creation-date', - ] - - def initialize_options(self): - self.domain = 'messages' - self.input_file = None - self.output_dir = None - self.output_file = None - self.omit_header = False - self.locale = None - self.width = None - self.no_wrap = False - self.ignore_obsolete = False - self.init_missing = False - self.no_fuzzy_matching = False - self.update_header_comment = False - self.previous = False - self.check = False - self.ignore_pot_creation_date = False - - def finalize_options(self): - if not self.input_file: - raise OptionError('you must specify the input file') - if not self.output_file and not self.output_dir: - raise OptionError('you must specify the output file or directory') - if self.output_file and not self.locale: - raise OptionError('you must specify the locale') - - if self.init_missing: - if not self.locale: - raise OptionError( - 'you must specify the locale for the init-missing option to work', - ) - - try: - self._locale = Locale.parse(self.locale) - except UnknownLocaleError as e: - raise OptionError(e) from e - else: - self._locale = None - - if self.no_wrap and self.width: - raise OptionError("'--no-wrap' and '--width' are mutually exclusive") - if not self.no_wrap and not self.width: - self.width = 76 - elif self.width is not None: - self.width = int(self.width) - if self.no_fuzzy_matching and self.previous: - self.previous = False - - def _get_locale_po_file_tuples(self): - if not self.output_file: - output_path = pathlib.Path(self.output_dir) - if self.locale: - lc_messages_path = output_path / self.locale / "LC_MESSAGES" - yield self.locale, str(lc_messages_path / f"{self.domain}.po") - else: - for locale_path in output_path.iterdir(): - po_file = locale_path / "LC_MESSAGES" / f"{self.domain}.po" - if po_file.exists(): - yield locale_path.stem, po_file - else: - yield self.locale, self.output_file - - def run(self): - domain = self.domain - if not domain: - domain = os.path.splitext(os.path.basename(self.input_file))[0] - - check_status = {} - locale_po_file_tuples = list(self._get_locale_po_file_tuples()) - - if not locale_po_file_tuples: - raise OptionError(f'no message catalogs found for domain {domain!r}') - - with open(self.input_file, 'rb') as infile: - template = read_po(infile) - - for locale, filename in locale_po_file_tuples: - 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, - ) - - _init_catalog( - input_file=self.input_file, - output_file=filename, - locale=self._locale, - width=self.width, - ) - - 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, - no_fuzzy_matching=self.no_fuzzy_matching, - update_header_comment=self.update_header_comment, - update_creation_date=not self.ignore_pot_creation_date, - ) - - tmpname = os.path.join( - os.path.dirname(filename), - tempfile.gettempprefix() + os.path.basename(filename), - ) - try: - with open(tmpname, 'wb') as tmpfile: - write_po( - tmpfile, - catalog, - ignore_obsolete=self.ignore_obsolete, - include_previous=self.previous, - omit_header=self.omit_header, - width=self.width, - ) - except Exception: - os.remove(tmpname) - raise - - if self.check: - with open(filename, "rb") as origfile: - original_catalog = read_po(origfile) - with open(tmpname, "rb") as newfile: - updated_catalog = read_po(newfile) - updated_catalog.revision_date = original_catalog.revision_date - check_status[filename] = updated_catalog.is_identical(original_catalog) - os.remove(tmpname) - continue - - try: - os.rename(tmpname, filename) - except OSError: - # We're probably on Windows, which doesn't support atomic - # renames, at least not through Python - # If the error is in fact due to a permissions problem, that - # same error is going to be raised from one of the following - # operations - os.remove(filename) - shutil.copy(tmpname, filename) - os.remove(tmpname) - - if self.check: - for filename, up_to_date in check_status.items(): - if up_to_date: - self.log.info('Catalog %s is up to date.', filename) - else: - self.log.warning('Catalog %s is out of date.', filename) - if not all(check_status.values()): - raise BaseError("Some catalogs are out of date.") - else: - self.log.info("All the catalogs are up-to-date.") - return - - -class CommandLineInterface: - """Command-line interface. - - This class provides a simple command-line interface to the message - extraction and PO file generation functionality. - """ - - usage = '%%prog %s [options] %s' - version = f'%prog {VERSION}' - commands = { - 'compile': 'compile message catalogs to MO files', - 'extract': 'extract messages from source files and generate a POT file', - 'init': 'create new message catalogs from a POT file', - 'update': 'update existing message catalogs from a POT file', - } - - command_classes = { - 'compile': CompileCatalog, - 'extract': ExtractMessages, - 'init': InitCatalog, - 'update': UpdateCatalog, - } - - log = None # Replaced on instance level - - def run(self, argv=None): - """Main entry point of the command-line interface. - - :param argv: list of arguments passed on the command-line - """ - - if argv is None: - argv = sys.argv - - self.parser = optparse.OptionParser( - usage=self.usage % ('command', '[args]'), - version=self.version, - ) - self.parser.disable_interspersed_args() - self.parser.print_help = self._help - self.parser.add_option( - "--list-locales", - dest="list_locales", - action="store_true", - help="print all known locales and exit", - ) - self.parser.add_option( - "-v", - "--verbose", - action="store_const", - dest="loglevel", - const=logging.DEBUG, - help="print as much as possible", - ) - self.parser.add_option( - "-q", - "--quiet", - action="store_const", - dest="loglevel", - const=logging.ERROR, - help="print as little as possible", - ) - self.parser.set_defaults(list_locales=False, loglevel=logging.INFO) - - options, args = self.parser.parse_args(argv[1:]) - - self._configure_logging(options.loglevel) - if options.list_locales: - identifiers = localedata.locale_identifiers() - id_width = max(len(identifier) for identifier in identifiers) + 1 - for identifier in sorted(identifiers): - locale = Locale.parse(identifier) - print(f"{identifier:<{id_width}} {locale.english_name}") - return 0 - - if not args: - self.parser.error( - "no valid command or option passed. " - "Try the -h/--help option for more information.", - ) - - cmdname = args[0] - if cmdname not in self.commands: - self.parser.error(f'unknown command "{cmdname}"') - - cmdinst = self._configure_command(cmdname, args[1:]) - return cmdinst.run() - - def _configure_logging(self, loglevel): - self.log = log - self.log.setLevel(loglevel) - # Don't add a new handler for every instance initialization (#227), this - # would cause duplicated output when the CommandLineInterface as an - # normal Python class. - if self.log.handlers: - handler = self.log.handlers[0] - else: - handler = logging.StreamHandler() - self.log.addHandler(handler) - handler.setLevel(loglevel) - formatter = logging.Formatter('%(message)s') - handler.setFormatter(formatter) - - def _help(self): - print(self.parser.format_help()) - print("commands:") - cmd_width = max(8, max(len(command) for command in self.commands) + 1) - for name, description in sorted(self.commands.items()): - print(f" {name:<{cmd_width}} {description}") - - def _configure_command(self, cmdname, argv): - """ - :type cmdname: str - :type argv: list[str] - """ - cmdclass = self.command_classes[cmdname] - cmdinst = cmdclass() - if self.log: - cmdinst.log = self.log # Use our logger, not distutils'. - assert isinstance(cmdinst, CommandMixin) - cmdinst.initialize_options() - - parser = optparse.OptionParser( - usage=self.usage % (cmdname, ''), - description=self.commands[cmdname], - ) - as_args: str | None = getattr(cmdclass, "as_args", None) - for long, short, help in cmdclass.user_options: - name = long.strip("=") - default = getattr(cmdinst, name.replace("-", "_")) - strs = [f"--{name}"] - if short: - strs.append(f"-{short}") - strs.extend(cmdclass.option_aliases.get(name, ())) - choices = cmdclass.option_choices.get(name, None) - if name == as_args: - parser.usage += f"<{name}>" - elif name in cmdclass.boolean_options: - parser.add_option(*strs, action="store_true", help=help) - elif name in cmdclass.multiple_value_options: - parser.add_option(*strs, action="append", help=help, choices=choices) - else: - parser.add_option(*strs, help=help, default=default, choices=choices) - options, args = parser.parse_args(argv) - - if as_args: - setattr(options, as_args.replace('-', '_'), args) - - for key, value in vars(options).items(): - setattr(cmdinst, key, value) - - try: - cmdinst.ensure_finalized() - except OptionError as err: - parser.error(str(err)) - - return cmdinst - - -def main(): - return CommandLineInterface().run(sys.argv) - - -def parse_mapping(fileobj, filename=None): - warnings.warn( - "parse_mapping is deprecated, use parse_mapping_cfg instead", - DeprecationWarning, - stacklevel=2, - ) - return parse_mapping_cfg(fileobj, filename) - - -def parse_mapping_cfg(fileobj, filename=None): - """Parse an extraction method mapping from a file-like object. - - :param fileobj: a readable file-like object containing the configuration - text to parse - :param filename: the name of the file being parsed, for error messages - """ - extractors = {} - method_map = [] - options_map = {} - - parser = RawConfigParser() - parser.read_file(fileobj, filename) - - for section in parser.sections(): - if section == 'extractors': - extractors = dict(parser.items(section)) - else: - method, pattern = (part.strip() for part in section.split(':', 1)) - method_map.append((pattern, method)) - options_map[pattern] = _parse_string_options(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_string_options(options: dict[str, str]) -> dict[str, Any]: - """ - Parse string-formatted options from a mapping configuration. - - The `keywords` and `add_comments` options are parsed into a canonical - internal format, so they can be merged with global keywords/comment tags - during extraction. - """ - options: dict[str, Any] = options.copy() - - if keywords_val := options.pop("keywords", None): - options['keywords'] = parse_keywords(listify_value(keywords_val)) - - if comments_val := options.pop("add_comments", None): - options['add_comments'] = listify_value(comments_val) - - return options - - -def _parse_config_object(config: dict, *, filename="(unknown)"): - extractors = {} - method_map = [] - options_map = {} - - extractors_read = config.get("extractors", {}) - if not isinstance(extractors_read, dict): - raise ConfigurationError( - f"{filename}: extractors: Expected a dictionary, got {type(extractors_read)!r}", - ) - for method, callable_spec in extractors_read.items(): - if not isinstance(method, str): - # Impossible via TOML, but could happen with a custom object. - raise ConfigurationError( - f"{filename}: extractors: Extraction method must be a string, got {method!r}", - ) - if not isinstance(callable_spec, str): - raise ConfigurationError( - f"{filename}: extractors: Callable specification must be a string, got {callable_spec!r}", - ) - extractors[method] = callable_spec - - if "mapping" in config: - raise ConfigurationError( - f"{filename}: 'mapping' is not a valid key, did you mean 'mappings'?", - ) - - mappings_read = config.get("mappings", []) - if not isinstance(mappings_read, list): - raise ConfigurationError( - f"{filename}: mappings: Expected a list, got {type(mappings_read)!r}", - ) - for idx, entry in enumerate(mappings_read): - if not isinstance(entry, dict): - raise ConfigurationError( - f"{filename}: mappings[{idx}]: Expected a dictionary, got {type(entry)!r}", - ) - entry = entry.copy() - - method = entry.pop("method", None) - if not isinstance(method, str): - raise ConfigurationError( - f"{filename}: mappings[{idx}]: 'method' must be a string, got {method!r}", - ) - method = extractors.get(method, method) # Map the extractor name to the callable now - - pattern = entry.pop("pattern", None) - if not isinstance(pattern, (list, str)): - raise ConfigurationError( - f"{filename}: mappings[{idx}]: 'pattern' must be a list or a string, got {pattern!r}", - ) - if not isinstance(pattern, list): - pattern = [pattern] - - if keywords_val := entry.pop("keywords", None): - if isinstance(keywords_val, str): - entry["keywords"] = parse_keywords(listify_value(keywords_val)) - elif isinstance(keywords_val, list): - entry["keywords"] = parse_keywords(keywords_val) - else: - raise ConfigurationError( - f"{filename}: mappings[{idx}]: 'keywords' must be a string or list, got {keywords_val!r}", - ) - - if comments_val := entry.pop("add_comments", None): - if isinstance(comments_val, str): - entry["add_comments"] = [comments_val] - elif isinstance(comments_val, list): - entry["add_comments"] = comments_val - else: - raise ConfigurationError( - f"{filename}: mappings[{idx}]: 'add_comments' must be a string or list, got {comments_val!r}", - ) - - for pat in pattern: - if not isinstance(pat, str): - raise ConfigurationError( - f"{filename}: mappings[{idx}]: 'pattern' elements must be strings, got {pat!r}", - ) - method_map.append((pat, method)) - options_map[pat] = entry - - return method_map, options_map - - -def _parse_mapping_toml( - fileobj: BinaryIO, - filename: str = "(unknown)", - style: Literal["standalone", "pyproject.toml"] = "standalone", -): - """Parse an extraction method mapping from a binary file-like object. - - .. warning: As of this version of Babel, this is a private API subject to changes. - - :param fileobj: a readable binary file-like object containing the configuration TOML to parse - :param filename: the name of the file being parsed, for error messages - :param style: whether the file is in the style of a `pyproject.toml` file, i.e. whether to look for `tool.babel`. - """ - try: - import tomllib - except ImportError: - try: - import tomli as tomllib - except ImportError as ie: # pragma: no cover - raise ImportError("tomli or tomllib is required to parse TOML files") from ie - - try: - parsed_data = tomllib.load(fileobj) - except tomllib.TOMLDecodeError as e: - raise ConfigurationError(f"{filename}: Error parsing TOML file: {e}") from e - - if style == "pyproject.toml": - try: - babel_data = parsed_data["tool"]["babel"] - except (TypeError, KeyError) as e: - raise ConfigurationError( - f"{filename}: No 'tool.babel' section found in file", - ) from e - elif style == "standalone": - babel_data = parsed_data - if "babel" in babel_data: - raise ConfigurationError( - f"{filename}: 'babel' should not be present in a stand-alone configuration file", - ) - else: # pragma: no cover - raise ValueError(f"Unknown TOML style {style!r}") - - return _parse_config_object(babel_data, filename=filename) - - -def _parse_spec(s: str) -> tuple[int | None, tuple[int | tuple[int, str], ...]]: - inds = [] - number = None - for x in s.split(','): - if x[-1] == 't': - number = int(x[:-1]) - elif x[-1] == 'c': - inds.append((int(x[:-1]), 'c')) - else: - inds.append(int(x)) - return number, tuple(inds) - - -def parse_keywords(strings: Iterable[str] = ()): - """Parse keywords specifications from the given list of strings. - - >>> import pprint - >>> keywords = ['_', 'dgettext:2', 'dngettext:2,3', 'pgettext:1c,2', - ... 'polymorphic:1', 'polymorphic:2,2t', 'polymorphic:3c,3t'] - >>> pprint.pprint(parse_keywords(keywords)) - {'_': None, - 'dgettext': (2,), - 'dngettext': (2, 3), - 'pgettext': ((1, 'c'), 2), - 'polymorphic': {None: (1,), 2: (2,), 3: ((3, 'c'),)}} - - The input keywords are in GNU Gettext style; see :doc:`cmdline` for details. - - The output is a dictionary mapping keyword names to a dictionary of specifications. - Keys in this dictionary are numbers of arguments, where ``None`` means that all numbers - of arguments are matched, and a number means only calls with that number of arguments - are matched (which happens when using the "t" specifier). However, as a special - case for backwards compatibility, if the dictionary of specifications would - be ``{None: x}``, i.e., there is only one specification and it matches all argument - counts, then it is collapsed into just ``x``. - - A specification is either a tuple or None. If a tuple, each element can be either a number - ``n``, meaning that the nth argument should be extracted as a message, or the tuple - ``(n, 'c')``, meaning that the nth argument should be extracted as context for the - messages. A ``None`` specification is equivalent to ``(1,)``, extracting the first - argument. - """ - keywords = {} - for string in strings: - if ':' in string: - funcname, spec_str = string.split(':') - number, spec = _parse_spec(spec_str) - else: - funcname = string - number = None - spec = None - keywords.setdefault(funcname, {})[number] = spec - - # For best backwards compatibility, collapse {None: x} into x. - for k, v in keywords.items(): - if set(v) == {None}: - keywords[k] = v[None] - - return keywords - - -def __getattr__(name: str): - # Re-exports for backwards compatibility; - # `setuptools_frontend` is the canonical import location. - if name in { - 'check_message_extractors', - 'compile_catalog', - 'extract_messages', - 'init_catalog', - 'update_catalog', - }: - from babel.messages import setuptools_frontend - - return getattr(setuptools_frontend, name) - - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") - - -if __name__ == '__main__': - main() diff --git a/.venv/lib/python3.12/site-packages/babel/messages/jslexer.py b/.venv/lib/python3.12/site-packages/babel/messages/jslexer.py deleted file mode 100644 index d751b58f..00000000 --- a/.venv/lib/python3.12/site-packages/babel/messages/jslexer.py +++ /dev/null @@ -1,216 +0,0 @@ -""" -babel.messages.jslexer -~~~~~~~~~~~~~~~~~~~~~~ - -A simple JavaScript 1.5 lexer which is used for the JavaScript -extractor. - -:copyright: (c) 2013-2026 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) # fmt: skip - -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.005 and coherence_difference > 0.02: - return self.coherence > other.coherence - elif chaos_difference < 0.005 and coherence_difference <= 0.02: - # When having a difficult decision, use the result that decoded as many multi-byte as possible. - # preserve RAM usage! - if len(self._payload) >= TOO_BIG_SEQUENCE: - return self.chaos < other.chaos - return self.multi_byte_usage > other.multi_byte_usage - - return self.chaos < other.chaos - - @property - def multi_byte_usage(self) -> float: - return 1.0 - (len(str(self)) / len(self.raw)) - - def __str__(self) -> str: - # Lazy Str Loading - if self._string is None: - self._string = str(self._payload, self._encoding, "strict") - # UTF-7 BOM is encoded in modified Base64 whose byte boundary - # can overlap with the next character, so raw-byte stripping - # is unreliable. Strip the decoded BOM character instead. - if ( - self._has_sig_or_bom - and self._encoding == "utf_7" - and self._string - and self._string[0] == "\ufeff" - ): - self._string = self._string[1:] - return self._string - - def __repr__(self) -> str: - return f"" - - def add_submatch(self, other: CharsetMatch) -> None: - if not isinstance(other, CharsetMatch) or other == self: - raise ValueError( - "Unable to add instance <{}> as a submatch of a CharsetMatch".format( - other.__class__ - ) - ) - - other._string = None # Unload RAM usage; dirty trick. - self._leaves.append(other) - - @property - def encoding(self) -> str: - return self._encoding - - @property - def encoding_aliases(self) -> list[str]: - """ - Encoding name are known by many name, using this could help when searching for IBM855 when it's listed as CP855. - """ - also_known_as: list[str] = [] - for u, p in aliases.items(): - if self.encoding == u: - also_known_as.append(p) - elif self.encoding == p: - also_known_as.append(u) - return also_known_as - - @property - def bom(self) -> bool: - return self._has_sig_or_bom - - @property - def byte_order_mark(self) -> bool: - return self._has_sig_or_bom - - @property - def languages(self) -> list[str]: - """ - Return the complete list of possible languages found in decoded sequence. - Usually not really useful. Returned list may be empty even if 'language' property return something != 'Unknown'. - """ - return [e[0] for e in self._languages] - - @property - def language(self) -> str: - """ - Most probable language found in decoded sequence. If none were detected or inferred, the property will return - "Unknown". - """ - if not self._languages: - # Trying to infer the language based on the given encoding - # Its either English or we should not pronounce ourselves in certain cases. - if "ascii" in self.could_be_from_charset: - return "English" - - # doing it there to avoid circular import - from charset_normalizer.cd import encoding_languages, mb_encoding_languages - - languages = ( - mb_encoding_languages(self.encoding) - if is_multi_byte_encoding(self.encoding) - else encoding_languages(self.encoding) - ) - - if len(languages) == 0 or "Latin Based" in languages: - return "Unknown" - - return languages[0] - - return self._languages[0][0] - - @property - def chaos(self) -> float: - return self._mean_mess_ratio - - @property - def coherence(self) -> float: - if not self._languages: - return 0.0 - return self._languages[0][1] - - @property - def percent_chaos(self) -> float: - return round(self.chaos * 100, ndigits=3) - - @property - def percent_coherence(self) -> float: - return round(self.coherence * 100, ndigits=3) - - @property - def raw(self) -> bytes | bytearray: - """ - Original untouched bytes. - """ - return self._payload - - @property - def submatch(self) -> list[CharsetMatch]: - return self._leaves - - @property - def has_submatch(self) -> bool: - return len(self._leaves) > 0 - - @property - def alphabets(self) -> list[str]: - if self._unicode_ranges is not None: - return self._unicode_ranges - # list detected ranges - detected_ranges: list[str | None] = [unicode_range(char) for char in str(self)] - # filter and sort - self._unicode_ranges = sorted(list({r for r in detected_ranges if r})) - return self._unicode_ranges - - @property - def could_be_from_charset(self) -> list[str]: - """ - The complete list of encoding that output the exact SAME str result and therefore could be the originating - encoding. - This list does include the encoding available in property 'encoding'. - """ - return [self._encoding] + [m.encoding for m in self._leaves] - - def output(self, encoding: str = "utf_8") -> bytes: - """ - Method to get re-encoded bytes payload using given target encoding. Default to UTF-8. - Any errors will be simply ignored by the encoder NOT replaced. - """ - if self._output_encoding is None or self._output_encoding != encoding: - self._output_encoding = encoding - decoded_string = str(self) - if ( - self._preemptive_declaration is not None - and self._preemptive_declaration.lower() - not in ["utf-8", "utf8", "utf_8"] - ): - patched_header = sub( - RE_POSSIBLE_ENCODING_INDICATION, - lambda m: m.string[m.span()[0] : m.span()[1]].replace( - m.groups()[0], - iana_name(self._output_encoding).replace("_", "-"), # type: ignore[arg-type] - ), - decoded_string[:8192], - count=1, - ) - - decoded_string = patched_header + decoded_string[8192:] - - self._output_payload = decoded_string.encode(encoding, "replace") - - return self._output_payload # type: ignore - - @property - def fingerprint(self) -> int: - """ - Retrieve a hash fingerprint of the decoded payload, used for deduplication. - """ - return hash(str(self)) - - -class CharsetMatches: - """ - Container with every CharsetMatch items ordered by default from most probable to the less one. - Act like a list(iterable) but does not implements all related methods. - """ - - def __init__(self, results: list[CharsetMatch] | None = None): - self._results: list[CharsetMatch] = sorted(results) if results else [] - - def __iter__(self) -> Iterator[CharsetMatch]: - yield from self._results - - def __getitem__(self, item: int | str) -> CharsetMatch: - """ - Retrieve a single item either by its position or encoding name (alias may be used here). - Raise KeyError upon invalid index or encoding not present in results. - """ - if isinstance(item, int): - return self._results[item] - if isinstance(item, str): - item = iana_name(item, False) - for result in self._results: - if item in result.could_be_from_charset: - return result - raise KeyError - - def __len__(self) -> int: - return len(self._results) - - def __bool__(self) -> bool: - return len(self._results) > 0 - - def append(self, item: CharsetMatch) -> None: - """ - Insert a single match. Will be inserted accordingly to preserve sort. - Can be inserted as a submatch. - """ - if not isinstance(item, CharsetMatch): - raise ValueError( - "Cannot append instance '{}' to CharsetMatches".format( - str(item.__class__) - ) - ) - # We should disable the submatch factoring when the input file is too heavy (conserve RAM usage) - if len(item.raw) < TOO_BIG_SEQUENCE: - for match in self._results: - if match.fingerprint == item.fingerprint and match.chaos == item.chaos: - match.add_submatch(item) - return - self._results.append(item) - self._results = sorted(self._results) - - def best(self) -> CharsetMatch | None: - """ - Simply return the first match. Strict equivalent to matches[0]. - """ - if not self._results: - return None - return self._results[0] - - def first(self) -> CharsetMatch | None: - """ - Redundant method, call the method best(). Kept for BC reasons. - """ - return self.best() - - -CoherenceMatch = Tuple[str, float] -CoherenceMatches = List[CoherenceMatch] - - -class CliDetectionResult: - def __init__( - self, - path: str, - encoding: str | None, - encoding_aliases: list[str], - alternative_encodings: list[str], - language: str, - alphabets: list[str], - has_sig_or_bom: bool, - chaos: float, - coherence: float, - unicode_path: str | None, - is_preferred: bool, - ): - self.path: str = path - self.unicode_path: str | None = unicode_path - self.encoding: str | None = encoding - self.encoding_aliases: list[str] = encoding_aliases - self.alternative_encodings: list[str] = alternative_encodings - self.language: str = language - self.alphabets: list[str] = alphabets - self.has_sig_or_bom: bool = has_sig_or_bom - self.chaos: float = chaos - self.coherence: float = coherence - self.is_preferred: bool = is_preferred - - @property - def __dict__(self) -> dict[str, Any]: # type: ignore - return { - "path": self.path, - "encoding": self.encoding, - "encoding_aliases": self.encoding_aliases, - "alternative_encodings": self.alternative_encodings, - "language": self.language, - "alphabets": self.alphabets, - "has_sig_or_bom": self.has_sig_or_bom, - "chaos": self.chaos, - "coherence": self.coherence, - "unicode_path": self.unicode_path, - "is_preferred": self.is_preferred, - } - - def to_json(self) -> str: - return dumps(self.__dict__, ensure_ascii=True, indent=4) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/py.typed b/.venv/lib/python3.12/site-packages/charset_normalizer/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/utils.py b/.venv/lib/python3.12/site-packages/charset_normalizer/utils.py deleted file mode 100644 index 0f529b59..00000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/utils.py +++ /dev/null @@ -1,422 +0,0 @@ -from __future__ import annotations - -import importlib -import logging -import unicodedata -from bisect import bisect_right -from codecs import IncrementalDecoder -from encodings.aliases import aliases -from functools import lru_cache -from re import findall -from typing import Generator - -from _multibytecodec import ( # type: ignore[import-not-found,import] - MultibyteIncrementalDecoder, -) - -from .constant import ( - ENCODING_MARKS, - IANA_SUPPORTED_SIMILAR, - RE_POSSIBLE_ENCODING_INDICATION, - UNICODE_RANGES_COMBINED, - UNICODE_SECONDARY_RANGE_KEYWORD, - UTF8_MAXIMAL_ALLOCATION, - COMMON_CJK_CHARACTERS, - _LATIN, - _CJK, - _HANGUL, - _KATAKANA, - _HIRAGANA, - _THAI, - _ARABIC, - _ARABIC_ISOLATED_FORM, - _ACCENT_KEYWORDS, - _ACCENTUATED, -) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def _character_flags(character: str) -> int: - """Compute all name-based classification flags with a single unicodedata.name() call.""" - try: - desc: str = unicodedata.name(character) - except ValueError: - return 0 - - flags: int = 0 - - if "LATIN" in desc: - flags |= _LATIN - if "CJK" in desc: - flags |= _CJK - if "HANGUL" in desc: - flags |= _HANGUL - if "KATAKANA" in desc: - flags |= _KATAKANA - if "HIRAGANA" in desc: - flags |= _HIRAGANA - if "THAI" in desc: - flags |= _THAI - if "ARABIC" in desc: - flags |= _ARABIC - if "ISOLATED FORM" in desc: - flags |= _ARABIC_ISOLATED_FORM - - for kw in _ACCENT_KEYWORDS: - if kw in desc: - flags |= _ACCENTUATED - break - - return flags - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_accentuated(character: str) -> bool: - return bool(_character_flags(character) & _ACCENTUATED) - - -@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)) - - -# Pre-built sorted lookup table for O(log n) binary search in unicode_range(). -# Each entry is (range_start, range_end_exclusive, range_name). -_UNICODE_RANGES_SORTED: list[tuple[int, int, str]] = sorted( - (ord_range.start, ord_range.stop, name) - for name, ord_range in UNICODE_RANGES_COMBINED.items() -) -_UNICODE_RANGE_STARTS: list[int] = [e[0] for e in _UNICODE_RANGES_SORTED] - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def unicode_range(character: str) -> str | None: - """ - Retrieve the Unicode range official name from a single character. - """ - character_ord: int = ord(character) - - # Binary search: find the rightmost range whose start <= character_ord - idx = bisect_right(_UNICODE_RANGE_STARTS, character_ord) - 1 - if idx >= 0: - start, stop, name = _UNICODE_RANGES_SORTED[idx] - if character_ord < stop: - return name - - return None - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_latin(character: str) -> bool: - return bool(_character_flags(character) & _LATIN) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_punctuation(character: str) -> bool: - character_category: str = unicodedata.category(character) - - if "P" in character_category: - return True - - character_range: str | None = unicode_range(character) - - if character_range is None: - return False - - return "Punctuation" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_symbol(character: str) -> bool: - character_category: str = unicodedata.category(character) - - if "S" in character_category or "N" in character_category: - return True - - character_range: str | None = unicode_range(character) - - if character_range is None: - return False - - return "Forms" in character_range and character_category != "Lo" - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_emoticon(character: str) -> bool: - character_range: str | None = unicode_range(character) - - if character_range is None: - return False - - return "Emoticons" in character_range or "Pictographs" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_separator(character: str) -> bool: - if character.isspace() or character in {"|", "+", "<", ">"}: - return True - - character_category: str = unicodedata.category(character) - - return "Z" in character_category or character_category in {"Po", "Pd", "Pc"} - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_case_variable(character: str) -> bool: - return character.islower() != character.isupper() - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_cjk(character: str) -> bool: - return bool(_character_flags(character) & _CJK) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_hiragana(character: str) -> bool: - return bool(_character_flags(character) & _HIRAGANA) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_katakana(character: str) -> bool: - return bool(_character_flags(character) & _KATAKANA) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_hangul(character: str) -> bool: - return bool(_character_flags(character) & _HANGUL) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_thai(character: str) -> bool: - return bool(_character_flags(character) & _THAI) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_arabic(character: str) -> bool: - return bool(_character_flags(character) & _ARABIC) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_arabic_isolated_form(character: str) -> bool: - return bool(_character_flags(character) & _ARABIC_ISOLATED_FORM) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_cjk_uncommon(character: str) -> bool: - return character not in COMMON_CJK_CHARACTERS - - -@lru_cache(maxsize=len(UNICODE_RANGES_COMBINED)) -def is_unicode_range_secondary(range_name: str) -> bool: - return any(keyword in range_name for keyword in UNICODE_SECONDARY_RANGE_KEYWORD) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_unprintable(character: str) -> bool: - return ( - character.isspace() is False # includes \n \t \r \v - and character.isprintable() is False - and character != "\x1a" # Why? Its the ASCII substitute character. - and character != "\ufeff" # bug discovered in Python, - # Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space. - ) - - -def any_specified_encoding( - sequence: bytes | bytearray, search_zone: int = 8192 -) -> str | None: - """ - Extract using ASCII-only decoder any specified encoding in the first n-bytes. - """ - if not isinstance(sequence, (bytes, bytearray)): - raise TypeError - - seq_len: int = len(sequence) - - results: list[str] = findall( - RE_POSSIBLE_ENCODING_INDICATION, - sequence[: min(seq_len, search_zone)].decode("ascii", errors="ignore"), - ) - - if len(results) == 0: - return None - - for specified_encoding in results: - specified_encoding = specified_encoding.lower().replace("-", "_") - - encoding_alias: str - encoding_iana: str - - for encoding_alias, encoding_iana in aliases.items(): - if encoding_alias == specified_encoding: - return encoding_iana - if encoding_iana == specified_encoding: - return encoding_iana - - return None - - -@lru_cache(maxsize=128) -def is_multi_byte_encoding(name: str) -> bool: - """ - Verify is a specific encoding is a multi byte one based on it IANA name - """ - return name in { - "utf_8", - "utf_8_sig", - "utf_16", - "utf_16_be", - "utf_16_le", - "utf_32", - "utf_32_le", - "utf_32_be", - "utf_7", - } or issubclass( - importlib.import_module(f"encodings.{name}").IncrementalDecoder, - MultibyteIncrementalDecoder, - ) - - -def identify_sig_or_bom(sequence: bytes | bytearray) -> tuple[str | None, bytes]: - """ - Identify and extract SIG/BOM in given sequence. - """ - - for iana_encoding in ENCODING_MARKS: - marks: bytes | list[bytes] = ENCODING_MARKS[iana_encoding] - - if isinstance(marks, bytes): - marks = [marks] - - for mark in marks: - if sequence.startswith(mark): - return iana_encoding, mark - - return None, b"" - - -def should_strip_sig_or_bom(iana_encoding: str) -> bool: - return iana_encoding not in {"utf_16", "utf_32"} - - -def iana_name(cp_name: str, strict: bool = True) -> str: - """Returns the Python normalized encoding name (Not the IANA official name).""" - cp_name = cp_name.lower().replace("-", "_") - - encoding_alias: str - encoding_iana: str - - for encoding_alias, encoding_iana in aliases.items(): - if cp_name in [encoding_alias, encoding_iana]: - return encoding_iana - - if strict: - raise ValueError(f"Unable to retrieve IANA for '{cp_name}'") - - return cp_name - - -def cp_similarity(iana_name_a: str, iana_name_b: str) -> float: - if is_multi_byte_encoding(iana_name_a) or is_multi_byte_encoding(iana_name_b): - return 0.0 - - decoder_a = importlib.import_module(f"encodings.{iana_name_a}").IncrementalDecoder - decoder_b = importlib.import_module(f"encodings.{iana_name_b}").IncrementalDecoder - - id_a: IncrementalDecoder = decoder_a(errors="ignore") - id_b: IncrementalDecoder = decoder_b(errors="ignore") - - character_match_count: int = 0 - - for i in range(256): - 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 / 256 - - -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 | bytearray, - encoding_iana: str, - offsets: range, - chunk_size: int, - bom_or_sig_available: bool, - strip_sig_or_bom: bool, - sig_payload: bytes, - is_multi_byte_decoder: bool, - decoded_payload: str | None = None, -) -> Generator[str, None, None]: - if decoded_payload and is_multi_byte_decoder is False: - for i in offsets: - chunk = decoded_payload[i : i + chunk_size] - if not chunk: - break - yield chunk - else: - for i in offsets: - chunk_end = i + chunk_size - if chunk_end > len(sequences) + 8: - continue - - cut_sequence = sequences[i : i + chunk_size] - - if bom_or_sig_available and strip_sig_or_bom is False: - cut_sequence = sig_payload + cut_sequence - - chunk = cut_sequence.decode( - encoding_iana, - errors="ignore" if is_multi_byte_decoder else "strict", - ) - - # multi-byte bad cutting detector and adjustment - # not the cleanest way to perform that fix but clever enough for now. - if is_multi_byte_decoder and i > 0: - chunk_partial_size_chk: int = min(chunk_size, 16) - - if ( - decoded_payload - and chunk[:chunk_partial_size_chk] not in decoded_payload - ): - for j in range(i, i - 4, -1): - cut_sequence = sequences[j:chunk_end] - - if bom_or_sig_available and strip_sig_or_bom is False: - cut_sequence = sig_payload + cut_sequence - - chunk = cut_sequence.decode(encoding_iana, errors="ignore") - - if chunk[:chunk_partial_size_chk] in decoded_payload: - break - - yield chunk diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/version.py b/.venv/lib/python3.12/site-packages/charset_normalizer/version.py deleted file mode 100644 index a93d3672..00000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/version.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -Expose version -""" - -from __future__ import annotations - -__version__ = "3.4.7" -VERSION = __version__.split(".") diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/METADATA b/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/METADATA deleted file mode 100644 index 43f17c61..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/METADATA +++ /dev/null @@ -1,401 +0,0 @@ -Metadata-Version: 2.4 -Name: claude-agent-sdk -Version: 0.1.70 -Summary: Python SDK for Claude Code -Project-URL: Homepage, https://github.com/anthropics/claude-agent-sdk-python -Project-URL: Documentation, https://docs.anthropic.com/en/docs/claude-code/sdk -Project-URL: Issues, https://github.com/anthropics/claude-agent-sdk-python/issues -Author-email: Anthropic -License: MIT -License-File: LICENSE -Keywords: ai,anthropic,claude,sdk -Classifier: Development Status :: 3 - Alpha -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Typing :: Typed -Requires-Python: >=3.10 -Requires-Dist: anyio>=4.0.0 -Requires-Dist: mcp>=1.19.0 -Requires-Dist: sniffio>=1.0.0 -Requires-Dist: typing-extensions>=4.0.0; python_version < '3.11' -Provides-Extra: dev -Requires-Dist: anyio[trio]>=4.0.0; extra == 'dev' -Requires-Dist: mypy>=1.0.0; extra == 'dev' -Requires-Dist: pytest-asyncio>=0.20.0; extra == 'dev' -Requires-Dist: pytest-cov>=4.0.0; extra == 'dev' -Requires-Dist: pytest>=7.0.0; extra == 'dev' -Requires-Dist: ruff>=0.1.0; extra == 'dev' -Provides-Extra: examples -Requires-Dist: asyncpg>=0.27.0; extra == 'examples' -Requires-Dist: boto3>=1.28.0; extra == 'examples' -Requires-Dist: fakeredis>=2.20.0; extra == 'examples' -Requires-Dist: moto[s3]>=5.0.0; extra == 'examples' -Requires-Dist: redis>=4.2.0; extra == 'examples' -Provides-Extra: otel -Requires-Dist: opentelemetry-api>=1.20.0; extra == 'otel' -Description-Content-Type: text/markdown - -# Claude Agent SDK for Python - -Python SDK for Claude Agent. See the [Claude Agent SDK documentation](https://platform.claude.com/docs/en/agent-sdk/python) for more information. - -## Installation - -```bash -pip install claude-agent-sdk -``` - -**Prerequisites:** - -- Python 3.10+ - -**Note:** The Claude Code CLI is automatically bundled with the package - no separate installation required! The SDK will use the bundled CLI by default. If you prefer to use a system-wide installation or a specific version, you can: - -- Install Claude Code separately: `curl -fsSL https://claude.ai/install.sh | bash` -- Specify a custom path: `ClaudeAgentOptions(cli_path="/path/to/claude")` - -## Quick Start - -```python -import anyio -from claude_agent_sdk import query - -async def main(): - async for message in query(prompt="What is 2 + 2?"): - print(message) - -anyio.run(main) -``` - -## Basic Usage: query() - -`query()` is an async function for querying Claude Code. It returns an `AsyncIterator` of response messages. See [src/claude_agent_sdk/query.py](src/claude_agent_sdk/query.py). - -```python -from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock - -# Simple query -async for message in query(prompt="Hello Claude"): - if isinstance(message, AssistantMessage): - for block in message.content: - if isinstance(block, TextBlock): - print(block.text) - -# With options -options = ClaudeAgentOptions( - system_prompt="You are a helpful assistant", - max_turns=1 -) - -async for message in query(prompt="Tell me a joke", options=options): - print(message) -``` - -### Using Tools - -By default, Claude has access to the full [Claude Code toolset](https://code.claude.com/docs/en/settings#tools-available-to-claude) (Read, Write, Edit, Bash, and others). `allowed_tools` is a permission allowlist: listed tools are auto-approved, and unlisted tools fall through to `permission_mode` and `can_use_tool` for a decision. It does not remove tools from Claude's toolset. To block specific tools, use `disallowed_tools`. See the [permissions guide](https://platform.claude.com/docs/en/agent-sdk/permissions) for the full evaluation order. - -```python -options = ClaudeAgentOptions( - allowed_tools=["Read", "Write", "Bash"], # auto-approve these tools - permission_mode='acceptEdits' # auto-accept file edits -) - -async for message in query( - prompt="Create a hello.py file", - options=options -): - # Process tool use and results - pass -``` - -### Working Directory - -```python -from pathlib import Path - -options = ClaudeAgentOptions( - cwd="/path/to/project" # or Path("/path/to/project") -) -``` - -## ClaudeSDKClient - -`ClaudeSDKClient` supports bidirectional, interactive conversations with Claude -Code. See [src/claude_agent_sdk/client.py](src/claude_agent_sdk/client.py). - -Unlike `query()`, `ClaudeSDKClient` additionally enables **custom tools** and **hooks**, both of which can be defined as Python functions. - -### Custom Tools (as In-Process SDK MCP Servers) - -A **custom tool** is a Python function that you can offer to Claude, for Claude to invoke as needed. - -Custom tools are implemented in-process MCP servers that run directly within your Python application, eliminating the need for separate processes that regular MCP servers require. - -For an end-to-end example, see [MCP Calculator](examples/mcp_calculator.py). - -#### Creating a Simple Tool - -```python -from claude_agent_sdk import tool, create_sdk_mcp_server, ClaudeAgentOptions, ClaudeSDKClient - -# Define a tool using the @tool decorator -@tool("greet", "Greet a user", {"name": str}) -async def greet_user(args): - return { - "content": [ - {"type": "text", "text": f"Hello, {args['name']}!"} - ] - } - -# Create an SDK MCP server -server = create_sdk_mcp_server( - name="my-tools", - version="1.0.0", - tools=[greet_user] -) - -# Use it with Claude. allowed_tools pre-approves the tool so it runs -# without a permission prompt; it does not control tool availability. -options = ClaudeAgentOptions( - mcp_servers={"tools": server}, - allowed_tools=["mcp__tools__greet"] -) - -async with ClaudeSDKClient(options=options) as client: - await client.query("Greet Alice") - - # Extract and print response - async for msg in client.receive_response(): - print(msg) -``` - -#### Benefits Over External MCP Servers - -- **No subprocess management** - Runs in the same process as your application -- **Better performance** - No IPC overhead for tool calls -- **Simpler deployment** - Single Python process instead of multiple -- **Easier debugging** - All code runs in the same process -- **Type safety** - Direct Python function calls with type hints - -#### Migration from External Servers - -```python -# BEFORE: External MCP server (separate process) -options = ClaudeAgentOptions( - mcp_servers={ - "calculator": { - "type": "stdio", - "command": "python", - "args": ["-m", "calculator_server"] - } - } -) - -# AFTER: SDK MCP server (in-process) -from my_tools import add, subtract # Your tool functions - -calculator = create_sdk_mcp_server( - name="calculator", - tools=[add, subtract] -) - -options = ClaudeAgentOptions( - mcp_servers={"calculator": calculator} -) -``` - -#### Mixed Server Support - -You can use both SDK and external MCP servers together: - -```python -options = ClaudeAgentOptions( - mcp_servers={ - "internal": sdk_server, # In-process SDK server - "external": { # External subprocess server - "type": "stdio", - "command": "external-server" - } - } -) -``` - -### Hooks - -A **hook** is a Python function that the Claude Code _application_ (_not_ Claude) invokes at specific points of the Claude agent loop. Hooks can provide deterministic processing and automated feedback for Claude. Read more in [Intercept and control agent behavior with hooks](https://platform.claude.com/docs/en/agent-sdk/hooks). - -For more examples, see examples/hooks.py. - -#### Example - -```python -from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient, HookMatcher - -async def check_bash_command(input_data, tool_use_id, context): - tool_name = input_data["tool_name"] - tool_input = input_data["tool_input"] - if tool_name != "Bash": - return {} - command = tool_input.get("command", "") - block_patterns = ["foo.sh"] - for pattern in block_patterns: - if pattern in command: - return { - "hookSpecificOutput": { - "hookEventName": "PreToolUse", - "permissionDecision": "deny", - "permissionDecisionReason": f"Command contains invalid pattern: {pattern}", - } - } - return {} - -options = ClaudeAgentOptions( - allowed_tools=["Bash"], - hooks={ - "PreToolUse": [ - HookMatcher(matcher="Bash", hooks=[check_bash_command]), - ], - } -) - -async with ClaudeSDKClient(options=options) as client: - # Test 1: Command with forbidden pattern (will be blocked) - await client.query("Run the bash command: ./foo.sh --help") - async for msg in client.receive_response(): - print(msg) - - print("\n" + "=" * 50 + "\n") - - # Test 2: Safe command that should work - await client.query("Run the bash command: echo 'Hello from hooks example!'") - async for msg in client.receive_response(): - print(msg) -``` - -## Types - -See [src/claude_agent_sdk/types.py](src/claude_agent_sdk/types.py) for complete type definitions: - -- `ClaudeAgentOptions` - Configuration options -- `AssistantMessage`, `UserMessage`, `SystemMessage`, `ResultMessage` - Message types -- `TextBlock`, `ToolUseBlock`, `ToolResultBlock` - Content blocks - -## Error Handling - -```python -from claude_agent_sdk import ( - ClaudeSDKError, # Base error - CLINotFoundError, # Claude Code not installed - CLIConnectionError, # Connection issues - ProcessError, # Process failed - CLIJSONDecodeError, # JSON parsing issues -) - -try: - async for message in query(prompt="Hello"): - pass -except CLINotFoundError: - print("Please install Claude Code") -except ProcessError as e: - print(f"Process failed with exit code: {e.exit_code}") -except CLIJSONDecodeError as e: - print(f"Failed to parse response: {e}") -``` - -See [src/claude_agent_sdk/\_errors.py](src/claude_agent_sdk/_errors.py) for all error types. - -## Available Tools - -See the [Claude Code documentation](https://code.claude.com/docs/en/settings#tools-available-to-claude) for a complete list of available tools. - -## Examples - -See [examples/quick_start.py](examples/quick_start.py) for a complete working example. - -See [examples/streaming_mode.py](examples/streaming_mode.py) for comprehensive examples involving `ClaudeSDKClient`. You can even run interactive examples in IPython from [examples/streaming_mode_ipython.py](examples/streaming_mode_ipython.py). - -## Migrating from Claude Code SDK - -If you're upgrading from the Claude Code SDK (versions < 0.1.0), please see the [CHANGELOG.md](CHANGELOG.md#010) for details on breaking changes and new features, including: - -- `ClaudeCodeOptions` → `ClaudeAgentOptions` rename -- Merged system prompt configuration -- Settings isolation and explicit control -- New programmatic subagents and session forking features - -## Development - -If you're contributing to this project, run the initial setup script to install git hooks: - -```bash -./scripts/initial-setup.sh -``` - -This installs a pre-push hook that runs lint checks before pushing, matching the CI workflow. To skip the hook temporarily, use `git push --no-verify`. - -### Building Wheels Locally - -To build wheels with the bundled Claude Code CLI: - -```bash -# Install build dependencies -pip install build twine - -# Build wheel with bundled CLI -python scripts/build_wheel.py - -# Build with specific version -python scripts/build_wheel.py --version 0.1.4 - -# Build with specific CLI version -python scripts/build_wheel.py --cli-version 2.0.0 - -# Clean bundled CLI after building -python scripts/build_wheel.py --clean - -# Skip CLI download (use existing) -python scripts/build_wheel.py --skip-download -``` - -The build script: - -1. Downloads Claude Code CLI for your platform -2. Bundles it in the wheel -3. Builds both wheel and source distribution -4. Checks the package with twine - -See `python scripts/build_wheel.py --help` for all options. - -### Release Workflow - -The package is published to PyPI via the GitHub Actions workflow in `.github/workflows/publish.yml`. To create a new release: - -1. **Trigger the workflow** manually from the Actions tab with two inputs: - - `version`: The package version to publish (e.g., `0.1.5`) - - `claude_code_version`: The Claude Code CLI version to bundle (e.g., `2.0.0` or `latest`) - -2. **The workflow will**: - - Build platform-specific wheels for macOS, Linux, and Windows - - Bundle the specified Claude Code CLI version in each wheel - - Build a source distribution - - Publish all artifacts to PyPI - - Create a release branch with version updates - - Open a PR to main with: - - Updated `pyproject.toml` version - - Updated `src/claude_agent_sdk/_version.py` - - Updated `src/claude_agent_sdk/_cli_version.py` with bundled CLI version - - Auto-generated `CHANGELOG.md` entry - -3. **Review and merge** the release PR to update main with the new version information - -The workflow tracks both the package version and the bundled CLI version separately, allowing you to release a new package version with an updated CLI without code changes. - -## License and terms - -Use of this SDK is governed by Anthropic's [Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms), including when you use it to power products and services that you make available to your own customers and end users, except to the extent a specific component or dependency is covered by a different license as indicated in that component's LICENSE file. diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/RECORD b/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/RECORD deleted file mode 100644 index daf655f7..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/RECORD +++ /dev/null @@ -1,57 +0,0 @@ -claude_agent_sdk-0.1.70.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -claude_agent_sdk-0.1.70.dist-info/METADATA,sha256=3vbVppLjCn28I8tcl6rv24pWaVzObHvxZ9styOpplUo,13657 -claude_agent_sdk-0.1.70.dist-info/RECORD,, -claude_agent_sdk-0.1.70.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -claude_agent_sdk-0.1.70.dist-info/WHEEL,sha256=DVUJrTnHPe4nHXrfSPuDNw4yuoLP4C12iop_O8nzZI4,102 -claude_agent_sdk-0.1.70.dist-info/licenses/LICENSE,sha256=zr3eio-57lnl6q7RlXi_gIWqcEdWIlnDHzjyJcJvaBI,1070 -claude_agent_sdk/__init__.py,sha256=QDJnojFIkGKJqVZ_bGX3r0CA9n6TJaRzoe-i_sUhG5A,21782 -claude_agent_sdk/__pycache__/__init__.cpython-312.pyc,, -claude_agent_sdk/__pycache__/_cli_version.cpython-312.pyc,, -claude_agent_sdk/__pycache__/_errors.cpython-312.pyc,, -claude_agent_sdk/__pycache__/_version.cpython-312.pyc,, -claude_agent_sdk/__pycache__/client.cpython-312.pyc,, -claude_agent_sdk/__pycache__/query.cpython-312.pyc,, -claude_agent_sdk/__pycache__/types.cpython-312.pyc,, -claude_agent_sdk/_bundled/.gitignore,sha256=wUkDNuHQq96Q6QWJsLFFkJXtD3IuzYfuOBZaB9bR7f8,74 -claude_agent_sdk/_bundled/claude,sha256=SwEHW9kjCE_lYSS_tkf17KmMKx-jTLA50udczISvS4Y,215880320 -claude_agent_sdk/_cli_version.py,sha256=IUjSXB864f5XS8yNO3o6kiEfk1UMFD-5INCD-fVqS4I,68 -claude_agent_sdk/_errors.py,sha256=nSdJNNeszvXG1PfnXd2sQpVNORqMct-MfPaiM3XeJL4,1579 -claude_agent_sdk/_internal/__init__.py,sha256=zDdgjqp8SI9mTnwZbP2Be-w4LWlv4a3kA-TS2i75jsM,39 -claude_agent_sdk/_internal/__pycache__/__init__.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/_task_compat.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/client.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/message_parser.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/query.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/session_import.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/session_mutations.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/session_resume.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/session_store.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/session_store_validation.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/session_summary.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/sessions.cpython-312.pyc,, -claude_agent_sdk/_internal/__pycache__/transcript_mirror_batcher.cpython-312.pyc,, -claude_agent_sdk/_internal/_task_compat.py,sha256=PJT9DnQmbSbvGeKW9BJ_J5LcPO9TPdTSHOMBnrhCFds,6198 -claude_agent_sdk/_internal/client.py,sha256=_7UsjLuT3YFr_iIl_E-8ntuTRchzjaKZyMuS5KbPOOs,9444 -claude_agent_sdk/_internal/message_parser.py,sha256=mMwMqsTxfNexBq1GTGpz0JM5x1RLO9hnnQLUo72_2Y8,11846 -claude_agent_sdk/_internal/query.py,sha256=rm8ykBquVhMKK0flGy9Q7DahFNw2xGQvQF8-uvhFSdI,35430 -claude_agent_sdk/_internal/session_import.py,sha256=P9lvqlS60hHHZehAkOfjrbWAtvVQuj8zRvjGyRSy1g4,6605 -claude_agent_sdk/_internal/session_mutations.py,sha256=FmU_tdu26ZY-Be8VIuQ7pLjij9CwN-R0nd6GXKnk5nU,34480 -claude_agent_sdk/_internal/session_resume.py,sha256=jYXtfTe-Do6ypsenQ56qMnG2flfZdZZ097CBtrah4Fk,19773 -claude_agent_sdk/_internal/session_store.py,sha256=fGX4hSCaWrxcnQfQovtb4xif3QySB3gve17Ubd0ZMaE,7501 -claude_agent_sdk/_internal/session_store_validation.py,sha256=GRsWm2XpEWtv_cwhXfwmRNsqucOFS4iyMgbuTydYC6Q,1663 -claude_agent_sdk/_internal/session_summary.py,sha256=WgmHTDhSUzQM_PuYYtaZWQrTBzWrdV1H4W4pv8C9P-g,8404 -claude_agent_sdk/_internal/sessions.py,sha256=seui8AeBzJSvaKmP-X-W6FhF3V_vpXmKw4juW9zr98E,66882 -claude_agent_sdk/_internal/transcript_mirror_batcher.py,sha256=5Gk90SiSUAeZ-gKPkOft9V9wcLoDnxlwaNSoewgAeV8,9516 -claude_agent_sdk/_internal/transport/__init__.py,sha256=sv8Iy1b9YmPlXu4XsdN98gJIlyrLtwq8PKQyF4qnQLk,1978 -claude_agent_sdk/_internal/transport/__pycache__/__init__.cpython-312.pyc,, -claude_agent_sdk/_internal/transport/__pycache__/subprocess_cli.cpython-312.pyc,, -claude_agent_sdk/_internal/transport/subprocess_cli.py,sha256=x25gLGvUDlAhz44Y09SDQAVz_toWVoXNZ-rtYBFTBSo,29873 -claude_agent_sdk/_version.py,sha256=etcy6KMRkfOl-3VeARzKcE7IULeRbjPXcYSiPvecCYQ,72 -claude_agent_sdk/client.py,sha256=pi5Mg6uQYyubAYW_0Y_BnuvTrpCg7-cVGbup1IrfdgI,26535 -claude_agent_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -claude_agent_sdk/query.py,sha256=UMOx6zU5_YKNetpLfDf6jiP3UgAH91eLFt6LR-d1hVM,4685 -claude_agent_sdk/testing/__init__.py,sha256=uE4_eBfxLRjWwmhsZeS6ebGH_G8uLo74NKjcYlAJbc0,299 -claude_agent_sdk/testing/__pycache__/__init__.cpython-312.pyc,, -claude_agent_sdk/testing/__pycache__/session_store_conformance.cpython-312.pyc,, -claude_agent_sdk/testing/session_store_conformance.py,sha256=Q1SPjcswVj8k7p4BnxoDmg6NAkz1GvI-IbteUYvrPtw,13733 -claude_agent_sdk/types.py,sha256=N6DGyPxaf-gnIvTfnymCbvmuEZYG7yTLSwCCLxEJtGw,60320 diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/REQUESTED deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/WHEEL deleted file mode 100644 index dd615f20..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.29.0 -Root-Is-Purelib: true -Tag: py3-none-macosx_11_0_arm64 - diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/licenses/LICENSE deleted file mode 100644 index 3fa6a64e..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk-0.1.70.dist-info/licenses/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 Anthropic, PBC - -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. \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/__init__.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/__init__.py deleted file mode 100644 index 3e60d5bd..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/__init__.py +++ /dev/null @@ -1,655 +0,0 @@ -"""Claude SDK for Python.""" - -import logging -import sys -import types as builtin_types -from collections.abc import Awaitable, Callable -from dataclasses import dataclass -from typing import Annotated, Any, Generic, TypeVar, Union, get_args, get_origin - -if sys.version_info >= (3, 11): - from typing import get_type_hints as _get_type_hints - from typing import is_typeddict -else: - # On 3.10, stdlib is_typeddict doesn't recognize typing_extensions.TypedDict - # subclasses, and stdlib get_type_hints doesn't strip NotRequired markers. - from typing_extensions import get_type_hints as _get_type_hints - from typing_extensions import is_typeddict - -from mcp.types import ToolAnnotations - -from ._errors import ( - ClaudeSDKError, - CLIConnectionError, - CLIJSONDecodeError, - CLINotFoundError, - ProcessError, -) -from ._internal.session_import import import_session_to_store -from ._internal.session_mutations import ( - ForkSessionResult, - delete_session, - delete_session_via_store, - fork_session, - fork_session_via_store, - rename_session, - rename_session_via_store, - tag_session, - tag_session_via_store, -) -from ._internal.session_store import InMemorySessionStore, project_key_for_directory -from ._internal.session_summary import fold_session_summary -from ._internal.sessions import ( - get_session_info, - get_session_info_from_store, - get_session_messages, - get_session_messages_from_store, - get_subagent_messages, - get_subagent_messages_from_store, - list_sessions, - list_sessions_from_store, - list_subagents, - list_subagents_from_store, -) -from ._internal.transport import Transport -from ._version import __version__ -from .client import ClaudeSDKClient -from .query import query -from .types import ( - AgentDefinition, - AssistantMessage, - BaseHookInput, - CanUseTool, - ClaudeAgentOptions, - ContentBlock, - ContextUsageCategory, - ContextUsageResponse, - HookCallback, - HookContext, - HookInput, - HookJSONOutput, - HookMatcher, - McpSdkServerConfig, - McpServerConfig, - McpServerConnectionStatus, - McpServerInfo, - McpServerStatus, - McpServerStatusConfig, - McpStatusResponse, - McpToolAnnotations, - McpToolInfo, - Message, - MirrorErrorMessage, - NotificationHookInput, - NotificationHookSpecificOutput, - PermissionMode, - PermissionRequestHookInput, - PermissionRequestHookSpecificOutput, - PermissionResult, - PermissionResultAllow, - PermissionResultDeny, - PermissionUpdate, - PostToolUseFailureHookInput, - PostToolUseFailureHookSpecificOutput, - PostToolUseHookInput, - PreCompactHookInput, - PreToolUseHookInput, - RateLimitEvent, - RateLimitInfo, - RateLimitStatus, - RateLimitType, - ResultMessage, - SandboxIgnoreViolations, - SandboxNetworkConfig, - SandboxSettings, - SdkBeta, - SdkPluginConfig, - SDKSessionInfo, - ServerToolName, - ServerToolResultBlock, - ServerToolUseBlock, - SessionKey, - SessionListSubkeysKey, - SessionMessage, - SessionStore, - SessionStoreEntry, - SessionStoreListEntry, - SessionSummaryEntry, - SettingSource, - StopHookInput, - StreamEvent, - SubagentStartHookInput, - SubagentStartHookSpecificOutput, - SubagentStopHookInput, - SystemMessage, - TaskBudget, - TaskNotificationMessage, - TaskNotificationStatus, - TaskProgressMessage, - TaskStartedMessage, - TaskUsage, - TextBlock, - ThinkingBlock, - ThinkingConfig, - ThinkingConfigAdaptive, - ThinkingConfigDisabled, - ThinkingConfigEnabled, - ToolPermissionContext, - ToolResultBlock, - ToolUseBlock, - UserMessage, - UserPromptSubmitHookInput, -) - -# MCP Server Support - -logger = logging.getLogger(__name__) - -T = TypeVar("T") - - -@dataclass -class SdkMcpTool(Generic[T]): - """Definition for an SDK MCP tool.""" - - name: str - description: str - input_schema: type[T] | dict[str, Any] - handler: Callable[[T], Awaitable[dict[str, Any]]] - annotations: ToolAnnotations | None = None - - -def tool( - name: str, - description: str, - input_schema: type | dict[str, Any], - annotations: ToolAnnotations | None = None, -) -> Callable[[Callable[[Any], Awaitable[dict[str, Any]]]], SdkMcpTool[Any]]: - """Decorator for defining MCP tools with type safety. - - Creates a tool that can be used with SDK MCP servers. The tool runs - in-process within your Python application, providing better performance - than external MCP servers. - - Args: - name: Unique identifier for the tool. This is what Claude will use - to reference the tool in function calls. - description: Human-readable description of what the tool does. - This helps Claude understand when to use the tool. - input_schema: Schema defining the tool's input parameters. - Can be either: - - A dictionary mapping parameter names to types (e.g., {"text": str}) - - A TypedDict class for more complex schemas - - A JSON Schema dictionary for full validation - Use ``Annotated[type, "description"]`` to add a description to a - parameter in either dict-style or TypedDict schemas. - - Returns: - A decorator function that wraps the tool implementation and returns - an SdkMcpTool instance ready for use with create_sdk_mcp_server(). - - Example: - Basic tool with simple schema: - >>> @tool("greet", "Greet a user", {"name": str}) - ... async def greet(args): - ... return {"content": [{"type": "text", "text": f"Hello, {args['name']}!"}]} - - Tool with multiple parameters: - >>> @tool("add", "Add two numbers", {"a": float, "b": float}) - ... async def add_numbers(args): - ... result = args["a"] + args["b"] - ... return {"content": [{"type": "text", "text": f"Result: {result}"}]} - - Tool with error handling: - >>> @tool("divide", "Divide two numbers", {"a": float, "b": float}) - ... async def divide(args): - ... if args["b"] == 0: - ... return {"content": [{"type": "text", "text": "Error: Division by zero"}], "is_error": True} - ... return {"content": [{"type": "text", "text": f"Result: {args['a'] / args['b']}"}]} - - Notes: - - The tool function must be async (defined with async def) - - The function receives a single dict argument with the input parameters - - The function should return a dict with a "content" key containing the response - - Errors can be indicated by including "is_error": True in the response - """ - - def decorator( - handler: Callable[[Any], Awaitable[dict[str, Any]]], - ) -> SdkMcpTool[Any]: - return SdkMcpTool( - name=name, - description=description, - input_schema=input_schema, - handler=handler, - annotations=annotations, - ) - - return decorator - - -def _python_type_to_json_schema(py_type: Any) -> dict[str, Any]: - """Convert a Python type annotation to a JSON Schema dict.""" - origin = get_origin(py_type) - - # NotRequired/Required/ReadOnly survive include_extras=True; unwrap them - if getattr(origin, "_name", None) in ("NotRequired", "Required", "ReadOnly"): - return _python_type_to_json_schema(get_args(py_type)[0]) - - if origin is Annotated: - args = get_args(py_type) - schema = _python_type_to_json_schema(args[0]) - for meta in args[1:]: - if isinstance(meta, str): - schema["description"] = meta - break - return schema - - if py_type is str: - return {"type": "string"} - if py_type is int: - return {"type": "integer"} - if py_type is float: - return {"type": "number"} - if py_type is bool: - return {"type": "boolean"} - - origin = getattr(py_type, "__origin__", None) - - if origin is Union or isinstance(py_type, builtin_types.UnionType): - args = py_type.__args__ - non_none = [a for a in args if a is not builtin_types.NoneType] - if len(non_none) == 1: - return _python_type_to_json_schema(non_none[0]) - return {"anyOf": [_python_type_to_json_schema(a) for a in non_none]} - - if origin is list: - item_args = getattr(py_type, "__args__", None) - if item_args: - return {"type": "array", "items": _python_type_to_json_schema(item_args[0])} - return {"type": "array"} - if origin is dict: - return {"type": "object"} - - if py_type is list: - return {"type": "array"} - if py_type is dict: - return {"type": "object"} - - if is_typeddict(py_type): - return _typeddict_to_json_schema(py_type) - - return {"type": "string"} - - -def _typeddict_to_json_schema(td_class: type) -> dict[str, Any]: - """Convert a TypedDict class to a JSON Schema dict.""" - hints = _get_type_hints(td_class, include_extras=True) - - properties: dict[str, Any] = {} - for field_name, field_type in hints.items(): - properties[field_name] = _python_type_to_json_schema(field_type) - - required_keys = getattr(td_class, "__required_keys__", set(properties.keys())) - schema: dict[str, Any] = { - "type": "object", - "properties": properties, - } - if required_keys: - schema["required"] = sorted(required_keys) - return schema - - -def create_sdk_mcp_server( - name: str, version: str = "1.0.0", tools: list[SdkMcpTool[Any]] | None = None -) -> McpSdkServerConfig: - """Create an in-process MCP server that runs within your Python application. - - Unlike external MCP servers that run as separate processes, SDK MCP servers - run directly in your application's process. This provides: - - Better performance (no IPC overhead) - - Simpler deployment (single process) - - Easier debugging (same process) - - Direct access to your application's state - - Args: - name: Unique identifier for the server. This name is used to reference - the server in the mcp_servers configuration. - version: Server version string. Defaults to "1.0.0". This is for - informational purposes and doesn't affect functionality. - tools: List of SdkMcpTool instances created with the @tool decorator. - These are the functions that Claude can call through this server. - If None or empty, the server will have no tools (rarely useful). - - Returns: - McpSdkServerConfig: A configuration object that can be passed to - ClaudeAgentOptions.mcp_servers. This config contains the server - instance and metadata needed for the SDK to route tool calls. - - Example: - Simple calculator server: - >>> @tool("add", "Add numbers", {"a": float, "b": float}) - ... async def add(args): - ... return {"content": [{"type": "text", "text": f"Sum: {args['a'] + args['b']}"}]} - >>> - >>> @tool("multiply", "Multiply numbers", {"a": float, "b": float}) - ... async def multiply(args): - ... return {"content": [{"type": "text", "text": f"Product: {args['a'] * args['b']}"}]} - >>> - >>> calculator = create_sdk_mcp_server( - ... name="calculator", - ... version="2.0.0", - ... tools=[add, multiply] - ... ) - >>> - >>> # Use with Claude - >>> options = ClaudeAgentOptions( - ... mcp_servers={"calc": calculator}, - ... allowed_tools=["add", "multiply"] - ... ) - - Server with application state access: - >>> class DataStore: - ... def __init__(self): - ... self.items = [] - ... - >>> store = DataStore() - >>> - >>> @tool("add_item", "Add item to store", {"item": str}) - ... async def add_item(args): - ... store.items.append(args["item"]) - ... return {"content": [{"type": "text", "text": f"Added: {args['item']}"}]} - >>> - >>> server = create_sdk_mcp_server("store", tools=[add_item]) - - Notes: - - The server runs in the same process as your Python application - - Tools have direct access to your application's variables and state - - No subprocess or IPC overhead for tool calls - - Server lifecycle is managed automatically by the SDK - - See Also: - - tool(): Decorator for creating tool functions - - ClaudeAgentOptions: Configuration for using servers with query() - """ - from mcp.server import Server - from mcp.types import ( - AudioContent, - CallToolResult, - EmbeddedResource, - ImageContent, - ResourceLink, - TextContent, - Tool, - ) - - # Create MCP server instance - server = Server(name, version=version) - - # Register tools if provided - if tools: - # Store tools for access in handlers - tool_map = {tool_def.name: tool_def for tool_def in tools} - - # Pre-compute tool schemas once at creation time - def _build_schema(tool_def: SdkMcpTool[Any]) -> dict[str, Any]: - if isinstance(tool_def.input_schema, dict): - if ( - "type" in tool_def.input_schema - and "properties" in tool_def.input_schema - and isinstance(tool_def.input_schema["type"], str) - ): - return tool_def.input_schema - properties = {} - for param_name, param_type in tool_def.input_schema.items(): - properties[param_name] = _python_type_to_json_schema(param_type) - return { - "type": "object", - "properties": properties, - "required": list(properties.keys()), - } - if is_typeddict(tool_def.input_schema): - return _typeddict_to_json_schema(tool_def.input_schema) - return {"type": "object", "properties": {}} - - def _build_meta(tool_def: "SdkMcpTool[Any]") -> dict[str, Any] | None: - # The MCP SDK's Zod schema strips unknown annotation fields, so - # Anthropic-specific hints use _meta with namespaced keys instead. - # maxResultSizeChars controls the CLI's layer-2 tool-result spill - # threshold (toolResultStorage.ts maybePersistLargeToolResult). - if tool_def.annotations is None: - return None - max_size = getattr(tool_def.annotations, "maxResultSizeChars", None) - if max_size is None: - return None - return {"anthropic/maxResultSizeChars": max_size} - - cached_tool_list = [ - Tool.model_validate( - { - "name": tool_def.name, - "description": tool_def.description, - "inputSchema": _build_schema(tool_def), - "annotations": tool_def.annotations, - "_meta": _build_meta(tool_def), - } - ) - for tool_def in tools - ] - - # Register list_tools handler to expose available tools - @server.list_tools() # type: ignore[no-untyped-call,untyped-decorator] - async def list_tools() -> list[Tool]: - """Return the list of available tools.""" - return cached_tool_list - - # Register call_tool handler to execute tools - @server.call_tool() # type: ignore[untyped-decorator] - async def call_tool(name: str, arguments: dict[str, Any]) -> Any: - """Execute a tool by name with given arguments.""" - if name not in tool_map: - raise ValueError(f"Tool '{name}' not found") - - tool_def = tool_map[name] - # Call the tool's handler with arguments - result = await tool_def.handler(arguments) - - # Convert result to MCP format - content: list[ - TextContent - | ImageContent - | AudioContent - | ResourceLink - | EmbeddedResource - ] = [] - if "content" in result: - for item in result["content"]: - item_type = item.get("type") - if item_type == "text": - content.append(TextContent(type="text", text=item["text"])) - elif item_type == "image": - content.append( - ImageContent( - type="image", - data=item["data"], - mimeType=item["mimeType"], - ) - ) - elif item_type == "resource_link": - parts = [] - link_name = item.get("name") - uri = item.get("uri") - desc = item.get("description") - if link_name: - parts.append(link_name) - if uri: - parts.append(str(uri)) - if desc: - parts.append(desc) - content.append( - TextContent( - type="text", - text="\n".join(parts) if parts else "Resource link", - ) - ) - elif item_type == "resource": - resource = item.get("resource") or {} - if "text" in resource: - content.append( - TextContent(type="text", text=resource["text"]) - ) - else: - logger.warning( - "Binary embedded resource cannot be converted to text, skipping" - ) - else: - logger.warning( - "Unsupported content type %r in tool result, skipping", - item_type, - ) - - return CallToolResult( - content=content, isError=result.get("is_error", False) - ) - - # Return SDK server configuration - return McpSdkServerConfig(type="sdk", name=name, instance=server) - - -__all__ = [ - # Main exports - "query", - "__version__", - # Transport - "Transport", - "ClaudeSDKClient", - # Types - "PermissionMode", - "McpServerConfig", - "McpSdkServerConfig", - "McpServerStatus", - "McpServerStatusConfig", - "McpServerConnectionStatus", - "McpServerInfo", - "McpStatusResponse", - "McpToolAnnotations", - "McpToolInfo", - "UserMessage", - "AssistantMessage", - "SystemMessage", - "TaskStartedMessage", - "TaskProgressMessage", - "TaskNotificationMessage", - "TaskNotificationStatus", - "TaskUsage", - "ResultMessage", - "RateLimitEvent", - "RateLimitInfo", - "RateLimitStatus", - "RateLimitType", - "StreamEvent", - "Message", - "ClaudeAgentOptions", - "TaskBudget", - "TextBlock", - "ThinkingBlock", - "ThinkingConfig", - "ThinkingConfigAdaptive", - "ThinkingConfigEnabled", - "ThinkingConfigDisabled", - "ToolUseBlock", - "ToolResultBlock", - "ServerToolName", - "ServerToolUseBlock", - "ServerToolResultBlock", - "ContentBlock", - "ContextUsageCategory", - "ContextUsageResponse", - # Tool callbacks - "CanUseTool", - "ToolPermissionContext", - "PermissionResult", - "PermissionResultAllow", - "PermissionResultDeny", - "PermissionUpdate", - # Hook support - "HookCallback", - "HookContext", - "HookInput", - "BaseHookInput", - "PreToolUseHookInput", - "PostToolUseHookInput", - "PostToolUseFailureHookInput", - "PostToolUseFailureHookSpecificOutput", - "UserPromptSubmitHookInput", - "StopHookInput", - "SubagentStopHookInput", - "PreCompactHookInput", - "NotificationHookInput", - "SubagentStartHookInput", - "PermissionRequestHookInput", - "NotificationHookSpecificOutput", - "SubagentStartHookSpecificOutput", - "PermissionRequestHookSpecificOutput", - "HookJSONOutput", - "HookMatcher", - # Agent support - "AgentDefinition", - "SettingSource", - # Plugin support - "SdkPluginConfig", - # Session listing - "list_sessions", - "get_session_info", - "get_session_messages", - "list_subagents", - "get_subagent_messages", - "SDKSessionInfo", - "SessionMessage", - # Session store - "SessionKey", - "SessionStore", - "SessionStoreEntry", - "SessionStoreListEntry", - "SessionSummaryEntry", - "SessionListSubkeysKey", - "InMemorySessionStore", - "fold_session_summary", - "MirrorErrorMessage", - "project_key_for_directory", - "import_session_to_store", - # Session listing (SessionStore-backed async variants) - "list_sessions_from_store", - "get_session_info_from_store", - "get_session_messages_from_store", - "list_subagents_from_store", - "get_subagent_messages_from_store", - # Session mutations - "rename_session", - "tag_session", - "delete_session", - "fork_session", - "ForkSessionResult", - # Session mutations (SessionStore-backed async variants) - "rename_session_via_store", - "tag_session_via_store", - "delete_session_via_store", - "fork_session_via_store", - # Beta support - "SdkBeta", - # Sandbox support - "SandboxSettings", - "SandboxNetworkConfig", - "SandboxIgnoreViolations", - # MCP Server Support - "create_sdk_mcp_server", - "tool", - "SdkMcpTool", - "ToolAnnotations", - # Errors - "ClaudeSDKError", - "CLIConnectionError", - "CLINotFoundError", - "ProcessError", - "CLIJSONDecodeError", -] diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_bundled/.gitignore b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_bundled/.gitignore deleted file mode 100644 index b8f03540..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_bundled/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Ignore bundled CLI binaries (downloaded during build) -claude -claude.exe diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_cli_version.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_cli_version.py deleted file mode 100644 index 964fbdcc..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_cli_version.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Bundled Claude Code CLI version.""" - -__cli_version__ = "2.1.122" diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_errors.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_errors.py deleted file mode 100644 index c86bf235..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_errors.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Error types for Claude SDK.""" - -from typing import Any - - -class ClaudeSDKError(Exception): - """Base exception for all Claude SDK errors.""" - - -class CLIConnectionError(ClaudeSDKError): - """Raised when unable to connect to Claude Code.""" - - -class CLINotFoundError(CLIConnectionError): - """Raised when Claude Code is not found or not installed.""" - - def __init__( - self, message: str = "Claude Code not found", cli_path: str | None = None - ): - if cli_path: - message = f"{message}: {cli_path}" - super().__init__(message) - - -class ProcessError(ClaudeSDKError): - """Raised when the CLI process fails.""" - - def __init__( - self, message: str, exit_code: int | None = None, stderr: str | None = None - ): - self.exit_code = exit_code - self.stderr = stderr - - if exit_code is not None: - message = f"{message} (exit code: {exit_code})" - if stderr: - message = f"{message}\nError output: {stderr}" - - super().__init__(message) - - -class CLIJSONDecodeError(ClaudeSDKError): - """Raised when unable to decode JSON from CLI output.""" - - def __init__(self, line: str, original_error: Exception): - self.line = line - self.original_error = original_error - super().__init__(f"Failed to decode JSON: {line[:100]}...") - - -class MessageParseError(ClaudeSDKError): - """Raised when unable to parse a message from CLI output.""" - - def __init__(self, message: str, data: dict[str, Any] | None = None): - self.data = data - super().__init__(message) diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/__init__.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/__init__.py deleted file mode 100644 index 62791d73..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Internal implementation details.""" diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/_task_compat.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/_task_compat.py deleted file mode 100644 index 5d339bb5..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/_task_compat.py +++ /dev/null @@ -1,176 +0,0 @@ -"""Backend-agnostic detached task spawning. - -``Query`` manages background tasks (the read loop, ``stream_input``, -control-request handlers) that must be cancellable from any task context -— including async-generator finalizers, which Python may run in a -different task than the one that called ``start()``. anyio's -``TaskGroup`` cannot be used for this because its cancel scope has task -affinity: exiting it from a different task either raises ``RuntimeError: -Attempted to exit cancel scope in a different task than it was entered -in`` or busy-spins in ``_deliver_cancellation`` on the asyncio backend. - -Under asyncio this is solved with plain ``loop.create_task()``, but that -raises ``RuntimeError: no running event loop`` under trio. This module -provides ``spawn_detached()`` which dispatches via sniffio to the -appropriate backend primitive, returning a uniform ``TaskHandle``. -""" - -from __future__ import annotations - -import contextvars -import logging -from collections.abc import Callable, Coroutine -from contextlib import suppress -from typing import Any - -import sniffio - -logger = logging.getLogger(__name__) - - -class TaskHandle: - """Backend-agnostic handle to a detached background task. - - Safe to ``.cancel()`` from any task — no anyio cancel-scope task - affinity. - """ - - def cancel(self) -> None: - """Request cancellation of the wrapped task.""" - raise NotImplementedError - - def done(self) -> bool: - """Return True if the wrapped task has finished.""" - raise NotImplementedError - - def add_done_callback(self, callback: Callable[[TaskHandle], None]) -> None: - """Register ``callback(self)`` to run when the task finishes.""" - raise NotImplementedError - - async def wait(self) -> None: - """Wait for the task to finish. - - Suppresses the backend's cancellation exception (the task was - cancelled by us) but re-raises any other exception the task - raised. - """ - raise NotImplementedError - - -class _AsyncioTaskHandle(TaskHandle): - """Thin wrapper around ``asyncio.Task``.""" - - def __init__(self, task: Any) -> None: - self._task = task - - def cancel(self) -> None: - self._task.cancel() - - def done(self) -> bool: - return bool(self._task.done()) - - def add_done_callback(self, callback: Callable[[TaskHandle], None]) -> None: - self._task.add_done_callback(lambda _t: callback(self)) - - async def wait(self) -> None: - import asyncio - - with suppress(asyncio.CancelledError): - await self._task - - -class _TrioTaskHandle(TaskHandle): - """Wraps a trio system task with its own ``CancelScope``.""" - - def __init__(self) -> None: - import trio - - self._cancel_scope = trio.CancelScope() - self._done_event = trio.Event() - self._exception: BaseException | None = None - self._callbacks: list[Callable[[TaskHandle], None]] = [] - - def cancel(self) -> None: - # CancelScope.cancel() is sync and safe to call from any task. - self._cancel_scope.cancel() - - def done(self) -> bool: - return self._done_event.is_set() - - def add_done_callback(self, callback: Callable[[TaskHandle], None]) -> None: - if self.done(): - callback(self) - else: - self._callbacks.append(callback) - - def _mark_done(self, exc: BaseException | None) -> None: - import trio - - # Parity with asyncio's "Task exception was never retrieved": - # close() only .cancel()s child tasks (never .wait()s them), so a - # non-Cancelled exception would otherwise be silently dropped. - if exc is not None and not isinstance(exc, trio.Cancelled): - logger.warning("Unhandled exception in detached trio task", exc_info=exc) - self._exception = exc - self._done_event.set() - for cb in self._callbacks: - # Suppress BaseException so a misbehaving callback can never - # propagate out of the system-task _runner (which would crash - # trio with TrioInternalError). The actual callbacks used here - # are set.discard / dict.pop, so this is purely defensive. - with suppress(BaseException): - cb(self) - self._callbacks.clear() - - async def wait(self) -> None: - import trio - - await self._done_event.wait() - if self._exception is not None and not isinstance( - self._exception, trio.Cancelled - ): - raise self._exception - - -def spawn_detached(coro: Coroutine[Any, Any, Any]) -> TaskHandle: - """Spawn ``coro`` as a detached background task on the current backend. - - - **asyncio**: ``asyncio.get_running_loop().create_task(coro)``. - - **trio**: ``trio.lowlevel.spawn_system_task`` wrapping ``coro`` in a - per-task ``CancelScope`` so the handle supports ``.cancel()``. - """ - backend = sniffio.current_async_library() - if backend == "asyncio": - import asyncio - - loop = asyncio.get_running_loop() - return _AsyncioTaskHandle(loop.create_task(coro)) - if backend == "trio": - import trio - - handle = _TrioTaskHandle() - - async def _runner() -> None: - exc: BaseException | None = None - try: - with handle._cancel_scope: - await coro - except BaseException as e: # noqa: BLE001 - # System tasks must not raise (would crash trio). Store - # the exception on the handle; ``.wait()`` re-raises it. - exc = e - finally: - handle._mark_done(exc) - - # Pass context= so trio system tasks inherit the caller's - # contextvars (asyncio's loop.create_task() does this implicitly; - # spawn_system_task does not). - trio.lowlevel.spawn_system_task(_runner, context=contextvars.copy_context()) - return handle - # Unsupported backend: close the coroutine so we don't leak a "coroutine - # was never awaited" RuntimeWarning on top of the RuntimeError. - coro.close() - raise RuntimeError( - f"Unsupported async backend: {backend!r}. " - "claude_agent_sdk requires asyncio or trio." - ) diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/client.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/client.py deleted file mode 100644 index 2d0029a9..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/client.py +++ /dev/null @@ -1,228 +0,0 @@ -"""Internal client implementation.""" - -import json -import os -from collections.abc import AsyncGenerator, AsyncIterable, AsyncIterator -from dataclasses import asdict, replace -from typing import Any - -from ..types import ( - ClaudeAgentOptions, - HookEvent, - HookMatcher, - Message, -) -from .message_parser import parse_message -from .query import Query -from .session_resume import ( - MaterializedResume, - apply_materialized_options, - build_mirror_batcher, - materialize_resume_session, -) -from .session_store_validation import validate_session_store_options -from .transport import Transport -from .transport.subprocess_cli import SubprocessCLITransport - - -class InternalClient: - """Internal client implementation.""" - - def __init__(self) -> None: - """Initialize the internal client.""" - - def _convert_hooks_to_internal_format( - self, hooks: dict[HookEvent, list[HookMatcher]] - ) -> dict[str, list[dict[str, Any]]]: - """Convert HookMatcher format to internal Query format.""" - internal_hooks: dict[str, list[dict[str, Any]]] = {} - for event, matchers in hooks.items(): - internal_hooks[event] = [] - for matcher in matchers: - # Convert HookMatcher to internal dict format - internal_matcher: dict[str, Any] = { - "matcher": matcher.matcher if hasattr(matcher, "matcher") else None, - "hooks": matcher.hooks if hasattr(matcher, "hooks") else [], - } - if hasattr(matcher, "timeout") and matcher.timeout is not None: - internal_matcher["timeout"] = matcher.timeout - internal_hooks[event].append(internal_matcher) - return internal_hooks - - async def process_query( - self, - prompt: str | AsyncIterable[dict[str, Any]], - options: ClaudeAgentOptions, - transport: Transport | None = None, - ) -> AsyncIterator[Message]: - """Process a query through transport and Query.""" - - # Fail fast on invalid session_store option combinations before - # spawning the subprocess. - validate_session_store_options(options) - - # resume/continue + session_store: load the session from the store - # into a temp CLAUDE_CONFIG_DIR for the subprocess to resume from. - # Skipped when a custom transport was supplied — the materialized - # options never reach a pre-constructed transport, so loading the - # store and writing .credentials.json to a temp dir would be wasted. - materialized = ( - await materialize_resume_session(options) if transport is None else None - ) - inner = self._process_query_inner(prompt, options, transport, materialized) - try: - async for msg in inner: - yield msg - finally: - # ``async for`` does NOT close its iterator when the loop body - # raises (PEP 533 was deferred). Explicitly aclose the inner - # generator first so its ``finally: await query.close()`` runs — - # i.e. the subprocess is terminated — *before* we remove the temp - # CLAUDE_CONFIG_DIR it is reading/writing. - try: - await inner.aclose() - finally: - # The temp dir holds a .credentials.json copy — remove it on - # every exit path, including transport spawn failure before - # the inner try/finally is reached. - if materialized is not None: - await materialized.cleanup() - - async def _process_query_inner( - self, - prompt: str | AsyncIterable[dict[str, Any]], - options: ClaudeAgentOptions, - transport: Transport | None, - materialized: MaterializedResume | None, - ) -> AsyncGenerator[Message, None]: - # Validate and configure permission settings (matching TypeScript SDK logic) - configured_options = options - if options.can_use_tool: - # canUseTool callback requires streaming mode (AsyncIterable prompt) - if isinstance(prompt, str): - raise ValueError( - "can_use_tool callback requires streaming mode. " - "Please provide prompt as an AsyncIterable instead of a string." - ) - - # canUseTool and permission_prompt_tool_name are mutually exclusive - if options.permission_prompt_tool_name: - raise ValueError( - "can_use_tool callback cannot be used with permission_prompt_tool_name. " - "Please use one or the other." - ) - - # Automatically set permission_prompt_tool_name to "stdio" for control protocol - configured_options = replace(options, permission_prompt_tool_name="stdio") - - if materialized is not None: - configured_options = apply_materialized_options( - configured_options, materialized - ) - - # Use provided transport or create subprocess transport - if transport is not None: - chosen_transport = transport - else: - chosen_transport = SubprocessCLITransport( - prompt=prompt, - options=configured_options, - ) - - # Connect transport - await chosen_transport.connect() - - # Extract SDK MCP servers from configured options - sdk_mcp_servers = {} - if configured_options.mcp_servers and isinstance( - configured_options.mcp_servers, dict - ): - for name, config in configured_options.mcp_servers.items(): - if isinstance(config, dict) and config.get("type") == "sdk": - sdk_mcp_servers[name] = config["instance"] # type: ignore[typeddict-item] - - # Extract exclude_dynamic_sections from preset system prompt for the - # initialize request (older CLIs ignore unknown initialize fields). - exclude_dynamic_sections: bool | None = None - sp = configured_options.system_prompt - if isinstance(sp, dict) and sp.get("type") == "preset": - eds = sp.get("exclude_dynamic_sections") - if isinstance(eds, bool): - exclude_dynamic_sections = eds - - # Convert agents to dict format for initialize request - agents_dict = None - if configured_options.agents: - agents_dict = { - name: {k: v for k, v in asdict(agent_def).items() if v is not None} - for name, agent_def in configured_options.agents.items() - } - - # Match ClaudeSDKClient.connect() — without this, query() ignores the env var - initialize_timeout_ms = int( - os.environ.get("CLAUDE_CODE_STREAM_CLOSE_TIMEOUT", "60000") - ) - initialize_timeout = max(initialize_timeout_ms / 1000.0, 60.0) - - # Create Query to handle control protocol - # Always use streaming mode internally (matching TypeScript SDK) - # This ensures agents are always sent via initialize request - query = Query( - transport=chosen_transport, - is_streaming_mode=True, # Always streaming internally - can_use_tool=configured_options.can_use_tool, - hooks=self._convert_hooks_to_internal_format(configured_options.hooks) - if configured_options.hooks - else None, - sdk_mcp_servers=sdk_mcp_servers, - initialize_timeout=initialize_timeout, - agents=agents_dict, - exclude_dynamic_sections=exclude_dynamic_sections, - skills=configured_options.skills, - ) - - if configured_options.session_store is not None: - - async def _on_mirror_error(key: Any, error: str) -> None: - query.report_mirror_error(key, error) - - query.set_transcript_mirror_batcher( - build_mirror_batcher( - store=configured_options.session_store, - materialized=materialized, - env=configured_options.env, - on_error=_on_mirror_error, - ) - ) - - try: - # Start reading messages - await query.start() - - # Always initialize to send agents via stdin (matching TypeScript SDK) - await query.initialize() - - # Handle prompt input - if isinstance(prompt, str): - # For string prompts, write user message to stdin after initialize - # (matching TypeScript SDK behavior) - user_message = { - "type": "user", - "session_id": "", - "message": {"role": "user", "content": prompt}, - "parent_tool_use_id": None, - } - await chosen_transport.write(json.dumps(user_message) + "\n") - query.spawn_task(query.wait_for_result_and_end_input()) - elif isinstance(prompt, AsyncIterable): - # Stream input in background for async iterables - query.spawn_task(query.stream_input(prompt)) - - # Yield parsed messages, skipping unknown message types - async for data in query.receive_messages(): - message = parse_message(data) - if message is not None: - yield message - - finally: - await query.close() diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/message_parser.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/message_parser.py deleted file mode 100644 index 757c5ceb..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/message_parser.py +++ /dev/null @@ -1,285 +0,0 @@ -"""Message parser for Claude Code SDK responses.""" - -import logging -from typing import Any - -from .._errors import MessageParseError -from ..types import ( - AssistantMessage, - ContentBlock, - Message, - MirrorErrorMessage, - RateLimitEvent, - RateLimitInfo, - ResultMessage, - ServerToolResultBlock, - ServerToolUseBlock, - StreamEvent, - SystemMessage, - TaskNotificationMessage, - TaskProgressMessage, - TaskStartedMessage, - TextBlock, - ThinkingBlock, - ToolResultBlock, - ToolUseBlock, - UserMessage, -) - -logger = logging.getLogger(__name__) - - -def parse_message(data: dict[str, Any]) -> Message | None: - """ - Parse message from CLI output into typed Message objects. - - Args: - data: Raw message dictionary from CLI output - - Returns: - Parsed Message object - - Raises: - MessageParseError: If parsing fails or message type is unrecognized - """ - if not isinstance(data, dict): - raise MessageParseError( - f"Invalid message data type (expected dict, got {type(data).__name__})", - data, - ) - - message_type = data.get("type") - if not message_type: - raise MessageParseError("Message missing 'type' field", data) - - match message_type: - case "user": - try: - parent_tool_use_id = data.get("parent_tool_use_id") - tool_use_result = data.get("tool_use_result") - uuid = data.get("uuid") - if isinstance(data["message"]["content"], list): - user_content_blocks: list[ContentBlock] = [] - for block in data["message"]["content"]: - match block["type"]: - case "text": - user_content_blocks.append( - TextBlock(text=block["text"]) - ) - case "tool_use": - user_content_blocks.append( - ToolUseBlock( - id=block["id"], - name=block["name"], - input=block["input"], - ) - ) - case "tool_result": - user_content_blocks.append( - ToolResultBlock( - tool_use_id=block["tool_use_id"], - content=block.get("content"), - is_error=block.get("is_error"), - ) - ) - return UserMessage( - content=user_content_blocks, - uuid=uuid, - parent_tool_use_id=parent_tool_use_id, - tool_use_result=tool_use_result, - ) - return UserMessage( - content=data["message"]["content"], - uuid=uuid, - parent_tool_use_id=parent_tool_use_id, - tool_use_result=tool_use_result, - ) - except KeyError as e: - raise MessageParseError( - f"Missing required field in user message: {e}", data - ) from e - - case "assistant": - try: - content_blocks: list[ContentBlock] = [] - for block in data["message"]["content"]: - match block["type"]: - case "text": - content_blocks.append(TextBlock(text=block["text"])) - case "thinking": - content_blocks.append( - ThinkingBlock( - thinking=block["thinking"], - signature=block["signature"], - ) - ) - case "tool_use": - content_blocks.append( - ToolUseBlock( - id=block["id"], - name=block["name"], - input=block["input"], - ) - ) - case "tool_result": - content_blocks.append( - ToolResultBlock( - tool_use_id=block["tool_use_id"], - content=block.get("content"), - is_error=block.get("is_error"), - ) - ) - case "server_tool_use": - content_blocks.append( - ServerToolUseBlock( - id=block["id"], - name=block["name"], - input=block["input"], - ) - ) - case "advisor_tool_result": - content_blocks.append( - ServerToolResultBlock( - tool_use_id=block["tool_use_id"], - content=block["content"], - ) - ) - - return AssistantMessage( - content=content_blocks, - model=data["message"]["model"], - parent_tool_use_id=data.get("parent_tool_use_id"), - error=data.get("error"), - usage=data["message"].get("usage"), - message_id=data["message"].get("id"), - stop_reason=data["message"].get("stop_reason"), - session_id=data.get("session_id"), - uuid=data.get("uuid"), - ) - except KeyError as e: - raise MessageParseError( - f"Missing required field in assistant message: {e}", data - ) from e - - case "system": - try: - subtype = data["subtype"] - match subtype: - case "task_started": - return TaskStartedMessage( - subtype=subtype, - data=data, - task_id=data["task_id"], - description=data["description"], - uuid=data["uuid"], - session_id=data["session_id"], - tool_use_id=data.get("tool_use_id"), - task_type=data.get("task_type"), - ) - case "task_progress": - return TaskProgressMessage( - subtype=subtype, - data=data, - task_id=data["task_id"], - description=data["description"], - usage=data["usage"], - uuid=data["uuid"], - session_id=data["session_id"], - tool_use_id=data.get("tool_use_id"), - last_tool_name=data.get("last_tool_name"), - ) - case "task_notification": - return TaskNotificationMessage( - subtype=subtype, - data=data, - task_id=data["task_id"], - status=data["status"], - output_file=data["output_file"], - summary=data["summary"], - uuid=data["uuid"], - session_id=data["session_id"], - tool_use_id=data.get("tool_use_id"), - usage=data.get("usage"), - ) - case "mirror_error": - # SDK-synthesized via report_mirror_error — never emitted by the CLI subprocess. - return MirrorErrorMessage( - subtype=subtype, - data=data, - key=data.get("key"), - error=data.get("error", ""), - ) - case _: - return SystemMessage( - subtype=subtype, - data=data, - ) - except KeyError as e: - raise MessageParseError( - f"Missing required field in system message: {e}", data - ) from e - - case "result": - try: - return ResultMessage( - subtype=data["subtype"], - duration_ms=data["duration_ms"], - duration_api_ms=data["duration_api_ms"], - is_error=data["is_error"], - num_turns=data["num_turns"], - session_id=data["session_id"], - stop_reason=data.get("stop_reason"), - total_cost_usd=data.get("total_cost_usd"), - usage=data.get("usage"), - result=data.get("result"), - structured_output=data.get("structured_output"), - model_usage=data.get("modelUsage"), - permission_denials=data.get("permission_denials"), - errors=data.get("errors"), - uuid=data.get("uuid"), - ) - except KeyError as e: - raise MessageParseError( - f"Missing required field in result message: {e}", data - ) from e - - case "stream_event": - try: - return StreamEvent( - uuid=data["uuid"], - session_id=data["session_id"], - event=data["event"], - parent_tool_use_id=data.get("parent_tool_use_id"), - ) - except KeyError as e: - raise MessageParseError( - f"Missing required field in stream_event message: {e}", data - ) from e - - case "rate_limit_event": - try: - info = data["rate_limit_info"] - return RateLimitEvent( - rate_limit_info=RateLimitInfo( - status=info["status"], - resets_at=info.get("resetsAt"), - rate_limit_type=info.get("rateLimitType"), - utilization=info.get("utilization"), - overage_status=info.get("overageStatus"), - overage_resets_at=info.get("overageResetsAt"), - overage_disabled_reason=info.get("overageDisabledReason"), - raw=info, - ), - uuid=data["uuid"], - session_id=data["session_id"], - ) - except KeyError as e: - raise MessageParseError( - f"Missing required field in rate_limit_event message: {e}", data - ) from e - - case _: - # Forward-compatible: skip unrecognized message types so newer - # CLI versions don't crash older SDK versions. - logger.debug("Skipping unknown message type: %s", message_type) - return None diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/query.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/query.py deleted file mode 100644 index 0843453e..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/query.py +++ /dev/null @@ -1,839 +0,0 @@ -"""Query class for handling bidirectional control protocol.""" - -import json -import logging -import os -import uuid -from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable -from contextlib import suppress -from typing import TYPE_CHECKING, Any, Literal - -import anyio -from mcp.types import ( - CallToolRequest, - CallToolRequestParams, - ListToolsRequest, -) - -from ..types import ( - PermissionMode, - PermissionResultAllow, - PermissionResultDeny, - SDKControlPermissionRequest, - SDKControlRequest, - SDKControlResponse, - SDKHookCallbackRequest, - ToolPermissionContext, -) -from ._task_compat import TaskHandle, spawn_detached -from .transport import Transport - -if TYPE_CHECKING: - from mcp.server import Server as McpServer - - from ..types import SessionKey - from .transcript_mirror_batcher import TranscriptMirrorBatcher - -logger = logging.getLogger(__name__) - - -def _convert_hook_output_for_cli(hook_output: dict[str, Any]) -> dict[str, Any]: - """Convert Python-safe field names to CLI-expected field names. - - The Python SDK uses `async_` and `continue_` to avoid keyword conflicts, - but the CLI expects `async` and `continue`. This function performs the - necessary conversion. - """ - converted = {} - for key, value in hook_output.items(): - # Convert Python-safe names to JavaScript names - if key == "async_": - converted["async"] = value - elif key == "continue_": - converted["continue"] = value - else: - converted[key] = value - return converted - - -class Query: - """Handles bidirectional control protocol on top of Transport. - - This class manages: - - Control request/response routing - - Hook callbacks - - Tool permission callbacks - - Message streaming - - Initialization handshake - """ - - def __init__( - self, - transport: Transport, - is_streaming_mode: bool, - can_use_tool: Callable[ - [str, dict[str, Any], ToolPermissionContext], - Awaitable[PermissionResultAllow | PermissionResultDeny], - ] - | None = None, - hooks: dict[str, list[dict[str, Any]]] | None = None, - sdk_mcp_servers: dict[str, "McpServer"] | None = None, - initialize_timeout: float = 60.0, - agents: dict[str, dict[str, Any]] | None = None, - exclude_dynamic_sections: bool | None = None, - skills: list[str] | Literal["all"] | None = None, - ): - """Initialize Query with transport and callbacks. - - Args: - transport: Low-level transport for I/O - is_streaming_mode: Whether using streaming (bidirectional) mode - can_use_tool: Optional callback for tool permission requests - hooks: Optional hook configurations - sdk_mcp_servers: Optional SDK MCP server instances - initialize_timeout: Timeout in seconds for the initialize request - agents: Optional agent definitions to send via initialize - exclude_dynamic_sections: Optional preset-prompt flag to send via - initialize (see ``SystemPromptPreset``) - skills: Optional skill allowlist to send via initialize so the CLI - can filter which skills are loaded into the system prompt - """ - self._initialize_timeout = initialize_timeout - self.transport = transport - self.is_streaming_mode = is_streaming_mode - self.can_use_tool = can_use_tool - self.hooks = hooks or {} - self.sdk_mcp_servers = sdk_mcp_servers or {} - self._agents = agents - self._exclude_dynamic_sections = exclude_dynamic_sections - self._skills = skills - - # Control protocol state - self.pending_control_responses: dict[str, anyio.Event] = {} - self.pending_control_results: dict[str, dict[str, Any] | Exception] = {} - self.hook_callbacks: dict[str, Callable[..., Any]] = {} - self.next_callback_id = 0 - self._request_counter = 0 - - # Message stream - self._message_send, self._message_receive = anyio.create_memory_object_stream[ - dict[str, Any] - ](max_buffer_size=100) - self._read_task: TaskHandle | None = None - self._child_tasks: set[TaskHandle] = set() - self._inflight_requests: dict[str, TaskHandle] = {} - self._initialized = False - self._closed = False - self._initialization_result: dict[str, Any] | None = None - - # Track first result for proper stream closure with SDK MCP servers - self._first_result_event = anyio.Event() - - # SessionStore mirroring (set via set_transcript_mirror_batcher) - self._transcript_mirror_batcher: TranscriptMirrorBatcher | None = None - - def set_transcript_mirror_batcher(self, batcher: "TranscriptMirrorBatcher") -> None: - """Attach a batcher that receives ``transcript_mirror`` frames. - - When set, the read loop peels ``transcript_mirror`` frames off stdout - (they are not yielded to consumers), enqueues them on the batcher, and - flushes before yielding each ``result`` message. - """ - self._transcript_mirror_batcher = batcher - - def report_mirror_error(self, key: "SessionKey | None", error: str) -> None: - """Surface a :meth:`SessionStore.append` failure as a system message. - - Called from the batcher's ``on_error``; the dropped batch is not - retried (at-most-once delivery), so this is the consumer's only signal. - Non-blocking — if the message buffer is full the error is logged and - dropped rather than back-pressuring the read loop. - """ - msg: dict[str, Any] = { - "type": "system", - "subtype": "mirror_error", - "error": error, - "key": key, - "uuid": str(uuid.uuid4()), - "session_id": key.get("session_id", "") if key else "", - } - try: - self._message_send.send_nowait(msg) - except Exception as e: # pragma: no cover - buffer-full edge case - logger.warning("Dropping mirror_error message (buffer full): %s", e) - - async def initialize(self) -> dict[str, Any] | None: - """Initialize control protocol if in streaming mode. - - Returns: - Initialize response with supported commands, or None if not streaming - """ - if not self.is_streaming_mode: - return None - - # Build hooks configuration for initialization - hooks_config: dict[str, Any] = {} - if self.hooks: - for event, matchers in self.hooks.items(): - if matchers: - hooks_config[event] = [] - for matcher in matchers: - callback_ids = [] - for callback in matcher.get("hooks", []): - callback_id = f"hook_{self.next_callback_id}" - self.next_callback_id += 1 - self.hook_callbacks[callback_id] = callback - callback_ids.append(callback_id) - hook_matcher_config: dict[str, Any] = { - "matcher": matcher.get("matcher"), - "hookCallbackIds": callback_ids, - } - if matcher.get("timeout") is not None: - hook_matcher_config["timeout"] = matcher.get("timeout") - hooks_config[event].append(hook_matcher_config) - - # Send initialize request - request: dict[str, Any] = { - "subtype": "initialize", - "hooks": hooks_config if hooks_config else None, - } - if self._agents: - request["agents"] = self._agents - if self._exclude_dynamic_sections is not None: - request["excludeDynamicSections"] = self._exclude_dynamic_sections - # 'all' and omitted are equivalent at the wire level (no filter), so - # only send the field when it's an explicit list. - if isinstance(self._skills, list): - request["skills"] = self._skills - - # Use longer timeout for initialize since MCP servers may take time to start - response = await self._send_control_request( - request, timeout=self._initialize_timeout - ) - self._initialized = True - self._initialization_result = response # Store for later access - return response - - async def start(self) -> None: - """Start reading messages from transport.""" - if self._read_task is None: - self._read_task = spawn_detached(self._read_messages()) - - def spawn_task(self, coro: Any) -> TaskHandle: - """Spawn a child task that will be cancelled on close().""" - task = spawn_detached(coro) - self._child_tasks.add(task) - task.add_done_callback(self._child_tasks.discard) - return task - - def _spawn_control_request_handler(self, request: SDKControlRequest) -> None: - """Spawn a control request handler and track it for cancellation.""" - req_id = request["request_id"] - task = self.spawn_task(self._handle_control_request(request)) - self._inflight_requests[req_id] = task - - def _done(_t: TaskHandle) -> None: - self._inflight_requests.pop(req_id, None) - - task.add_done_callback(_done) - - async def _read_messages(self) -> None: - """Read messages from transport and route them.""" - try: - async for message in self.transport.read_messages(): - if self._closed: - break - - msg_type = message.get("type") - - # Route control messages - if msg_type == "control_response": - response = message.get("response", {}) - request_id = response.get("request_id") - if request_id in self.pending_control_responses: - event = self.pending_control_responses[request_id] - if response.get("subtype") == "error": - self.pending_control_results[request_id] = Exception( - response.get("error", "Unknown error") - ) - else: - self.pending_control_results[request_id] = response - event.set() - continue - - elif msg_type == "control_request": - # Handle incoming control requests from CLI - # Cast message to SDKControlRequest for type safety - request: SDKControlRequest = message # type: ignore[assignment] - if not self._closed: - self._spawn_control_request_handler(request) - continue - - elif msg_type == "control_cancel_request": - cancel_id = message.get("request_id") - if cancel_id: - inflight = self._inflight_requests.pop(cancel_id, None) - if inflight: - inflight.cancel() - continue - - elif msg_type == "transcript_mirror": - # SessionStore write path: peel mirror frames off stdout - # and hand to the batcher; do NOT yield to consumers. - if self._transcript_mirror_batcher is not None: - self._transcript_mirror_batcher.enqueue( - message["filePath"], message["entries"] - ) - continue - - # Track results for proper stream closure - if msg_type == "result": - # Flush pending transcript mirror entries before yielding - # result so consumers observing the result can rely on the - # SessionStore being up to date for this turn. - if self._transcript_mirror_batcher is not None: - await self._transcript_mirror_batcher.flush() - self._first_result_event.set() - - # Regular SDK messages go to the stream - await self._message_send.send(message) - - except anyio.get_cancelled_exc_class(): - # Task was cancelled - this is expected behavior - logger.debug("Read task cancelled") - raise # Re-raise to properly handle cancellation - except Exception as e: - logger.error(f"Fatal error in message reader: {e}") - # Signal all pending control requests so they fail fast instead of timing out - for request_id, event in list(self.pending_control_responses.items()): - if request_id not in self.pending_control_results: - self.pending_control_results[request_id] = e - event.set() - # Put error in stream so iterators can handle it - await self._message_send.send({"type": "error", "error": str(e)}) - finally: - # Flush any remaining transcript mirror entries before closing so - # an early stdout EOF or transport error doesn't drop entries - # batched this turn. flush() never raises. Shielded so the await - # still runs when this finally is reached via cancellation. - if self._transcript_mirror_batcher is not None: - with anyio.CancelScope(shield=True): - await self._transcript_mirror_batcher.flush() - # Unblock any waiters (e.g. string-prompt path waiting for first - # result) so they don't stall for the full timeout on early exit. - self._first_result_event.set() - # Always signal end of stream. send_nowait: trio's level-triggered - # cancellation would re-raise Cancelled at an await checkpoint - # here, dropping the sentinel and leaving receive_messages() hung. - # close() is the fallback for the buffer-full case where - # send_nowait raises WouldBlock — receivers then exit on - # EndOfStream after draining. - with suppress(anyio.WouldBlock): - self._message_send.send_nowait({"type": "end"}) - self._message_send.close() - - async def _handle_control_request(self, request: SDKControlRequest) -> None: - """Handle incoming control request from CLI.""" - request_id = request["request_id"] - request_data = request["request"] - subtype = request_data["subtype"] - - try: - response_data: dict[str, Any] = {} - - if subtype == "can_use_tool": - permission_request: SDKControlPermissionRequest = request_data # type: ignore[assignment] - original_input = permission_request["input"] - # Handle tool permission request - if not self.can_use_tool: - raise Exception("canUseTool callback is not provided") - - context = ToolPermissionContext( - signal=None, # TODO: Add abort signal support - suggestions=permission_request.get("permission_suggestions", []) - or [], - tool_use_id=permission_request.get("tool_use_id"), - agent_id=permission_request.get("agent_id"), - ) - - response = await self.can_use_tool( - permission_request["tool_name"], - permission_request["input"], - context, - ) - - # Convert PermissionResult to expected dict format - if isinstance(response, PermissionResultAllow): - response_data = { - "behavior": "allow", - "updatedInput": ( - response.updated_input - if response.updated_input is not None - else original_input - ), - } - if response.updated_permissions is not None: - response_data["updatedPermissions"] = [ - permission.to_dict() - for permission in response.updated_permissions - ] - elif isinstance(response, PermissionResultDeny): - response_data = {"behavior": "deny", "message": response.message} - if response.interrupt: - response_data["interrupt"] = response.interrupt - else: - raise TypeError( - f"Tool permission callback must return PermissionResult (PermissionResultAllow or PermissionResultDeny), got {type(response)}" - ) - - elif subtype == "hook_callback": - hook_callback_request: SDKHookCallbackRequest = request_data # type: ignore[assignment] - # Handle hook callback - callback_id = hook_callback_request["callback_id"] - callback = self.hook_callbacks.get(callback_id) - if not callback: - raise Exception(f"No hook callback found for ID: {callback_id}") - - hook_output = await callback( - request_data.get("input"), - request_data.get("tool_use_id"), - {"signal": None}, # TODO: Add abort signal support - ) - # Convert Python-safe field names (async_, continue_) to CLI-expected names (async, continue) - response_data = _convert_hook_output_for_cli(hook_output) - - elif subtype == "mcp_message": - # Handle SDK MCP request - server_name = request_data.get("server_name") - mcp_message = request_data.get("message") - - if not server_name or not mcp_message: - raise Exception("Missing server_name or message for MCP request") - - # Type narrowing - we've verified these are not None above - assert isinstance(server_name, str) - assert isinstance(mcp_message, dict) - mcp_response = await self._handle_sdk_mcp_request( - server_name, mcp_message - ) - # Wrap the MCP response as expected by the control protocol - response_data = {"mcp_response": mcp_response} - - else: - raise Exception(f"Unsupported control request subtype: {subtype}") - - # Send success response - success_response: SDKControlResponse = { - "type": "control_response", - "response": { - "subtype": "success", - "request_id": request_id, - "response": response_data, - }, - } - await self.transport.write(json.dumps(success_response) + "\n") - - except anyio.get_cancelled_exc_class(): - # Request was cancelled via control_cancel_request; the CLI has - # already abandoned this request, so don't write a response. - raise - except Exception as e: - # Send error response - error_response: SDKControlResponse = { - "type": "control_response", - "response": { - "subtype": "error", - "request_id": request_id, - "error": str(e), - }, - } - await self.transport.write(json.dumps(error_response) + "\n") - - async def _send_control_request( - self, request: dict[str, Any], timeout: float = 60.0 - ) -> dict[str, Any]: - """Send control request to CLI and wait for response. - - Args: - request: The control request to send - timeout: Timeout in seconds to wait for response (default 60s) - """ - if not self.is_streaming_mode: - raise Exception("Control requests require streaming mode") - - # Generate unique request ID - self._request_counter += 1 - request_id = f"req_{self._request_counter}_{os.urandom(4).hex()}" - - # Create event for response - event = anyio.Event() - self.pending_control_responses[request_id] = event - - # Build and send request - control_request = { - "type": "control_request", - "request_id": request_id, - "request": request, - } - - await self.transport.write(json.dumps(control_request) + "\n") - - # Wait for response - try: - with anyio.fail_after(timeout): - await event.wait() - - result = self.pending_control_results.pop(request_id) - self.pending_control_responses.pop(request_id, None) - - if isinstance(result, Exception): - raise result - - response_data = result.get("response", {}) - return response_data if isinstance(response_data, dict) else {} - except TimeoutError as e: - self.pending_control_responses.pop(request_id, None) - self.pending_control_results.pop(request_id, None) - raise Exception(f"Control request timeout: {request.get('subtype')}") from e - - async def _handle_sdk_mcp_request( - self, server_name: str, message: dict[str, Any] - ) -> dict[str, Any]: - """Handle an MCP request for an SDK server. - - This acts as a bridge between JSONRPC messages from the CLI - and the in-process MCP server. Ideally the MCP SDK would provide - a method to handle raw JSONRPC, but for now we route manually. - - Args: - server_name: Name of the SDK MCP server - message: The JSONRPC message - - Returns: - The response message - """ - if server_name not in self.sdk_mcp_servers: - return { - "jsonrpc": "2.0", - "id": message.get("id"), - "error": { - "code": -32601, - "message": f"Server '{server_name}' not found", - }, - } - - server = self.sdk_mcp_servers[server_name] - method = message.get("method") - params = message.get("params", {}) - - try: - # TODO: Python MCP SDK lacks the Transport abstraction that TypeScript has. - # TypeScript: server.connect(transport) allows custom transports - # Python: server.run(read_stream, write_stream) requires actual streams - # - # This forces us to manually route methods. When Python MCP adds Transport - # support, we can refactor to match the TypeScript approach. - if method == "initialize": - # Handle MCP initialization - hardcoded for tools only, no listChanged - return { - "jsonrpc": "2.0", - "id": message.get("id"), - "result": { - "protocolVersion": "2024-11-05", - "capabilities": { - "tools": {} # Tools capability without listChanged - }, - "serverInfo": { - "name": server.name, - "version": server.version or "1.0.0", - }, - }, - } - - elif method == "tools/list": - request = ListToolsRequest(method=method) - handler = server.request_handlers.get(ListToolsRequest) - if handler: - result = await handler(request) - # Convert MCP result to JSONRPC response - tools_data = [] - for tool in result.root.tools: # type: ignore[union-attr] - tool_data: dict[str, Any] = { - "name": tool.name, - "description": tool.description, - "inputSchema": ( - tool.inputSchema.model_dump() - if hasattr(tool.inputSchema, "model_dump") - else tool.inputSchema - ) - if tool.inputSchema - else {}, - } - if tool.annotations: - tool_data["annotations"] = tool.annotations.model_dump( - exclude_none=True - ) - if tool.meta: - tool_data["_meta"] = tool.meta - tools_data.append(tool_data) - return { - "jsonrpc": "2.0", - "id": message.get("id"), - "result": {"tools": tools_data}, - } - - elif method == "tools/call": - call_request = CallToolRequest( - method=method, - params=CallToolRequestParams( - name=params.get("name"), arguments=params.get("arguments", {}) - ), - ) - handler = server.request_handlers.get(CallToolRequest) - if handler: - result = await handler(call_request) - # Convert MCP result to JSONRPC response - content = [] - for item in result.root.content: # type: ignore[union-attr] - item_type = getattr(item, "type", None) - if item_type == "text": - content.append( - {"type": "text", "text": getattr(item, "text", "")} - ) - elif item_type == "image": - content.append( - { - "type": "image", - "data": getattr(item, "data", ""), - "mimeType": getattr(item, "mimeType", ""), - } - ) - elif item_type == "resource_link": - parts = [] - name = getattr(item, "name", None) - uri = getattr(item, "uri", None) - desc = getattr(item, "description", None) - if name: - parts.append(name) - if uri: - parts.append(str(uri)) - if desc: - parts.append(desc) - content.append( - { - "type": "text", - "text": "\n".join(parts) - if parts - else "Resource link", - } - ) - elif item_type == "resource": - resource = getattr(item, "resource", None) - if resource and hasattr(resource, "text"): - content.append({"type": "text", "text": resource.text}) - else: - logger.warning( - "Binary embedded resource cannot be converted to text, skipping" - ) - else: - logger.warning( - "Unsupported content type %r in tool result, skipping", - item_type, - ) - - response_data = {"content": content} - if hasattr(result.root, "isError") and result.root.isError: - response_data["isError"] = True # type: ignore[assignment] - - return { - "jsonrpc": "2.0", - "id": message.get("id"), - "result": response_data, - } - - elif method == "notifications/initialized": - # Handle initialized notification - just acknowledge it - return {"jsonrpc": "2.0", "result": {}} - - # Add more methods here as MCP SDK adds them (resources, prompts, etc.) - # This is the limitation Ashwin pointed out - we have to manually update - - return { - "jsonrpc": "2.0", - "id": message.get("id"), - "error": {"code": -32601, "message": f"Method '{method}' not found"}, - } - - except Exception as e: - return { - "jsonrpc": "2.0", - "id": message.get("id"), - "error": {"code": -32603, "message": str(e)}, - } - - async def get_mcp_status(self) -> dict[str, Any]: - """Get current MCP server connection status.""" - return await self._send_control_request({"subtype": "mcp_status"}) - - async def get_context_usage(self) -> dict[str, Any]: - """Get a breakdown of current context window usage by category.""" - return await self._send_control_request({"subtype": "get_context_usage"}) - - async def interrupt(self) -> None: - """Send interrupt control request.""" - await self._send_control_request({"subtype": "interrupt"}) - - async def set_permission_mode(self, mode: PermissionMode) -> None: - """Change permission mode.""" - await self._send_control_request( - { - "subtype": "set_permission_mode", - "mode": mode, - } - ) - - async def set_model(self, model: str | None) -> None: - """Change the AI model.""" - await self._send_control_request( - { - "subtype": "set_model", - "model": model, - } - ) - - async def rewind_files(self, user_message_id: str) -> None: - """Rewind tracked files to their state at a specific user message. - - Requires file checkpointing to be enabled via the `enable_file_checkpointing` option. - - Args: - user_message_id: UUID of the user message to rewind to - """ - await self._send_control_request( - { - "subtype": "rewind_files", - "user_message_id": user_message_id, - } - ) - - async def reconnect_mcp_server(self, server_name: str) -> None: - """Reconnect a disconnected or failed MCP server. - - Args: - server_name: The name of the MCP server to reconnect - """ - await self._send_control_request( - { - "subtype": "mcp_reconnect", - "serverName": server_name, - } - ) - - async def toggle_mcp_server(self, server_name: str, enabled: bool) -> None: - """Enable or disable an MCP server. - - Args: - server_name: The name of the MCP server to toggle - enabled: Whether the server should be enabled - """ - await self._send_control_request( - { - "subtype": "mcp_toggle", - "serverName": server_name, - "enabled": enabled, - } - ) - - async def stop_task(self, task_id: str) -> None: - """Stop a running task. - - Args: - task_id: The task ID from task_notification events - """ - await self._send_control_request( - { - "subtype": "stop_task", - "task_id": task_id, - } - ) - - async def wait_for_result_and_end_input(self) -> None: - """Wait for the first result (if needed) then close stdin. - - If SDK MCP servers or hooks require bidirectional communication, - keeps stdin open until the first result arrives. The control protocol - requires stdin to remain open for the entire conversation, so no - timeout is applied. The event is guaranteed to fire: either when the - result message arrives, or in _read_messages' finally block if the - process exits early. - """ - if self.sdk_mcp_servers or self.hooks: - logger.debug( - "Waiting for first result before closing stdin " - f"(sdk_mcp_servers={len(self.sdk_mcp_servers)}, " - f"has_hooks={bool(self.hooks)})" - ) - await self._first_result_event.wait() - - await self.transport.end_input() - - async def stream_input(self, stream: AsyncIterable[dict[str, Any]]) -> None: - """Stream input messages to transport. - - If SDK MCP servers or hooks are present, waits for the first result - before closing stdin to allow bidirectional control protocol communication. - """ - try: - async for message in stream: - if self._closed: - break - await self.transport.write(json.dumps(message) + "\n") - - await self.wait_for_result_and_end_input() - except Exception as e: - logger.debug(f"Error streaming input: {e}") - - async def receive_messages(self) -> AsyncIterator[dict[str, Any]]: - """Receive SDK messages (not control messages).""" - async for message in self._message_receive: - # Check for special messages - if message.get("type") == "end": - break - elif message.get("type") == "error": - raise Exception(message.get("error", "Unknown error")) - - yield message - - async def close(self) -> None: - """Close the query and transport.""" - self._closed = True - # Final-flush mirror entries before tearing down so .return()/break - # don't drop the current turn when the process exits immediately. - if self._transcript_mirror_batcher is not None: - await self._transcript_mirror_batcher.close() - for task in list(self._child_tasks): - task.cancel() - if self._read_task is not None and not self._read_task.done(): - self._read_task.cancel() - await self._read_task.wait() - self._read_task = None - # The read task's finally closed the send side; repeat here for the - # case where start() was never called. Do NOT close the receive - # side — it belongs to the consumer, and anyio's receive_nowait() - # checks _closed before the buffer, so closing it here would make a - # non-parked consumer drop buffered messages with - # ClosedResourceError. _message_send.close() alone yields - # EndOfStream after the buffer drains. - self._message_send.close() - await self.transport.close() - - # Make Query an async iterator - def __aiter__(self) -> AsyncIterator[dict[str, Any]]: - """Return async iterator for messages.""" - return self.receive_messages() - - async def __anext__(self) -> dict[str, Any]: - """Get next message.""" - async for message in self.receive_messages(): - return message - raise StopAsyncIteration diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_import.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_import.py deleted file mode 100644 index 30d7823b..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_import.py +++ /dev/null @@ -1,163 +0,0 @@ -"""Replay a local on-disk session transcript into a :class:`SessionStore`. - -This is the inverse of :mod:`session_resume` — where ``materialize_resume_session`` -reads a store and writes a temp ``~/.claude`` tree, ``import_session_to_store`` -reads the local ``~/.claude/projects//.jsonl`` (plus subagent -transcripts) and replays each line into ``store.append()``. - -Mirrors the TypeScript SDK's ``importSessionToStore``. -""" - -from __future__ import annotations - -import errno -import json -from collections.abc import Iterator -from pathlib import Path - -from ..types import SessionKey, SessionStore, SessionStoreEntry -from .sessions import ( - _resolve_session_file_path, - _validate_uuid, -) -from .transcript_mirror_batcher import MAX_PENDING_BYTES, MAX_PENDING_ENTRIES - -__all__ = ["import_session_to_store"] - - -async def import_session_to_store( - session_id: str, - store: SessionStore, - *, - directory: str | None = None, - include_subagents: bool = True, - batch_size: int = MAX_PENDING_ENTRIES, -) -> None: - """Replay a local session transcript into a :class:`SessionStore`. - - Streams the on-disk JSONL line-by-line and calls ``store.append(key, batch)`` - every ``batch_size`` entries (or 1 MiB of line bytes, whichever comes - first). Useful for migrating existing local sessions to a remote store, or - for catching a store up after a :class:`MirrorErrorMessage` indicated a - live-mirror gap. Adapters should treat ``entry["uuid"]`` as an idempotency - key so re-import is duplicate-safe. - - The destination ``project_key`` is the name of the on-disk project - directory the session file was found in — the same key - :func:`file_path_to_session_key` (and thus ``TranscriptMirrorBatcher``) - would have produced for the same file — so an imported session is - indistinguishable from a live-mirrored one and resumable via - ``query(options=ClaudeAgentOptions(session_store=store, resume=session_id))`` - from the original ``cwd``. - - Args: - session_id: UUID of the session to import. - store: Destination :class:`SessionStore`. - directory: Project directory path (same semantics as - :func:`list_sessions`). When omitted, all project directories are - searched for the session file. - include_subagents: If ``True`` (default), also import subagent - transcripts under ``/subagents/**`` and their - ``.meta.json`` sidecars. - batch_size: Maximum entries per ``store.append()`` call. Default 500. - - Raises: - ValueError: If ``session_id`` is not a valid UUID. - FileNotFoundError: If the session JSONL cannot be found on disk. - """ - if not _validate_uuid(session_id): - raise ValueError(f"Invalid session_id: {session_id}") - - resolved = _resolve_session_file_path(session_id, directory) - if resolved is None: - raise FileNotFoundError(f"Session {session_id} not found") - - # Key under the on-disk project directory name — matches - # file_path_to_session_key() / TranscriptMirrorBatcher even when the - # resolver's search (directory=None) or worktree fallback found the file - # somewhere other than `directory`. - project_key = resolved.parent.name - if batch_size <= 0: - batch_size = MAX_PENDING_ENTRIES - - main_key: SessionKey = {"project_key": project_key, "session_id": session_id} - await _append_jsonl_file_in_batches(resolved, main_key, store, batch_size) - - if not include_subagents: - return - - # Subagent transcripts live at //subagents/**. - session_dir = resolved.with_suffix("") - subagents_dir = session_dir / "subagents" - for file_path in _collect_jsonl_files(subagents_dir): - # subpath is the path relative to session_dir, '/'-joined, sans .jsonl — - # e.g. subagents/agent-abc or subagents/workflows/run-1/agent-def. - # Matches file_path_to_session_key() so list_subkeys() and - # get_subagent_messages_from_store() round-trip. - rel_parts = list(file_path.relative_to(session_dir).parts) - rel_parts[-1] = rel_parts[-1][: -len(".jsonl")] - sub_key: SessionKey = { - "project_key": project_key, - "session_id": session_id, - "subpath": "/".join(rel_parts), - } - await _append_jsonl_file_in_batches(file_path, sub_key, store, batch_size) - - # The on-disk .jsonl does NOT contain agent_metadata entries — those - # are only sent to live mirrors and persisted in the .meta.json - # sidecar. Import the sidecar so materialize_resume_session() can - # recreate it and resumed subagents keep their agentType/worktreePath. - meta_path = file_path.with_name(file_path.name[: -len(".jsonl")] + ".meta.json") - try: - meta = json.loads(meta_path.read_text(encoding="utf-8")) - except OSError as e: - if e.errno != errno.ENOENT: - raise - else: - meta_entry: SessionStoreEntry = {"type": "agent_metadata"} - meta_entry.update(meta) - await store.append(sub_key, [meta_entry]) - - -async def _append_jsonl_file_in_batches( - file_path: Path, - key: SessionKey, - store: SessionStore, - batch_size: int, -) -> None: - """Stream-read a JSONL file line-by-line, parsing each line and flushing to - ``store.append()`` in batches of ``batch_size`` entries (or - ``MAX_PENDING_BYTES`` of line text, whichever comes first). Skips blank - lines.""" - batch: list[SessionStoreEntry] = [] - nbytes = 0 - with file_path.open(encoding="utf-8") as f: - for line in f: - line = line.rstrip("\n") - if not line: - continue - batch.append(json.loads(line)) - nbytes += len(line) - if len(batch) >= batch_size or nbytes >= MAX_PENDING_BYTES: - await store.append(key, batch) - batch = [] - nbytes = 0 - if batch: - await store.append(key, batch) - - -def _collect_jsonl_files(base_dir: Path) -> Iterator[Path]: - """Recursively yield all ``*.jsonl`` file paths under ``base_dir``. - - Yields nothing if ``base_dir`` does not exist. Sorted per directory so - import order is deterministic across platforms. - """ - try: - dirents = sorted(base_dir.iterdir(), key=lambda p: p.name) - except OSError: - return - for entry in dirents: - if entry.is_dir(): - yield from _collect_jsonl_files(entry) - elif entry.is_file() and entry.name.endswith(".jsonl"): - yield entry diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_mutations.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_mutations.py deleted file mode 100644 index 55a7f213..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_mutations.py +++ /dev/null @@ -1,962 +0,0 @@ -"""Portable session mutation functions for the Agent SDK. - -Rename/tag append typed metadata entries to the session's JSONL (matching -the CLI pattern); delete removes the JSONL file; fork creates a new session -with UUID remapping. Safe to call from any SDK host process — see -concurrent-writer note below. - -Directory resolution matches list_sessions / get_session_messages: -``directory`` is the project path (not the storage dir); when omitted, all -project directories are searched for the session file. - -Concurrent writers: if the target session is currently open in a CLI -process, the CLI's reAppendSessionMetadata() tail-reads before re-appending -its cached metadata. If an SDK write (e.g. a custom-title entry) is in the -tail scan window, the CLI absorbs it into its cache and re-appends the SDK -value — not the stale CLI value. -""" - -from __future__ import annotations - -import errno -import json -import os -import re -import shutil -import unicodedata -import uuid as uuid_mod -from collections.abc import Callable -from dataclasses import dataclass -from datetime import datetime, timezone -from pathlib import Path -from typing import Any, cast - -from ..types import SessionKey, SessionStore, SessionStoreEntry -from .session_store_validation import _store_implements -from .sessions import ( - LITE_READ_BUF_SIZE, - _canonicalize_path, - _extract_first_prompt_from_head, - _extract_last_json_string_field, - _find_project_dir, - _get_projects_dir, - _get_worktree_paths, - _validate_uuid, - project_key_for_directory, -) - -# --------------------------------------------------------------------------- -# Public API -# --------------------------------------------------------------------------- - - -def rename_session( - session_id: str, - title: str, - directory: str | None = None, -) -> None: - """Rename a session by appending a custom-title entry. - - ``list_sessions`` reads the LAST custom-title from the file tail, so - repeated calls are safe — the most recent wins. - - Args: - session_id: UUID of the session to rename. - title: New session title. Leading/trailing whitespace is stripped. - Must be non-empty after stripping. - directory: Project directory path (same semantics as - ``list_sessions(directory=...)``). When omitted, all project - directories are searched for the session file. - - Raises: - ValueError: If ``session_id`` is not a valid UUID, or if ``title`` - is empty/whitespace-only. - FileNotFoundError: If the session file cannot be found. - - See Also: - :func:`rename_session_via_store` for the :class:`SessionStore`-backed - async variant. - - Example: - Rename a session in a specific project:: - - rename_session( - "550e8400-e29b-41d4-a716-446655440000", - "My refactoring session", - directory="/path/to/project", - ) - """ - if not _validate_uuid(session_id): - raise ValueError(f"Invalid session_id: {session_id}") - # Matches CLI guard — empty/whitespace titles are rejected rather than - # overloaded as "clear title". - stripped = title.strip() - if not stripped: - raise ValueError("title must be non-empty") - - data = ( - json.dumps( - { - "type": "custom-title", - "customTitle": stripped, - "sessionId": session_id, - }, - separators=(",", ":"), - ) - + "\n" - ) - - _append_to_session(session_id, data, directory) - - -def tag_session( - session_id: str, - tag: str | None, - directory: str | None = None, -) -> None: - """Tag a session. Pass ``None`` to clear the tag. - - Appends a ``{type:'tag',tag:,sessionId:}`` JSONL entry. - ``list_sessions`` reads the LAST tag from the file tail — most recent - wins. Passing ``None`` appends an empty-string tag entry which - ``list_sessions`` treats as ``None`` (cleared). - - Tags are Unicode-sanitized before storing (removes zero-width chars, - directional marks, private-use characters, etc.) for CLI filter - compatibility. - - Args: - session_id: UUID of the session to tag. - tag: Tag string, or ``None`` to clear. Leading/trailing whitespace - is stripped. Must be non-empty after sanitization and stripping - (unless ``None``). - directory: Project directory path (same semantics as - ``list_sessions(directory=...)``). When omitted, all project - directories are searched for the session file. - - Raises: - ValueError: If ``session_id`` is not a valid UUID, or if ``tag`` is - empty/whitespace-only after sanitization. - FileNotFoundError: If the session file cannot be found. - - See Also: - :func:`tag_session_via_store` for the :class:`SessionStore`-backed - async variant. - - Example: - Tag a session:: - - tag_session( - "550e8400-e29b-41d4-a716-446655440000", - "experiment", - directory="/path/to/project", - ) - - Clear a tag:: - - tag_session(session_id, None) - """ - if not _validate_uuid(session_id): - raise ValueError(f"Invalid session_id: {session_id}") - if tag is not None: - sanitized = _sanitize_unicode(tag).strip() - if not sanitized: - raise ValueError("tag must be non-empty (use None to clear)") - tag = sanitized - - data = ( - json.dumps( - { - "type": "tag", - "tag": tag if tag is not None else "", - "sessionId": session_id, - }, - separators=(",", ":"), - ) - + "\n" - ) - - _append_to_session(session_id, data, directory) - - -def delete_session( - session_id: str, - directory: str | None = None, -) -> None: - """Delete a session by removing its JSONL file and subagent transcripts. - - This is a hard delete — the ``{session_id}.jsonl`` file is removed - permanently, along with the sibling ``{session_id}/`` subdirectory that - holds subagent transcripts (if it exists). SDK users who need soft-delete - semantics can use ``tag_session(id, '__hidden')`` and filter on listing - instead. - - Args: - session_id: UUID of the session to delete. - directory: Project directory path (same semantics as - ``list_sessions(directory=...)``). When omitted, all project - directories are searched for the session file. - - Raises: - ValueError: If ``session_id`` is not a valid UUID. - FileNotFoundError: If the session file cannot be found. - - See Also: - :func:`delete_session_via_store` for the :class:`SessionStore`-backed - async variant. - - Example: - Delete a session:: - - delete_session("550e8400-e29b-41d4-a716-446655440000") - """ - if not _validate_uuid(session_id): - raise ValueError(f"Invalid session_id: {session_id}") - - path = _find_session_file(session_id, directory) - if path is None: - raise FileNotFoundError( - f"Session {session_id} not found" - + (f" in project directory for {directory}" if directory else "") - ) - try: - path.unlink() - except OSError as e: - if e.errno == errno.ENOENT: - raise FileNotFoundError(f"Session {session_id} not found") from e - raise - # Subagent transcripts live in a sibling {session_id}/ dir; often absent. - shutil.rmtree(path.parent / session_id, ignore_errors=True) - - -@dataclass -class ForkSessionResult: - """Result of a fork operation.""" - - session_id: str - """UUID of the new forked session.""" - - -def fork_session( - session_id: str, - directory: str | None = None, - up_to_message_id: str | None = None, - title: str | None = None, -) -> ForkSessionResult: - """Fork a session into a new branch with fresh UUIDs. - - Copies transcript messages from the source session into a new session - file, remapping every message UUID and preserving the ``parentUuid`` - chain. Supports ``up_to_message_id`` for branching from a specific - point in the conversation. - - Forked sessions start without undo history (file-history snapshots are - not copied). - - Args: - session_id: UUID of the source session to fork. - directory: Project directory path (same semantics as - ``list_sessions(directory=...)``). When omitted, all project - directories are searched for the session file. - up_to_message_id: Slice transcript up to this message UUID - (inclusive). If omitted, copies the full transcript. - title: Custom title for the fork. If omitted, derives from - the original title + " (fork)". - - Returns: - ``ForkSessionResult`` with the new session's UUID. - - Raises: - ValueError: If ``session_id`` or ``up_to_message_id`` is not a - valid UUID. - FileNotFoundError: If the source session file cannot be found. - ValueError: If the session has no messages to fork, or if - ``up_to_message_id`` is not found in the transcript. - - See Also: - :func:`fork_session_via_store` for the :class:`SessionStore`-backed - async variant. - - Example: - Fork a session:: - - result = fork_session("550e8400-e29b-41d4-a716-446655440000") - print(result.session_id) - - Fork from a specific point:: - - result = fork_session( - "550e8400-e29b-41d4-a716-446655440000", - up_to_message_id="660e8400-e29b-41d4-a716-446655440001", - ) - """ - if not _validate_uuid(session_id): - raise ValueError(f"Invalid session_id: {session_id}") - if up_to_message_id and not _validate_uuid(up_to_message_id): - raise ValueError(f"Invalid up_to_message_id: {up_to_message_id}") - - source = _find_session_file_with_dir(session_id, directory) - if source is None: - raise FileNotFoundError( - f"Session {session_id} not found" - + (f" in project directory for {directory}" if directory else "") - ) - file_path, project_dir = source - - content = file_path.read_bytes() - if not content: - raise ValueError(f"Session {session_id} has no messages to fork") - - transcript, content_replacements = _parse_fork_transcript(content, session_id) - - def _derive_title() -> str | None: - buf_len = len(content) - head = content[: min(buf_len, LITE_READ_BUF_SIZE)].decode( - "utf-8", errors="replace" - ) - tail = content[max(0, buf_len - LITE_READ_BUF_SIZE) :].decode( - "utf-8", errors="replace" - ) - return ( - _extract_last_json_string_field(tail, "customTitle") - or _extract_last_json_string_field(head, "customTitle") - or _extract_last_json_string_field(tail, "aiTitle") - or _extract_last_json_string_field(head, "aiTitle") - or _extract_first_prompt_from_head(head) - or None - ) - - forked_session_id, lines = _build_fork_lines( - transcript, - content_replacements, - session_id, - up_to_message_id, - title, - _derive_title, - ) - - fork_path = project_dir / f"{forked_session_id}.jsonl" - fd = os.open(fork_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) - try: - os.write(fd, ("\n".join(lines) + "\n").encode("utf-8")) - finally: - os.close(fd) - - return ForkSessionResult(session_id=forked_session_id) - - -def _build_fork_lines( - transcript: list[dict[str, Any]], - content_replacements: list[Any], - session_id: str, - up_to_message_id: str | None, - title: str | None, - derive_title: Callable[[], str | None], -) -> tuple[str, list[str]]: - """Core fork transform — remap UUIDs and produce serialized JSONL lines. - - Shared by the filesystem and SessionStore-backed paths. Returns - ``(forked_session_id, lines)`` where each line is a compact JSON string - without a trailing newline. - - ``derive_title`` is invoked only when no explicit ``title`` is given, - so the disk path's head/tail byte scan and the store path's entry scan - only run when needed. - """ - # Filter out sidechains (subagent sessions with separate parentUuid - # graphs). Keep isMeta entries — they're interleaved in the main chain. - transcript = [e for e in transcript if not e.get("isSidechain")] - - if not transcript: - raise ValueError(f"Session {session_id} has no messages to fork") - - if up_to_message_id: - cutoff = -1 - for i, entry in enumerate(transcript): - if entry.get("uuid") == up_to_message_id: - cutoff = i - break - if cutoff == -1: - raise ValueError( - f"Message {up_to_message_id} not found in session {session_id}" - ) - transcript = transcript[: cutoff + 1] - - # Include progress entries in the mapping — needed for parentUuid chain walk. - uuid_mapping: dict[str, str] = {} - for entry in transcript: - uuid_mapping[entry["uuid"]] = str(uuid_mod.uuid4()) - - # Filter out progress messages from written output. They're UI-only - # chain links; not needed in a fresh fork. - writable = [e for e in transcript if e.get("type") != "progress"] - if not writable: - raise ValueError(f"Session {session_id} has no messages to fork") - - by_uuid: dict[str, dict[str, Any]] = {} - for entry in transcript: - by_uuid[entry["uuid"]] = entry - - forked_session_id = str(uuid_mod.uuid4()) - - now = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") - lines: list[str] = [] - - for i, original in enumerate(writable): - new_uuid = uuid_mapping[original["uuid"]] - - # Resolve parentUuid, skipping progress ancestors. - new_parent_uuid: str | None = None - parent_id: str | None = original.get("parentUuid") - while parent_id: - parent = by_uuid.get(parent_id) - if not parent: - break - if parent.get("type") != "progress": - new_parent_uuid = uuid_mapping.get(parent_id) - break - parent_id = parent.get("parentUuid") - - # Only update timestamp on the last message (leaf detection on resume). - timestamp = now if i == len(writable) - 1 else original.get("timestamp", now) - - # Remap logicalParentUuid (compact-boundary backpointer). - logical_parent = original.get("logicalParentUuid") - new_logical_parent = ( - uuid_mapping.get(logical_parent) if logical_parent else logical_parent - ) - - forked = { - **original, - "uuid": new_uuid, - "parentUuid": new_parent_uuid, - "logicalParentUuid": new_logical_parent, - "sessionId": forked_session_id, - "timestamp": timestamp, - # Clear session-specific fields from the spread - "isSidechain": False, - "forkedFrom": { - "sessionId": session_id, - "messageUuid": original["uuid"], - }, - } - # Remove fields that would leak state from the source session - for key in ("teamName", "agentName", "slug", "sourceToolAssistantUUID"): - forked.pop(key, None) - - lines.append(json.dumps(forked, separators=(",", ":"))) - - # Append content-replacement entry (if any) with the fork's sessionId. - if content_replacements: - lines.append( - json.dumps( - { - "type": "content-replacement", - "sessionId": forked_session_id, - "replacements": content_replacements, - "uuid": str(uuid_mod.uuid4()), - "timestamp": now, - }, - separators=(",", ":"), - ) - ) - - # Derive title: explicit > original customTitle > original aiTitle > first - # prompt. Suffix with " (fork)" for derived titles. listSessions reads the - # LAST custom-title from the tail, so this entry is what surfaces. - fork_title = title.strip() if title else None - if not fork_title: - fork_title = f"{derive_title() or 'Forked session'} (fork)" - - lines.append( - json.dumps( - { - "type": "custom-title", - "sessionId": forked_session_id, - "customTitle": fork_title, - "uuid": str(uuid_mod.uuid4()), - "timestamp": now, - }, - separators=(",", ":"), - ) - ) - - return forked_session_id, lines - - -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- - - -def _find_session_file( - session_id: str, - directory: str | None, -) -> Path | None: - """Find the path to a session's JSONL file. - - Returns the path if found, None otherwise. - """ - result = _find_session_file_with_dir(session_id, directory) - return result[0] if result else None - - -def _find_session_file_with_dir( - session_id: str, - directory: str | None, -) -> tuple[Path, Path] | None: - """Find a session file and its containing project directory. - - Returns ``(file_path, project_dir)`` or None. The fork operation - needs the project dir to write the new file adjacent to the source. - """ - file_name = f"{session_id}.jsonl" - - def _try_dir(project_dir: Path) -> tuple[Path, Path] | None: - path = project_dir / file_name - try: - st = path.stat() - if st.st_size > 0: - return (path, project_dir) - except OSError: - pass - return None - - if directory: - canonical = _canonicalize_path(directory) - project_dir = _find_project_dir(canonical) - if project_dir is not None: - result = _try_dir(project_dir) - if result: - return result - - try: - worktree_paths = _get_worktree_paths(canonical) - except Exception: - worktree_paths = [] - for wt in worktree_paths: - if wt == canonical: - continue - wt_project_dir = _find_project_dir(wt) - if wt_project_dir is not None: - result = _try_dir(wt_project_dir) - if result: - return result - return None - - projects_dir = _get_projects_dir() - try: - dirents = list(projects_dir.iterdir()) - except OSError: - return None - for entry in dirents: - result = _try_dir(entry) - if result: - return result - return None - - -_TRANSCRIPT_TYPES = frozenset({"user", "assistant", "attachment", "system", "progress"}) - - -def _derive_title_from_entries(raw: list[Any]) -> str | None: - """Mirror the disk path's head/tail title scan over parsed entry objects. - - Precedence matches ``_extract_last_json_string_field`` semantics: last - occurrence wins for both ``customTitle`` and ``aiTitle``; ``customTitle`` - beats ``aiTitle``; first user prompt is the final fallback. - """ - custom: str | None = None - ai: str | None = None - for e in raw: - if not isinstance(e, dict): - continue - ct = e.get("customTitle") - if isinstance(ct, str) and ct: - custom = ct - at = e.get("aiTitle") - if isinstance(at, str) and at: - ai = at - if custom: - return custom - if ai: - return ai - # First-prompt fallback — reuse the head extractor over a re-serialized - # JSONL string so skip-patterns/truncation match the disk path exactly. - jsonl = "\n".join(json.dumps(e, separators=(",", ":")) for e in raw) + "\n" - return _extract_first_prompt_from_head(jsonl) or None - - -def _parse_fork_transcript( - content: bytes, session_id: str -) -> tuple[list[dict[str, Any]], list[Any]]: - """Parse JSONL content into transcript entries + content-replacement records. - - Only keeps entries that have a uuid and are transcript message types. - Content-replacement entries are collected for re-emission in the fork. - """ - transcript: list[dict[str, Any]] = [] - content_replacements: list[Any] = [] - - for line in content.decode("utf-8", errors="replace").splitlines(): - line = line.strip() - if not line: - continue - try: - entry = json.loads(line) - except (json.JSONDecodeError, ValueError): - continue - if not isinstance(entry, dict): - continue - entry_type = entry.get("type") - if entry_type in _TRANSCRIPT_TYPES and isinstance(entry.get("uuid"), str): - transcript.append(entry) - elif ( - entry_type == "content-replacement" - and entry.get("sessionId") == session_id - and isinstance(entry.get("replacements"), list) - ): - content_replacements.extend(entry["replacements"]) - - return transcript, content_replacements - - -def _append_to_session( - session_id: str, - data: str, - directory: str | None, -) -> None: - """Append data to an existing session file. - - Searches candidate paths and tries the append directly — no existence - check. Uses O_WRONLY | O_APPEND (without O_CREAT) so the open fails with - ENOENT for missing files, avoiding TOCTOU. - """ - file_name = f"{session_id}.jsonl" - - if directory: - canonical = _canonicalize_path(directory) - - # Try the exact/prefix-matched project directory first. - project_dir = _find_project_dir(canonical) - if project_dir is not None and _try_append(project_dir / file_name, data): - return - - # Worktree fallback — matches list_sessions/get_session_messages. - # Sessions may live under a different worktree root. - try: - worktree_paths = _get_worktree_paths(canonical) - except Exception: - worktree_paths = [] - for wt in worktree_paths: - if wt == canonical: - continue # already tried above - wt_project_dir = _find_project_dir(wt) - if wt_project_dir is not None and _try_append( - wt_project_dir / file_name, data - ): - return - - raise FileNotFoundError( - f"Session {session_id} not found in project directory for {directory}" - ) - - # No directory — search all project directories by trying each directly. - projects_dir = _get_projects_dir() - try: - dirents = list(projects_dir.iterdir()) - except OSError as e: - raise FileNotFoundError( - f"Session {session_id} not found (no projects directory)" - ) from e - for entry in dirents: - if _try_append(entry / file_name, data): - return - raise FileNotFoundError(f"Session {session_id} not found in any project directory") - - -def _try_append(path: Path, data: str) -> bool: - """Try appending to a path. - - Opens with O_WRONLY | O_APPEND (no O_CREAT), so the open fails with - ENOENT if the file does not exist — no separate existence check. - - Returns ``True`` on successful write, ``False`` if the file does not - exist (ENOENT/ENOTDIR) or is 0-byte. A 0-byte ``.jsonl`` is a "session - not here, keep searching" signal that readers (``_read_session_lite``) - already honor; without this guard the search would stop at an empty stub - in one project dir while the real file lives in a worktree. Re-raises all - other errors (ENOSPC, EACCES, EIO, etc.) so real write failures surface. - - O_APPEND semantics: Python's ``os.open`` with ``os.O_APPEND`` maps to the - kernel's append mode on all platforms. On POSIX, O_APPEND makes the kernel - atomically seek-to-EOF on every write (race-free). On Windows, CPython's - ``os.open`` translates O_APPEND to ``FILE_APPEND_DATA`` (also atomic). - CPython handles this correctly on all platforms, so no explicit-position - fallback is needed. - """ - try: - fd = os.open(path, os.O_WRONLY | os.O_APPEND) - except OSError as e: - if e.errno in (errno.ENOENT, errno.ENOTDIR): - return False - raise - try: - stat = os.fstat(fd) - if stat.st_size == 0: - return False - os.write(fd, data.encode("utf-8")) - return True - finally: - os.close(fd) - - -# --------------------------------------------------------------------------- -# Unicode sanitization -# --------------------------------------------------------------------------- - -# Explicit ranges for dangerous Unicode characters. Python's regex supports -# Unicode categories via \p{} only in the third-party `regex` module, so we -# use explicit ranges here (matching the TS fallback paths). -_UNICODE_STRIP_RE = re.compile( - "[" - "\u200b-\u200f" # Zero-width spaces, LTR/RTL marks - "\u202a-\u202e" # Directional formatting characters - "\u2066-\u2069" # Directional isolates - "\ufeff" # Byte order mark - "\ue000-\uf8ff" # Basic Multilingual Plane private use - "]" -) - -# Format characters (Cf category) — the ones most commonly abused for -# injection. We check this per-character since Python's re module doesn't -# support \p{Cf} without the third-party regex module. -_FORMAT_CATEGORIES = frozenset({"Cf", "Co", "Cn"}) - - -def _sanitize_unicode(value: str) -> str: - """Sanitize a string by removing dangerous Unicode characters. - - Iteratively applies NFKC - normalization and strips format/private-use/unassigned characters until - no more changes occur (max 10 iterations). - """ - current = value - for _ in range(10): - previous = current - # Apply NFKC normalization to handle composed character sequences - current = unicodedata.normalize("NFKC", current) - # Strip Cf (format), Co (private use), Cn (unassigned) categories - current = "".join( - c for c in current if unicodedata.category(c) not in _FORMAT_CATEGORIES - ) - # Explicit ranges (redundant with category check but matches TS) - current = _UNICODE_STRIP_RE.sub("", current) - if current == previous: - break - return current - - -# --------------------------------------------------------------------------- -# SessionStore-backed implementations -# --------------------------------------------------------------------------- - - -def _iso_now() -> str: - return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") - - -async def rename_session_via_store( - session_store: SessionStore, - session_id: str, - title: str, - directory: str | None = None, -) -> None: - """Rename a session by appending a custom-title entry to a :class:`SessionStore`. - - Async, store-backed counterpart to :func:`rename_session`. - - Args: - session_store: The store to write to. - session_id: UUID of the session to rename. - title: New session title. Leading/trailing whitespace is stripped. - Must be non-empty after stripping. - directory: Project directory used to compute the ``project_key``. - Defaults to the current working directory. - - Raises: - ValueError: If ``session_id`` is not a valid UUID, or if ``title`` - is empty/whitespace-only. - """ - if not _validate_uuid(session_id): - raise ValueError(f"Invalid session_id: {session_id}") - stripped = title.strip() - if not stripped: - raise ValueError("title must be non-empty") - project_key = project_key_for_directory(directory) - key: SessionKey = {"project_key": project_key, "session_id": session_id} - entry: dict[str, Any] = { - "type": "custom-title", - "customTitle": stripped, - "sessionId": session_id, - "uuid": str(uuid_mod.uuid4()), - "timestamp": _iso_now(), - } - # SessionStoreEntry is a structural supertype ({type: str, ...}); the - # extra fields are opaque pass-through for adapters. - await session_store.append(key, [cast(SessionStoreEntry, entry)]) - - -async def tag_session_via_store( - session_store: SessionStore, - session_id: str, - tag: str | None, - directory: str | None = None, -) -> None: - """Tag a session by appending a tag entry to a :class:`SessionStore`. - - Async, store-backed counterpart to :func:`tag_session`. Pass ``None`` to - clear the tag. Tags are Unicode-sanitized before storing. - - Args: - session_store: The store to write to. - session_id: UUID of the session to tag. - tag: Tag string, or ``None`` to clear. - directory: Project directory used to compute the ``project_key``. - Defaults to the current working directory. - - Raises: - ValueError: If ``session_id`` is not a valid UUID, or if ``tag`` is - empty/whitespace-only after sanitization. - """ - if not _validate_uuid(session_id): - raise ValueError(f"Invalid session_id: {session_id}") - if tag is not None: - sanitized = _sanitize_unicode(tag).strip() - if not sanitized: - raise ValueError("tag must be non-empty (use None to clear)") - tag = sanitized - project_key = project_key_for_directory(directory) - key: SessionKey = {"project_key": project_key, "session_id": session_id} - entry: dict[str, Any] = { - "type": "tag", - "tag": tag if tag is not None else "", - "sessionId": session_id, - "uuid": str(uuid_mod.uuid4()), - "timestamp": _iso_now(), - } - await session_store.append(key, [cast(SessionStoreEntry, entry)]) - - -async def delete_session_via_store( - session_store: SessionStore, - session_id: str, - directory: str | None = None, -) -> None: - """Delete a session from a :class:`SessionStore`. - - Async, store-backed counterpart to :func:`delete_session`. If the store - does not implement :meth:`SessionStore.delete`, deletion is a no-op - (appropriate for WORM/append-only backends — matches the - :class:`SessionStore` contract). - - Whether subagent transcripts under the session are also removed depends - on the store's ``delete({session_id})`` semantics — - :class:`InMemorySessionStore` cascades; custom stores may not. - - Args: - session_store: The store to delete from. - session_id: UUID of the session to delete. - directory: Project directory used to compute the ``project_key``. - Defaults to the current working directory. - - Raises: - ValueError: If ``session_id`` is not a valid UUID. - """ - if not _validate_uuid(session_id): - raise ValueError(f"Invalid session_id: {session_id}") - if not _store_implements(session_store, "delete"): - return - project_key = project_key_for_directory(directory) - key: SessionKey = {"project_key": project_key, "session_id": session_id} - await session_store.delete(key) - - -async def fork_session_via_store( - session_store: SessionStore, - session_id: str, - directory: str | None = None, - up_to_message_id: str | None = None, - title: str | None = None, -) -> ForkSessionResult: - """Fork a session into a new branch with fresh UUIDs via a :class:`SessionStore`. - - Async, store-backed counterpart to :func:`fork_session`. Runs the fork - transform directly over the objects returned by ``session_store.load()`` — - no JSONL round-trip. A storage-layer copy (e.g. S3 CopyObject) is NOT - sufficient: the transform remaps every UUID, rewrites ``sessionId`` on - each entry, and stamps ``forkedFrom``, so the data must pass through - this process once. - - Args: - session_store: The store to read the source from and write the fork - to. - session_id: UUID of the source session to fork. - directory: Project directory used to compute the ``project_key``. - Defaults to the current working directory. - up_to_message_id: Slice transcript up to this message UUID - (inclusive). If omitted, copies the full transcript. - title: Custom title for the fork. If omitted, derives from the - original title + " (fork)". - - Returns: - ``ForkSessionResult`` with the new session's UUID. - - Raises: - ValueError: If ``session_id`` or ``up_to_message_id`` is not a - valid UUID, or if the session has no messages to fork. - FileNotFoundError: If the source session is not found in the store. - """ - if not _validate_uuid(session_id): - raise ValueError(f"Invalid session_id: {session_id}") - if up_to_message_id and not _validate_uuid(up_to_message_id): - raise ValueError(f"Invalid up_to_message_id: {up_to_message_id}") - project_key = project_key_for_directory(directory) - src_key: SessionKey = {"project_key": project_key, "session_id": session_id} - loaded = await session_store.load(src_key) - if not loaded: - raise FileNotFoundError(f"Session {session_id} not found") - - # Partition into transcript entries (with uuid) and content-replacement - # records, mirroring _parse_fork_transcript for the already-parsed path. - # SessionStoreEntry is a minimal structural supertype — widen to a plain - # dict for field access. - raw: list[dict[str, Any]] = cast("list[dict[str, Any]]", loaded) - transcript: list[dict[str, Any]] = [] - content_replacements: list[Any] = [] - for entry in raw: - entry_type = entry.get("type") - if entry_type in _TRANSCRIPT_TYPES and isinstance(entry.get("uuid"), str): - transcript.append(entry) - elif ( - entry_type == "content-replacement" - and entry.get("sessionId") == session_id - and isinstance(entry.get("replacements"), list) - ): - content_replacements.extend(entry["replacements"]) - - forked_session_id, lines = _build_fork_lines( - transcript, - content_replacements, - session_id, - up_to_message_id, - title, - lambda: _derive_title_from_entries(raw), - ) - - dst_key: SessionKey = {"project_key": project_key, "session_id": forked_session_id} - # _build_fork_lines emits compact JSON strings; re-parse to objects so the - # store receives the same shape it would from the mirror path. All entries - # satisfy the SessionStoreEntry structural supertype ({type: str, ...}). - await session_store.append(dst_key, [json.loads(line) for line in lines]) - return ForkSessionResult(session_id=forked_session_id) diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_resume.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_resume.py deleted file mode 100644 index 1bbace38..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_resume.py +++ /dev/null @@ -1,522 +0,0 @@ -"""Materialize a :class:`SessionStore`-backed resume into a temp ``CLAUDE_CONFIG_DIR``. - -When ``options.resume`` (or ``options.continue_conversation``) is paired with -``options.session_store``, the session JSONL almost certainly does not exist on -local disk — it lives in the external store. The CLI subprocess only knows how -to resume from a local file. This module bridges the gap: it loads the session -from the store, writes it to a temporary directory laid out exactly like -``~/.claude/``, and returns the path so the caller can point the subprocess at -it via ``CLAUDE_CONFIG_DIR``. - -Mirrors the behavior of the TypeScript SDK. -""" - -from __future__ import annotations - -import asyncio -import errno -import getpass -import json -import logging -import ntpath -import os -import platform -import re -import shutil -import subprocess -import tempfile -from collections.abc import Awaitable, Callable -from contextlib import suppress -from dataclasses import dataclass, replace -from pathlib import Path -from typing import Any - -from ..types import ClaudeAgentOptions, SessionKey, SessionStore -from .session_store_validation import _store_implements -from .sessions import _get_projects_dir, _validate_uuid, project_key_for_directory -from .transcript_mirror_batcher import TranscriptMirrorBatcher - -logger = logging.getLogger(__name__) - -# Default macOS Keychain service name for OAuth credentials when -# CLAUDE_CONFIG_DIR is unset (production OAUTH_FILE_SUFFIX is empty). -_KEYCHAIN_SERVICE_NAME = "Claude Code-credentials" - - -@dataclass -class MaterializedResume: - """Result of :func:`materialize_resume_session`. - - Attributes: - config_dir: Temporary directory laid out like ``~/.claude/`` — - point the subprocess at it via ``CLAUDE_CONFIG_DIR``. - resume_session_id: Session ID to pass as ``--resume``. When the - input was ``continue_conversation``, this is the most-recent - session resolved via :meth:`SessionStore.list_sessions`. - cleanup: Coroutine that removes ``config_dir`` (best-effort). - Call it after the subprocess exits. - """ - - config_dir: Path - resume_session_id: str - cleanup: Callable[[], Awaitable[None]] - - -def apply_materialized_options( - options: ClaudeAgentOptions, materialized: MaterializedResume -) -> ClaudeAgentOptions: - """Return a copy of ``options`` repointed at a materialized temp config dir. - - Sets ``CLAUDE_CONFIG_DIR`` in ``env``, ``resume`` to the materialized - session id, and clears ``continue_conversation`` (already resolved to a - concrete session id during materialization). - """ - return replace( - options, - env={ - **options.env, - "CLAUDE_CONFIG_DIR": str(materialized.config_dir), - }, - resume=materialized.resume_session_id, - continue_conversation=False, - ) - - -def build_mirror_batcher( - store: SessionStore, - materialized: MaterializedResume | None, - env: dict[str, str] | None, - on_error: Callable[[SessionKey | None, str], Awaitable[None]], -) -> TranscriptMirrorBatcher: - """Construct the :class:`TranscriptMirrorBatcher` for a session. - - Resolves ``projects_dir`` to the materialized temp dir when present - (so file_path → key resolution matches what the subprocess writes), - otherwise to the standard projects directory under the effective - ``CLAUDE_CONFIG_DIR``. - """ - projects_dir = ( - str(materialized.config_dir / "projects") - if materialized is not None - else str(_get_projects_dir(env)) - ) - return TranscriptMirrorBatcher( - store=store, - projects_dir=projects_dir, - on_error=on_error, - ) - - -async def materialize_resume_session( - options: ClaudeAgentOptions, -) -> MaterializedResume | None: - """Load a session from ``options.session_store`` and write it to a temp dir. - - Returns ``None`` when no materialization is needed (no store, no - resume/continue, store has no entries, or the resolved session ID is not a - valid UUID) — caller falls through to the normal (no-store) resume/spawn - path. For ``continue_conversation`` this means a fresh session; for an - explicit ``resume`` value the CLI receives it unchanged. - - Raises ``RuntimeError`` if a store call fails or times out. - """ - store = options.session_store - if store is None: - return None - if options.resume is None and not options.continue_conversation: - return None - - timeout_s = options.load_timeout_ms / 1000 - project_key = project_key_for_directory(options.cwd) - - # Resolve the session ID — explicit resume wins; otherwise pick the - # most-recently-modified non-sidechain session from the store. Empty - # list_sessions() → fresh session (matches CLI --continue with no history). - if options.resume is not None: - # session_id is used as a path component below; reject anything that - # isn't a UUID to prevent traversal and match every other resume path. - if _validate_uuid(options.resume) is None: - return None - resolved = await _load_candidate(store, project_key, options.resume, timeout_s) - else: - resolved = await _resolve_continue_candidate(store, project_key, timeout_s) - if resolved is None: - return None - session_id, entries = resolved - - tmp_base = Path(tempfile.mkdtemp(prefix="claude-resume-")) - try: - project_dir = tmp_base / "projects" / project_key - project_dir.mkdir(parents=True, exist_ok=True) - _write_jsonl(project_dir / f"{session_id}.jsonl", entries) - - # The subprocess will run with CLAUDE_CONFIG_DIR=tmp_base. Copy auth - # config from the caller's effective config locations so it can - # authenticate. Missing files are fine (API-key auth, etc.). - _copy_auth_files(tmp_base, options.env) - - # Materialize subagent transcripts if the store can enumerate them. - if _store_implements(store, "list_subkeys"): - await _materialize_subkeys( - store, tmp_base, project_dir, project_key, session_id, timeout_s - ) - except BaseException: - # Any failure after mkdtemp leaves tmp_base (which may already - # contain a .credentials.json copy) on disk with no path for the - # caller to clean it up. Remove it before rethrowing. BaseException - # so asyncio.CancelledError (BaseException since 3.8) also triggers - # cleanup — callers can't compensate because the assignment raises - # before completing. - await _rmtree_with_retry(tmp_base) - raise - - async def cleanup() -> None: - await _rmtree_with_retry(tmp_base) - - return MaterializedResume( - config_dir=tmp_base, - resume_session_id=session_id, - cleanup=cleanup, - ) - - -# --------------------------------------------------------------------------- -# Helpers -# --------------------------------------------------------------------------- - -# OSError errnos that indicate a transiently-held handle (Windows AV/indexer -# scanning a freshly-written file) rather than a permanent failure. -_RETRYABLE_RMTREE_ERRNOS = frozenset( - { - errno.EBUSY, - errno.EMFILE, - errno.ENFILE, - errno.ENOTEMPTY, - errno.EPERM, - errno.EACCES, - } -) - - -async def _rmtree_with_retry( - path: Path, *, retries: int = 4, delay: float = 0.1 -) -> None: - """Best-effort ``shutil.rmtree`` with retries on transient lock errors. - - On Windows, AV/indexer can briefly hold a handle on freshly-written - files (notably ``.credentials.json``), causing rmtree to fail with - EBUSY/EPERM. Retry a few times with a short backoff; after exhausting - retries, fall back to ``ignore_errors=True`` (matches the previous - behavior, but gives the handle a chance to release first so the access - token doesn't leak in temp). Never raises. - """ - if not path.exists(): - return - for _ in range(retries): - try: - shutil.rmtree(path) - return - except OSError as e: - if e.errno not in _RETRYABLE_RMTREE_ERRNOS and not isinstance( - e, PermissionError - ): - break - try: - await asyncio.sleep(delay) - except asyncio.CancelledError: - # Best-effort final sweep before propagating cancellation so a - # cancelled connect() doesn't leak the temp dir. - shutil.rmtree(path, ignore_errors=True) - raise - shutil.rmtree(path, ignore_errors=True) - - -async def _load_candidate( - store: SessionStore, project_key: str, session_id: str, timeout_s: float -) -> tuple[str, list[Any]] | None: - """Load entries for ``session_id``; return ``None`` if empty/missing.""" - entries = await _with_timeout( - store.load({"project_key": project_key, "session_id": session_id}), - timeout_s, - f"SessionStore.load() for session {session_id}", - ) - if not entries: - return None - return session_id, entries - - -async def _resolve_continue_candidate( - store: SessionStore, project_key: str, timeout_s: float -) -> tuple[str, list[Any]] | None: - """Pick the most-recently-modified non-sidechain session. - - Sidechain transcripts are mirrored as ordinary top-level keys and often - have the highest mtime (their append lands after the main session's in - the same flush). Walk newest→oldest, loading each candidate (the load is - needed anyway) and skipping sidechains so ``--continue`` resumes the - user's conversation, not a subagent's. Matches the CLI's own - ``--continue`` filter and ``list_sessions_from_store()``. - """ - sessions = await _with_timeout( - store.list_sessions(project_key), - timeout_s, - "SessionStore.list_sessions()", - ) - if not sessions: - return None - for cand in sorted(sessions, key=lambda s: s["mtime"], reverse=True): - sid = cand["session_id"] - if _validate_uuid(sid) is None: - continue - loaded = await _load_candidate(store, project_key, sid, timeout_s) - if loaded is None: - continue - first = loaded[1][0] - if isinstance(first, dict) and first.get("isSidechain") is True: - continue - return loaded - return None - - -async def _with_timeout(coro: Awaitable[Any], timeout_s: float, what: str) -> Any: - """Await ``coro`` with a timeout, re-raising as ``RuntimeError`` with context.""" - try: - return await asyncio.wait_for(coro, timeout=timeout_s) - except asyncio.TimeoutError as e: - raise RuntimeError( - f"{what} timed out after {int(timeout_s * 1000)}ms during resume " - f"materialization" - ) from e - except Exception as e: # noqa: BLE001 - surface adapter failures with context - raise RuntimeError(f"{what} failed during resume materialization: {e}") from e - - -def _write_jsonl(path: Path, entries: list[Any]) -> None: - """Stream-write ``entries`` as one JSON line each to ``path`` (mode 0o600).""" - path.parent.mkdir(parents=True, exist_ok=True) - with path.open("w", encoding="utf-8") as f: - for e in entries: - f.write(json.dumps(e, separators=(",", ":"))) - f.write("\n") - with suppress(OSError): - path.chmod(0o600) - - -def _copy_auth_files(tmp_base: Path, opt_env: dict[str, str]) -> None: - """Copy ``.credentials.json`` (refreshToken redacted) and ``.claude.json``. - - Source resolution mirrors the CLI: - - ``.credentials.json`` lives under the config dir (default ``~/.claude/``) - - ``.claude.json`` lives at ``$CLAUDE_CONFIG_DIR/.claude.json`` when set, - else ``~/.claude.json`` (NOT ``~/.claude/.claude.json``) - """ - caller_config_dir = opt_env.get("CLAUDE_CONFIG_DIR") or os.environ.get( - "CLAUDE_CONFIG_DIR" - ) - source_config_dir = ( - Path(caller_config_dir) if caller_config_dir else Path.home() / ".claude" - ) - - creds_json: str | None = None - creds_path = source_config_dir / ".credentials.json" - with suppress(FileNotFoundError): - creds_json = creds_path.read_text(encoding="utf-8") - - # macOS default setup keeps OAuth tokens in the Keychain, not a file. - # Redirecting CLAUDE_CONFIG_DIR changes the Keychain service-name suffix, - # so the subprocess's lookup misses and falls back to plainTextStorage at - # ${tmp_base}/.credentials.json. Populate that file from the parent's - # Keychain so the resumed subprocess can auth. Skipped when env-based - # auth or a custom config dir is already in play. - if ( - caller_config_dir is None - and not ( - opt_env.get("ANTHROPIC_API_KEY") or os.environ.get("ANTHROPIC_API_KEY") - ) - and not ( - opt_env.get("CLAUDE_CODE_OAUTH_TOKEN") - or os.environ.get("CLAUDE_CODE_OAUTH_TOKEN") - ) - ): - keychain = _read_keychain_credentials() - if keychain is not None: - creds_json = keychain - - _write_redacted_credentials(creds_json, tmp_base / ".credentials.json") - - claude_json_src = ( - Path(caller_config_dir) / ".claude.json" - if caller_config_dir - else Path.home() / ".claude.json" - ) - _copy_if_present(claude_json_src, tmp_base / ".claude.json") - - -def _write_redacted_credentials(creds_json: str | None, dst: Path) -> None: - """Write ``creds_json`` with ``claudeAiOauth.refreshToken`` removed. - - The resumed subprocess runs under a redirected ``CLAUDE_CONFIG_DIR``; if it - refreshed, the single-use refresh token would be consumed server-side and - the new tokens written to a location the parent never reads back — leaving - the parent's stored creds revoked. With no ``refreshToken``, the - subprocess's refresh check short-circuits. - """ - if creds_json is None: - return - out = creds_json - try: - data = json.loads(creds_json) - oauth = data.get("claudeAiOauth") if isinstance(data, dict) else None - if isinstance(oauth, dict) and "refreshToken" in oauth: - del oauth["refreshToken"] - out = json.dumps(data) - except (json.JSONDecodeError, ValueError): - # Unparseable — write through; subprocess will fail to parse it too. - pass - dst.write_text(out, encoding="utf-8") - with suppress(OSError): - dst.chmod(0o600) - - -def _copy_if_present(src: Path, dst: Path) -> None: - with suppress(FileNotFoundError): - shutil.copyfile(src, dst) - - -def _read_keychain_credentials() -> str | None: - """Read OAuth credentials JSON from the macOS Keychain (default service name). - - Best-effort — returns ``None`` on any error or non-macOS platforms. - """ - # platform.system() (not sys.platform) so mypy doesn't narrow the rest - # of the function to unreachable on the typecheck host. - if platform.system() != "Darwin": - return None - try: - user = os.environ.get("USER") or getpass.getuser() - except Exception: - user = "claude-code-user" - try: - result = subprocess.run( - [ - "security", - "find-generic-password", - "-a", - user, - "-w", - "-s", - _KEYCHAIN_SERVICE_NAME, - ], - capture_output=True, - text=True, - timeout=5, - check=False, - ) - except Exception: - return None - if result.returncode != 0: - return None - out = result.stdout.strip() - return out or None - - -async def _materialize_subkeys( - store: SessionStore, - tmp_base: Path, - project_dir: Path, - project_key: str, - session_id: str, - timeout_s: float, -) -> None: - """Load and write all subagent transcripts/metadata under ``session_id``.""" - session_dir = project_dir / session_id - subkeys = await _with_timeout( - store.list_subkeys({"project_key": project_key, "session_id": session_id}), - timeout_s, - f"SessionStore.list_subkeys() for session {session_id}", - ) - for subpath in subkeys: - # Subpaths come from an external store and are used as filesystem path - # components below. Reject anything that would escape the session - # directory. Empty string is rejected explicitly: '' + '.jsonl' → - # '.jsonl', a hidden dotfile that passes a naive prefix check. - if not _is_safe_subpath(subpath, session_dir): - logger.warning( - "[SessionStore] skipping unsafe subpath from list_subkeys: %r", subpath - ) - continue - - sub_key: SessionKey = { - "project_key": project_key, - "session_id": session_id, - "subpath": subpath, - } - sub_entries = await _with_timeout( - store.load(sub_key), - timeout_s, - f"SessionStore.load() for session {session_id} subpath {subpath}", - ) - if not sub_entries: - continue - - # Partition: agent_metadata entries describe the .meta.json sidecar; - # everything else is a transcript line. - metadata: list[dict[str, Any]] = [] - transcript: list[Any] = [] - for e in sub_entries: - if isinstance(e, dict) and e.get("type") == "agent_metadata": - metadata.append(e) - else: - transcript.append(e) - - sub_file = (session_dir / subpath).with_name( - (session_dir / subpath).name + ".jsonl" - ) - if transcript: - _write_jsonl(sub_file, transcript) - - if metadata: - # Last metadata entry wins; strip the synthetic ``type`` field. - meta_content = {k: v for k, v in metadata[-1].items() if k != "type"} - meta_file = sub_file.with_name( - sub_file.name[: -len(".jsonl")] + ".meta.json" - ) - meta_file.parent.mkdir(parents=True, exist_ok=True) - meta_file.write_text(json.dumps(meta_content), encoding="utf-8") - with suppress(OSError): - meta_file.chmod(0o600) - - -def _is_safe_subpath(subpath: str, session_dir: Path) -> bool: - """Reject subpaths that are empty, absolute, contain ``..``, or escape - ``session_dir`` after resolution.""" - if not subpath: - return False - # PurePosixPath/PureWindowsPath both checked — subpaths are store keys - # that may use either separator regardless of host OS. - if Path(subpath).is_absolute() or subpath.startswith(("/", "\\")): - return False - # Drive-prefixed (``C:foo``) and UNC subpaths are never legitimate store - # keys. ``ntpath.splitdrive`` is used regardless of host OS so a Windows - # consumer is protected even if the store was populated elsewhere; on - # POSIX this also rejects ``C:foo``, which is acceptable since the only - # subpaths we ever emit are ``subagents/...``. - if ntpath.splitdrive(subpath)[0]: - return False - if any(p in (".", "..") for p in re.split(r"[\\/]", subpath)): - return False - if "\x00" in subpath: - return False - # Resolve the .jsonl target — using the same expression as the writer in - # _materialize_subkeys so the validated path can't drift from the written - # one — and confirm it stays under session_dir. Both ``.resolve()`` calls - # can raise (e.g. ValueError on embedded NUL, OSError on broken symlink - # chains); treat any resolution failure as unsafe so the subpath is - # skipped with a warning rather than aborting the whole resume. - target = session_dir / subpath - try: - sub_file = target.with_name(target.name + ".jsonl").resolve() - sub_file.relative_to(session_dir.resolve()) - except (ValueError, OSError): - return False - return True diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_store.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_store.py deleted file mode 100644 index bb6a2155..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_store.py +++ /dev/null @@ -1,194 +0,0 @@ -"""In-memory reference implementation of :class:`SessionStore`.""" - -from __future__ import annotations - -import os -import time -from pathlib import Path - -from ..types import ( - SessionKey, - SessionListSubkeysKey, - SessionStore, - SessionStoreEntry, - SessionStoreListEntry, - SessionSummaryEntry, -) -from .session_summary import fold_session_summary -from .sessions import project_key_for_directory - -__all__ = [ - "InMemorySessionStore", - "file_path_to_session_key", - "project_key_for_directory", -] - - -def _key_to_string(key: SessionKey) -> str: - parts = [key["project_key"], key["session_id"]] - subpath = key.get("subpath") - if subpath: - parts.append(subpath) - return "/".join(parts) - - -class InMemorySessionStore(SessionStore): - """In-memory :class:`SessionStore` implementation for testing and development. - - Stores entries in a ``dict`` keyed by a composite ``project_key/session_id`` - string (with an optional ``/subpath`` suffix). Not suitable for production — - data is lost when the process exits. - """ - - def __init__(self) -> None: - self._store: dict[str, list[SessionStoreEntry]] = {} - self._mtimes: dict[str, int] = {} - self._summaries: dict[tuple[str, str], SessionSummaryEntry] = {} - self._last_mtime = 0 - - def _next_mtime(self) -> int: - """Storage write time for this adapter, in Unix epoch ms. - - Guaranteed strictly monotonically increasing across calls within the - process so back-to-back appends always produce distinct mtimes (real - storage backends — file mtime on modern filesystems, S3 - LastModified, Postgres updated_at — get this property for free from - their commit ordering). - """ - now_ms = int(time.time() * 1000) - if now_ms <= self._last_mtime: - now_ms = self._last_mtime + 1 - self._last_mtime = now_ms - return now_ms - - async def append(self, key: SessionKey, entries: list[SessionStoreEntry]) -> None: - k = _key_to_string(key) - self._store.setdefault(k, []).extend(entries) - now_ms = self._next_mtime() - # Maintain the per-session summary sidecar incrementally so - # list_session_summaries() never re-reads. Subagent subpaths don't - # contribute to the main session's summary. - if key.get("subpath") is None: - sk = (key["project_key"], key["session_id"]) - folded = fold_session_summary(self._summaries.get(sk), key, entries) - # Stamp the sidecar with this adapter's storage write time — the - # SAME clock list_sessions() exposes below. SessionSummaryEntry. - # mtime is contractually storage write time (not entry time), so - # the fast-path staleness check (summary.mtime < list_sessions - # mtime) works correctly. - folded["mtime"] = now_ms - self._summaries[sk] = folded - self._mtimes[k] = now_ms - - async def load(self, key: SessionKey) -> list[SessionStoreEntry] | None: - entries = self._store.get(_key_to_string(key)) - return None if entries is None else list(entries) - - async def list_sessions(self, project_key: str) -> list[SessionStoreListEntry]: - results: list[SessionStoreListEntry] = [] - prefix = project_key + "/" - for k in self._store: - if k.startswith(prefix): - rest = k[len(prefix) :] - # Only include main transcripts (no subpath, so no second '/') - if "/" not in rest: - results.append( - {"session_id": rest, "mtime": self._mtimes.get(k, 0)} - ) - return results - - async def list_session_summaries( - self, project_key: str - ) -> list[SessionSummaryEntry]: - return [s for (pk, _), s in self._summaries.items() if pk == project_key] - - async def delete(self, key: SessionKey) -> None: - k = _key_to_string(key) - self._store.pop(k, None) - self._mtimes.pop(k, None) - # Deleting the main transcript cascades to its subkeys (subagent - # transcripts, metadata) so they aren't orphaned. A targeted delete - # with an explicit subpath removes only that one entry. - if key.get("subpath") is None: - self._summaries.pop((key["project_key"], key["session_id"]), None) - prefix = f"{key['project_key']}/{key['session_id']}/" - for store_key in [sk for sk in self._store if sk.startswith(prefix)]: - self._store.pop(store_key, None) - self._mtimes.pop(store_key, None) - - async def list_subkeys(self, key: SessionListSubkeysKey) -> list[str]: - prefix = f"{key['project_key']}/{key['session_id']}/" - return [k[len(prefix) :] for k in self._store if k.startswith(prefix)] - - # ------------------------------------------------------------------ - # Test helpers - # ------------------------------------------------------------------ - - def get_entries(self, key: SessionKey) -> list[SessionStoreEntry]: - """Test helper — get all entries for a key (empty list if absent).""" - return list(self._store.get(_key_to_string(key), [])) - - @property - def size(self) -> int: - """Test helper — number of stored sessions (main transcripts only).""" - count = 0 - for k in self._store: - first_slash = k.find("/") - if first_slash != -1 and "/" not in k[first_slash + 1 :]: - count += 1 - return count - - def clear(self) -> None: - """Test helper — clear all stored data.""" - self._store.clear() - self._mtimes.clear() - self._summaries.clear() - self._last_mtime = 0 - - -def file_path_to_session_key(file_path: str, projects_dir: str) -> SessionKey | None: - """Derive a :class:`SessionKey` from an absolute transcript file path. - - Main transcripts: ``//.jsonl`` - Subagent transcripts: ``///subagents/agent-.jsonl`` - - Returns ``None`` if ``file_path`` is not under ``projects_dir`` or has an - unrecognized shape. - """ - try: - rel = os.path.relpath(file_path, projects_dir) - except ValueError: - # Windows: relpath raises when the paths are on different drives. - # Treat as "not under projects_dir" so the batcher drops the frame - # with a warning instead of letting the exception escape _drain(). - return None - rel_path = Path(rel) - parts = list(rel_path.parts) - if not parts or parts[0] == ".." or rel_path.is_absolute(): - return None - - if len(parts) < 2: - return None - - project_key = parts[0] - second = parts[1] - - # Main transcript: /.jsonl - if len(parts) == 2 and second.endswith(".jsonl"): - return {"project_key": project_key, "session_id": second[: -len(".jsonl")]} - - # Subagent transcript: //subagents/.../agent-.jsonl - if len(parts) >= 4: - subpath_parts = parts[2:] - last = subpath_parts[-1] - if last.endswith(".jsonl"): - subpath_parts[-1] = last[: -len(".jsonl")] - # Subpaths are always /-joined regardless of os.sep so keys are - # portable across platforms. - return { - "project_key": project_key, - "session_id": second, - "subpath": "/".join(subpath_parts), - } - - return None diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_store_validation.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_store_validation.py deleted file mode 100644 index 16addd21..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_store_validation.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Pre-flight validation for ``ClaudeAgentOptions.session_store`` combinations.""" - -from __future__ import annotations - -from ..types import ClaudeAgentOptions, SessionStore - - -def _store_implements(store: SessionStore, method: str) -> bool: - """True if ``store`` overrides ``method`` rather than inheriting the - Protocol default that raises :class:`NotImplementedError`.""" - impl = getattr(store, method, None) - if impl is None: - return False - default = getattr(SessionStore, method, None) - return getattr(type(store), method, None) is not default - - -def validate_session_store_options(options: ClaudeAgentOptions) -> None: - """Raise :class:`ValueError` for invalid ``session_store`` option combinations. - - Called before subprocess spawn so misconfiguration fails fast instead of - surfacing as a confusing runtime error mid-session. - """ - store = options.session_store - if store is None: - return - - if ( - options.continue_conversation - and options.resume is None - and not _store_implements(store, "list_sessions") - ): - # When resume is explicitly set, list_sessions() is provably never - # called (resume wins over continue), so a minimal store is fine. - raise ValueError( - "continue_conversation with session_store requires the store to " - "implement list_sessions()" - ) - - if options.enable_file_checkpointing: - raise ValueError( - "session_store cannot be combined with enable_file_checkpointing " - "(checkpoints are local-disk only and would diverge from the " - "mirrored transcript)" - ) diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_summary.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_summary.py deleted file mode 100644 index 3a509572..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/session_summary.py +++ /dev/null @@ -1,233 +0,0 @@ -"""Incremental session-summary derivation for :class:`SessionStore` adapters. - -:func:`fold_session_summary` lets a store maintain a per-session -:class:`SessionSummaryEntry` sidecar incrementally inside ``append()`` so -``list_sessions_from_store()`` can fetch all metadata in a single -``list_session_summaries()`` call instead of N per-session ``load()`` calls. - -Every derived field is append-incremental (set-once or last-wins) so adapters -never need to re-read previously appended entries. -""" - -from __future__ import annotations - -from datetime import datetime -from typing import Any, cast - -from ..types import ( - SDKSessionInfo, - SessionKey, - SessionStoreEntry, - SessionSummaryEntry, -) -from .sessions import _COMMAND_NAME_RE, _SKIP_FIRST_PROMPT_PATTERN - -__all__ = ["fold_session_summary", "summary_entry_to_sdk_info"] - - -# Map of JSONL entry keys → SessionSummaryEntry keys for last-wins string -# fields. Each appended entry overwrites the previous value when present. -_LAST_WINS_FIELDS: dict[str, str] = { - "customTitle": "custom_title", - "aiTitle": "ai_title", - "lastPrompt": "last_prompt", - "summary": "summary_hint", - "gitBranch": "git_branch", -} - - -def _iso_to_epoch_ms(ts: Any) -> int | None: - """Parse an ISO-8601 timestamp string to Unix epoch milliseconds.""" - if not isinstance(ts, str): - return None - try: - # Python 3.10's fromisoformat doesn't support trailing 'Z' - norm = ts.replace("Z", "+00:00") if ts.endswith("Z") else ts - return int(datetime.fromisoformat(norm).timestamp() * 1000) - except ValueError: - return None - - -def _entry_text_blocks(entry: dict[str, Any]) -> list[str]: - """Extract text strings from a ``type=="user"`` entry's message content.""" - message = entry.get("message") - if not isinstance(message, dict): - return [] - content = message.get("content") - texts: list[str] = [] - if isinstance(content, str): - texts.append(content) - elif isinstance(content, list): - for block in content: - if ( - isinstance(block, dict) - and block.get("type") == "text" - and isinstance(block.get("text"), str) - ): - texts.append(block["text"]) - return texts - - -def _fold_first_prompt(data: dict[str, Any], entry: dict[str, Any]) -> None: - """Replicate ``_extract_first_prompt_from_head`` for a single parsed entry. - - Mutates ``data`` in place: sets ``first_prompt`` + ``first_prompt_locked`` - on a real match, or stashes a ``command_fallback`` for slash-command - messages. Skips tool_result, isMeta, isCompactSummary, and auto-generated - patterns. - """ - if data.get("first_prompt_locked"): - return - if entry.get("type") != "user": - return - if entry.get("isMeta") is True or entry.get("isCompactSummary") is True: - return - # Skip tool_result-carrying user messages. - message = entry.get("message") - if isinstance(message, dict): - content = message.get("content") - if isinstance(content, list) and any( - isinstance(b, dict) and b.get("type") == "tool_result" for b in content - ): - return - - for raw in _entry_text_blocks(entry): - result = raw.replace("\n", " ").strip() - if not result: - continue - cmd_match = _COMMAND_NAME_RE.search(result) - if cmd_match: - if not data.get("command_fallback"): - data["command_fallback"] = cmd_match.group(1) - continue - if _SKIP_FIRST_PROMPT_PATTERN.match(result): - continue - if len(result) > 200: - result = result[:200].rstrip() + "\u2026" - data["first_prompt"] = result - data["first_prompt_locked"] = True - return - - -def fold_session_summary( - prev: SessionSummaryEntry | None, - key: SessionKey, - entries: list[SessionStoreEntry], -) -> SessionSummaryEntry: - """Fold a batch of appended entries into the running summary for ``key``. - - Stores call this from inside ``append()`` to keep a - :class:`SessionSummaryEntry` sidecar up to date without re-reading the - transcript. ``prev`` is the previous summary for the same key (or ``None`` - for the first append). - - Do not call this for keys with a ``subpath`` — subagent transcripts must - not contribute to the main session's summary. Guard with - ``if key.get("subpath") is None:`` before calling. - - All derived state lives in the opaque ``data`` dict; stores persist it - verbatim and do not interpret it. - - ``mtime`` is NOT touched by the fold — it is the sidecar's storage - write time and must be stamped by the adapter after persisting. It has - to share a clock with the ``mtime`` returned by - :meth:`SessionStore.list_sessions` for the same session (typically file - mtime, S3 ``LastModified``, Postgres ``updated_at``, or whatever native - timestamp the adapter surfaces); deriving it from entry ISO timestamps - would make every batched-write sidecar appear strictly older than the - session's current mtime, defeating the fast-path staleness check. For a - new session (``prev is None``) the fold returns ``mtime=0`` as a - placeholder; the adapter is expected to overwrite it. - - ``created_at`` latches the first parseable entry timestamp; the disk - lite-parse only inspects the first line, so for streams whose first - entry lacks a timestamp (does not occur in CLI-produced transcripts) - the fold path yields a non-``None`` ``created_at`` where lite-parse - yields ``None``. - """ - if prev is not None: - summary: SessionSummaryEntry = { - "session_id": prev["session_id"], - "mtime": prev["mtime"], - "data": dict(prev["data"]), - } - else: - summary = {"session_id": key["session_id"], "mtime": 0, "data": {}} - data = summary["data"] - - for raw in entries: - # SessionStoreEntry is a permissive TypedDict; widen to a plain dict - # so .get() of unknown keys type-checks. - entry = cast("dict[str, Any]", raw) - - ms = _iso_to_epoch_ms(entry.get("timestamp")) - - if "is_sidechain" not in data: - data["is_sidechain"] = entry.get("isSidechain") is True - if "created_at" not in data and ms is not None: - data["created_at"] = ms - - if "cwd" not in data: - cwd = entry.get("cwd") - if isinstance(cwd, str) and cwd: - data["cwd"] = cwd - - _fold_first_prompt(data, entry) - - for src, dst in _LAST_WINS_FIELDS.items(): - val = entry.get(src) - if isinstance(val, str): - data[dst] = val - - if entry.get("type") == "tag": - tag_val = entry.get("tag") - if isinstance(tag_val, str) and tag_val: - data["tag"] = tag_val - else: - # Empty string or absent tag clears the tag. - data.pop("tag", None) - - return summary - - -def summary_entry_to_sdk_info( - entry: SessionSummaryEntry, project_path: str | None -) -> SDKSessionInfo | None: - """Convert a :class:`SessionSummaryEntry` to :class:`SDKSessionInfo`. - - Returns ``None`` for sidechain sessions or sessions with no extractable - summary, matching ``_parse_session_info_from_lite``'s filtering. - """ - data = entry["data"] - if data.get("is_sidechain"): - return None - - first_prompt = ( - data.get("first_prompt") - if data.get("first_prompt_locked") - else data.get("command_fallback") - ) or None - custom_title = data.get("custom_title") or data.get("ai_title") or None - summary = ( - custom_title - or data.get("last_prompt") - or data.get("summary_hint") - or first_prompt - ) - if not summary: - return None - - return SDKSessionInfo( - session_id=entry["session_id"], - summary=summary, - last_modified=entry["mtime"], - # file_size is a JSONL byte count — meaningful only for the local-disk - # path (see SDKSessionInfo.file_size). Stores have no equivalent. - file_size=None, - custom_title=custom_title, - first_prompt=first_prompt, - git_branch=data.get("git_branch") or None, - cwd=data.get("cwd") or project_path or None, - tag=data.get("tag") or None, - created_at=data.get("created_at"), - ) diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/sessions.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/sessions.py deleted file mode 100644 index e495d476..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/sessions.py +++ /dev/null @@ -1,1914 +0,0 @@ -"""Session listing implementation. - -Scans ~/.claude/projects// for .jsonl session files and -extracts metadata from stat + head/tail reads without full JSONL parsing. -""" - -from __future__ import annotations - -import asyncio -import json -import logging -import os -import re -import subprocess -import sys -import time -import unicodedata -from datetime import datetime -from pathlib import Path -from typing import Any - -from ..types import SDKSessionInfo, SessionKey, SessionMessage, SessionStore -from .session_store_validation import _store_implements - -logger = logging.getLogger(__name__) - -# --------------------------------------------------------------------------- -# Constants -# --------------------------------------------------------------------------- - -# Size of the head/tail buffer for lite metadata reads. -LITE_READ_BUF_SIZE = 65536 - -# Upper bound on concurrent ``store.load()`` calls issued by -# ``list_sessions_from_store``. Keeps large project listings from exhausting -# adapter connection pools or tripping backend rate limits. -_STORE_LIST_LOAD_CONCURRENCY = 16 - -# Maximum length for a single filesystem path component. Most filesystems -# limit individual components to 255 bytes. We use 200 to leave room for -# the hash suffix and separator. -MAX_SANITIZED_LENGTH = 200 - -_UUID_RE = re.compile( - r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", - re.IGNORECASE, -) - -# Pattern matching auto-generated or system messages that should be skipped -# when looking for the first meaningful user prompt. -_SKIP_FIRST_PROMPT_PATTERN = re.compile( - r"^(?:||||" - r"\[Request interrupted by user[^\]]*\]|" - r"\s*[\s\S]*\s*$|" - r"\s*[\s\S]*\s*$)" -) - -_COMMAND_NAME_RE = re.compile(r"(.*?)") - -_SANITIZE_RE = re.compile(r"[^a-zA-Z0-9]") - - -# --------------------------------------------------------------------------- -# UUID validation -# --------------------------------------------------------------------------- - - -def _validate_uuid(maybe_uuid: str) -> str | None: - """Returns the string if it is a valid UUID, else None.""" - if _UUID_RE.match(maybe_uuid): - return maybe_uuid - return None - - -# --------------------------------------------------------------------------- -# Path sanitization -# --------------------------------------------------------------------------- - - -def _simple_hash(s: str) -> str: - """32-bit integer hash to base36, matching the CLI's directory naming.""" - h = 0 - for ch in s: - char = ord(ch) - h = (h << 5) - h + char - # Emulate JS `hash |= 0` (coerce to 32-bit signed int) - h = h & 0xFFFFFFFF - if h >= 0x80000000: - h -= 0x100000000 - h = abs(h) - # JS toString(36) - if h == 0: - return "0" - digits = "0123456789abcdefghijklmnopqrstuvwxyz" - out = [] - n = h - while n > 0: - out.append(digits[n % 36]) - n //= 36 - return "".join(reversed(out)) - - -def _sanitize_path(name: str) -> str: - """Makes a string safe for use as a directory name. - - Replaces all non-alphanumeric characters with hyphens. For paths - exceeding MAX_SANITIZED_LENGTH, truncates and appends a hash suffix. - """ - sanitized = _SANITIZE_RE.sub("-", name) - if len(sanitized) <= MAX_SANITIZED_LENGTH: - return sanitized - h = _simple_hash(name) - return f"{sanitized[:MAX_SANITIZED_LENGTH]}-{h}" - - -# --------------------------------------------------------------------------- -# Config directories -# --------------------------------------------------------------------------- - - -def _get_claude_config_home_dir() -> Path: - """Returns the Claude config directory (respects CLAUDE_CONFIG_DIR).""" - config_dir = os.environ.get("CLAUDE_CONFIG_DIR") - if config_dir: - return Path(unicodedata.normalize("NFC", config_dir)) - return Path(unicodedata.normalize("NFC", str(Path.home() / ".claude"))) - - -def _get_projects_dir(env_override: dict[str, str] | None = None) -> Path: - """Returns the projects directory. - - ``env_override`` is consulted before ``os.environ`` so callers that pass - ``CLAUDE_CONFIG_DIR`` to the subprocess via ``options.env`` resolve the - same directory the subprocess will write to. - """ - if env_override: - override = env_override.get("CLAUDE_CONFIG_DIR") - if override: - return Path(unicodedata.normalize("NFC", override)) / "projects" - return _get_claude_config_home_dir() / "projects" - - -def _get_project_dir(project_path: str) -> Path: - return _get_projects_dir() / _sanitize_path(project_path) - - -def _canonicalize_path(d: str) -> str: - """Resolves a directory path to its canonical form using realpath + NFC.""" - try: - resolved = os.path.realpath(d) - return unicodedata.normalize("NFC", resolved) - except OSError: - return unicodedata.normalize("NFC", d) - - -def _find_project_dir(project_path: str) -> Path | None: - """Finds the project directory for a given path. - - Tolerates hash mismatches for long paths (>200 chars). The CLI uses - Bun.hash while the SDK under Node.js uses simpleHash — for paths that - exceed MAX_SANITIZED_LENGTH, these produce different directory suffixes. - This function falls back to prefix-based scanning when the exact match - doesn't exist. - """ - exact = _get_project_dir(project_path) - if exact.is_dir(): - return exact - - # Exact match failed — for short paths this means no sessions exist. - # For long paths, try prefix matching to handle hash mismatches. - sanitized = _sanitize_path(project_path) - if len(sanitized) <= MAX_SANITIZED_LENGTH: - return None - - prefix = sanitized[:MAX_SANITIZED_LENGTH] - projects_dir = _get_projects_dir() - try: - for entry in projects_dir.iterdir(): - if entry.is_dir() and entry.name.startswith(prefix + "-"): - return entry - except OSError: - pass - return None - - -# --------------------------------------------------------------------------- -# JSON string field extraction — no full parse, works on truncated lines -# --------------------------------------------------------------------------- - - -def _unescape_json_string(raw: str) -> str: - """Unescape a JSON string value extracted as raw text.""" - if "\\" not in raw: - return raw - try: - result = json.loads(f'"{raw}"') - if isinstance(result, str): - return result - return raw - except (json.JSONDecodeError, ValueError): - return raw - - -def _extract_json_string_field(text: str, key: str) -> str | None: - """Extracts a simple JSON string field value without full parsing. - - Looks for "key":"value" or "key": "value" patterns. Returns the first - match, or None if not found. - """ - patterns = [f'"{key}":"', f'"{key}": "'] - for pattern in patterns: - idx = text.find(pattern) - if idx < 0: - continue - - value_start = idx + len(pattern) - i = value_start - while i < len(text): - if text[i] == "\\": - i += 2 - continue - if text[i] == '"': - return _unescape_json_string(text[value_start:i]) - i += 1 - return None - - -def _extract_last_json_string_field(text: str, key: str) -> str | None: - """Like _extract_json_string_field but finds the LAST occurrence.""" - patterns = [f'"{key}":"', f'"{key}": "'] - last_value: str | None = None - for pattern in patterns: - search_from = 0 - while True: - idx = text.find(pattern, search_from) - if idx < 0: - break - - value_start = idx + len(pattern) - i = value_start - while i < len(text): - if text[i] == "\\": - i += 2 - continue - if text[i] == '"': - last_value = _unescape_json_string(text[value_start:i]) - break - i += 1 - search_from = i + 1 - return last_value - - -# --------------------------------------------------------------------------- -# First prompt extraction from head chunk -# --------------------------------------------------------------------------- - - -def _extract_first_prompt_from_head(head: str) -> str: - """Extracts the first meaningful user prompt from a JSONL head chunk. - - Skips tool_result messages, isMeta, isCompactSummary, command-name - messages, and auto-generated patterns. Truncates to 200 chars. - """ - start = 0 - command_fallback = "" - head_len = len(head) - - while start < head_len: - newline_idx = head.find("\n", start) - if newline_idx >= 0: - line = head[start:newline_idx] - start = newline_idx + 1 - else: - line = head[start:] - start = head_len - - if '"type":"user"' not in line and '"type": "user"' not in line: - continue - if '"tool_result"' in line: - continue - if '"isMeta":true' in line or '"isMeta": true' in line: - continue - if '"isCompactSummary":true' in line or '"isCompactSummary": true' in line: - continue - - try: - entry = json.loads(line) - except (json.JSONDecodeError, ValueError): - continue - - if not isinstance(entry, dict) or entry.get("type") != "user": - continue - - message = entry.get("message") - if not isinstance(message, dict): - continue - - content = message.get("content") - texts: list[str] = [] - if isinstance(content, str): - texts.append(content) - elif isinstance(content, list): - for block in content: - if ( - isinstance(block, dict) - and block.get("type") == "text" - and isinstance(block.get("text"), str) - ): - texts.append(block["text"]) - - for raw in texts: - result = raw.replace("\n", " ").strip() - if not result: - continue - - # Skip slash-command messages but remember first as fallback - cmd_match = _COMMAND_NAME_RE.search(result) - if cmd_match: - if not command_fallback: - command_fallback = cmd_match.group(1) - continue - - if _SKIP_FIRST_PROMPT_PATTERN.match(result): - continue - - if len(result) > 200: - result = result[:200].rstrip() + "\u2026" - return result - - if command_fallback: - return command_fallback - return "" - - -# --------------------------------------------------------------------------- -# File I/O — read head and tail of a file -# --------------------------------------------------------------------------- - - -class _LiteSessionFile: - """Result of reading a session file's head, tail, mtime and size.""" - - __slots__ = ("mtime", "size", "head", "tail") - - def __init__(self, mtime: int, size: int, head: str, tail: str) -> None: - self.mtime = mtime - self.size = size - self.head = head - self.tail = tail - - -def _read_session_lite(file_path: Path) -> _LiteSessionFile | None: - """Opens a session file, stats it, and reads head + tail. - - Returns None on any error or if file is empty. - """ - try: - with file_path.open("rb") as f: - stat = os.fstat(f.fileno()) - size = stat.st_size - mtime = int(stat.st_mtime * 1000) - - head_bytes = f.read(LITE_READ_BUF_SIZE) - if not head_bytes: - return None - - head = head_bytes.decode("utf-8", errors="replace") - - tail_offset = max(0, size - LITE_READ_BUF_SIZE) - if tail_offset == 0: - tail = head - else: - f.seek(tail_offset) - tail_bytes = f.read(LITE_READ_BUF_SIZE) - tail = tail_bytes.decode("utf-8", errors="replace") - - return _LiteSessionFile(mtime=mtime, size=size, head=head, tail=tail) - except OSError: - return None - - -# --------------------------------------------------------------------------- -# Git worktree detection -# --------------------------------------------------------------------------- - - -def _get_worktree_paths(cwd: str) -> list[str]: - """Returns absolute worktree paths for the git repo containing cwd. - - Returns empty list if git is unavailable or cwd is not in a repo. - """ - try: - result = subprocess.run( - ["git", "worktree", "list", "--porcelain"], - cwd=cwd, - capture_output=True, - text=True, - timeout=5, - check=False, - ) - except (OSError, subprocess.SubprocessError): - return [] - - if result.returncode != 0 or not result.stdout: - return [] - - paths = [] - for line in result.stdout.split("\n"): - if line.startswith("worktree "): - path = unicodedata.normalize("NFC", line[len("worktree ") :]) - paths.append(path) - return paths - - -# --------------------------------------------------------------------------- -# Field extraction — shared by list_sessions and get_session_info -# --------------------------------------------------------------------------- - - -def _parse_session_info_from_lite( - session_id: str, - lite: _LiteSessionFile, - project_path: str | None = None, -) -> SDKSessionInfo | None: - """Parses SDKSessionInfo fields from a lite session read (head/tail/stat). - - Returns None for sidechain sessions or metadata-only sessions with no - extractable summary. - - Shared by list_sessions and get_session_info. - """ - head, tail, mtime, size = lite.head, lite.tail, lite.mtime, lite.size - - # Check first line for sidechain sessions - first_newline = head.find("\n") - first_line = head[:first_newline] if first_newline >= 0 else head - if '"isSidechain":true' in first_line or '"isSidechain": true' in first_line: - return None - - # User-set title (customTitle) wins over AI-generated title (aiTitle). - # Head fallback covers short sessions where the title entry may not be in tail. - custom_title = ( - _extract_last_json_string_field(tail, "customTitle") - or _extract_last_json_string_field(head, "customTitle") - or _extract_last_json_string_field(tail, "aiTitle") - or _extract_last_json_string_field(head, "aiTitle") - or None - ) - first_prompt = _extract_first_prompt_from_head(head) or None - # lastPrompt tail entry shows what the user was most recently doing. - summary = ( - custom_title - or _extract_last_json_string_field(tail, "lastPrompt") - or _extract_last_json_string_field(tail, "summary") - or first_prompt - ) - - # Skip metadata-only sessions (no title, no summary, no prompt) - if not summary: - return None - - git_branch = ( - _extract_last_json_string_field(tail, "gitBranch") - or _extract_json_string_field(head, "gitBranch") - or None - ) - session_cwd = _extract_json_string_field(head, "cwd") or project_path or None - # Scope tag extraction to {"type":"tag"} lines — a bare tail scan for - # "tag" would match tool_use inputs (git tag, Docker tags, cloud resource - # tags). - tag_line = next( - (ln for ln in reversed(tail.split("\n")) if ln.startswith('{"type":"tag"')), - None, - ) - tag = ( - (_extract_last_json_string_field(tag_line, "tag") or None) if tag_line else None - ) - - # created_at from first entry's ISO timestamp (epoch ms). More reliable - # than stat().birthtime which is unsupported on some filesystems. - created_at: int | None = None - first_timestamp = _extract_json_string_field(first_line, "timestamp") - if first_timestamp: - try: - # Python 3.10's fromisoformat doesn't support trailing 'Z' - ts = ( - first_timestamp.replace("Z", "+00:00") - if first_timestamp.endswith("Z") - else first_timestamp - ) - created_at = int(datetime.fromisoformat(ts).timestamp() * 1000) - except ValueError: - pass - - return SDKSessionInfo( - session_id=session_id, - summary=summary, - last_modified=mtime, - file_size=size, - custom_title=custom_title, - first_prompt=first_prompt, - git_branch=git_branch, - cwd=session_cwd, - tag=tag, - created_at=created_at, - ) - - -# --------------------------------------------------------------------------- -# Core implementation -# --------------------------------------------------------------------------- - - -def _read_sessions_from_dir( - project_dir: Path, project_path: str | None = None -) -> list[SDKSessionInfo]: - """Reads session files from a single project directory. - - Each file gets a stat + head/tail read. Filters out sidechain sessions - and metadata-only sessions (no title/summary/prompt). - """ - try: - entries = list(project_dir.iterdir()) - except OSError: - return [] - - results: list[SDKSessionInfo] = [] - - for entry in entries: - name = entry.name - if not name.endswith(".jsonl"): - continue - session_id = _validate_uuid(name[:-6]) - if not session_id: - continue - - lite = _read_session_lite(entry) - if lite is None: - continue - - info = _parse_session_info_from_lite(session_id, lite, project_path) - if info is not None: - results.append(info) - - return results - - -def _deduplicate_by_session_id( - sessions: list[SDKSessionInfo], -) -> list[SDKSessionInfo]: - """Deduplicates by session_id, keeping the newest last_modified.""" - by_id: dict[str, SDKSessionInfo] = {} - for s in sessions: - existing = by_id.get(s.session_id) - if existing is None or s.last_modified > existing.last_modified: - by_id[s.session_id] = s - return list(by_id.values()) - - -def _apply_sort_limit_offset( - sessions: list[SDKSessionInfo], - limit: int | None, - offset: int = 0, -) -> list[SDKSessionInfo]: - """Sorts sessions by last_modified descending and applies offset + limit.""" - sessions.sort(key=lambda s: s.last_modified, reverse=True) - if offset > 0: - sessions = sessions[offset:] - if limit is not None and limit > 0: - sessions = sessions[:limit] - return sessions - - -def _list_sessions_for_project( - directory: str, - limit: int | None, - offset: int, - include_worktrees: bool, -) -> list[SDKSessionInfo]: - """Lists sessions for a specific project directory (and its worktrees).""" - canonical_dir = _canonicalize_path(directory) - - if include_worktrees: - try: - worktree_paths = _get_worktree_paths(canonical_dir) - except Exception: - worktree_paths = [] - else: - worktree_paths = [] - - # No worktrees (or git not available / scanning disabled) — - # just scan the single project dir - if len(worktree_paths) <= 1: - project_dir = _find_project_dir(canonical_dir) - if project_dir is None: - return [] - sessions = _read_sessions_from_dir(project_dir, canonical_dir) - return _apply_sort_limit_offset(sessions, limit, offset) - - # Worktree-aware scanning: find all project dirs matching any worktree - projects_dir = _get_projects_dir() - case_insensitive = sys.platform == "win32" - - # Sort worktree paths by sanitized prefix length (longest first) so - # more specific matches take priority over shorter ones - indexed = [] - for wt in worktree_paths: - sanitized = _sanitize_path(wt) - prefix = sanitized.lower() if case_insensitive else sanitized - indexed.append((wt, prefix)) - indexed.sort(key=lambda x: len(x[1]), reverse=True) - - try: - all_dirents = [e for e in projects_dir.iterdir() if e.is_dir()] - except OSError: - # Fall back to single project dir - project_dir = _find_project_dir(canonical_dir) - if project_dir is None: - return _apply_sort_limit_offset([], limit, offset) - sessions = _read_sessions_from_dir(project_dir, canonical_dir) - return _apply_sort_limit_offset(sessions, limit, offset) - - all_sessions: list[SDKSessionInfo] = [] - seen_dirs: set[str] = set() - - # Always include the user's actual directory (handles subdirectories - # like /repo/packages/my-app that won't match worktree root prefixes) - canonical_project_dir = _find_project_dir(canonical_dir) - if canonical_project_dir is not None: - dir_base = canonical_project_dir.name - seen_dirs.add(dir_base.lower() if case_insensitive else dir_base) - sessions = _read_sessions_from_dir(canonical_project_dir, canonical_dir) - all_sessions.extend(sessions) - - for entry in all_dirents: - dir_name = entry.name.lower() if case_insensitive else entry.name - if dir_name in seen_dirs: - continue - - for wt_path, prefix in indexed: - # Only use startswith for truncated paths (>MAX_SANITIZED_LENGTH) - # where a hash suffix follows. For short paths, require exact match - # to avoid /root/project matching /root/project-foo. - is_match = dir_name == prefix or ( - len(prefix) >= MAX_SANITIZED_LENGTH - and dir_name.startswith(prefix + "-") - ) - if is_match: - seen_dirs.add(dir_name) - sessions = _read_sessions_from_dir(entry, wt_path) - all_sessions.extend(sessions) - break - - deduped = _deduplicate_by_session_id(all_sessions) - return _apply_sort_limit_offset(deduped, limit, offset) - - -def _list_all_sessions(limit: int | None, offset: int) -> list[SDKSessionInfo]: - """Lists sessions across all project directories.""" - projects_dir = _get_projects_dir() - - try: - project_dirs = [e for e in projects_dir.iterdir() if e.is_dir()] - except OSError: - return [] - - all_sessions: list[SDKSessionInfo] = [] - for project_dir in project_dirs: - all_sessions.extend(_read_sessions_from_dir(project_dir)) - - deduped = _deduplicate_by_session_id(all_sessions) - return _apply_sort_limit_offset(deduped, limit, offset) - - -def list_sessions( - directory: str | None = None, - limit: int | None = None, - offset: int = 0, - include_worktrees: bool = True, -) -> list[SDKSessionInfo]: - """Lists sessions with metadata extracted from stat + head/tail reads. - - When ``directory`` is provided, returns sessions for that project - directory and its git worktrees. When omitted, returns sessions - across all projects. - - Use ``limit`` and ``offset`` for pagination. - - Args: - directory: Directory to list sessions for. When provided, returns - sessions for this project directory (and optionally its git - worktrees). When omitted, returns sessions across all projects. - limit: Maximum number of sessions to return. - offset: Number of sessions to skip from the start of the sorted - result set. Use with ``limit`` for pagination. Defaults to 0. - include_worktrees: When ``directory`` is provided and the directory - is inside a git repository, include sessions from all git - worktree paths. Defaults to ``True``. - - Returns: - List of ``SDKSessionInfo`` sorted by ``last_modified`` descending. - - See Also: - :func:`list_sessions_from_store` for the :class:`SessionStore`-backed - async variant. - - Example: - List sessions for a specific project:: - - sessions = list_sessions(directory="/path/to/project") - - Paginate:: - - page1 = list_sessions(limit=50) - page2 = list_sessions(limit=50, offset=50) - - List sessions without scanning git worktrees:: - - sessions = list_sessions( - directory="/path/to/project", - include_worktrees=False, - ) - """ - if directory: - return _list_sessions_for_project(directory, limit, offset, include_worktrees) - return _list_all_sessions(limit, offset) - - -# --------------------------------------------------------------------------- -# get_session_info — single-session metadata lookup -# --------------------------------------------------------------------------- - - -def get_session_info( - session_id: str, - directory: str | None = None, -) -> SDKSessionInfo | None: - """Reads metadata for a single session by ID. - - Wraps ``_read_session_lite`` for one file — no O(n) directory scan. - Directory resolution matches ``get_session_messages``: ``directory`` is - the project path; when omitted, all project directories are searched for - the session file. - - Args: - session_id: UUID of the session to look up. - directory: Project directory path (same semantics as - ``list_sessions(directory=...)``). When omitted, all project - directories are searched for the session file. - - Returns: - ``SDKSessionInfo`` for the session, or ``None`` if the session file - is not found, is a sidechain session, or has no extractable summary. - - See Also: - :func:`get_session_info_from_store` for the - :class:`SessionStore`-backed async variant. - - Example: - Look up a session in a specific project:: - - info = get_session_info( - "550e8400-e29b-41d4-a716-446655440000", - directory="/path/to/project", - ) - if info: - print(info.summary) - - Search all projects for a session:: - - info = get_session_info("550e8400-e29b-41d4-a716-446655440000") - """ - uuid = _validate_uuid(session_id) - if not uuid: - return None - file_name = f"{uuid}.jsonl" - - if directory: - canonical = _canonicalize_path(directory) - project_dir = _find_project_dir(canonical) - if project_dir is not None: - lite = _read_session_lite(project_dir / file_name) - if lite is not None: - return _parse_session_info_from_lite(uuid, lite, canonical) - - # Worktree fallback — matches get_session_messages semantics. - # Sessions may live under a different worktree root. - try: - worktree_paths = _get_worktree_paths(canonical) - except Exception: - worktree_paths = [] - for wt in worktree_paths: - if wt == canonical: - continue - wt_project_dir = _find_project_dir(wt) - if wt_project_dir is not None: - lite = _read_session_lite(wt_project_dir / file_name) - if lite is not None: - return _parse_session_info_from_lite(uuid, lite, wt) - - return None - - # No directory — search all project directories for the session file. - projects_dir = _get_projects_dir() - try: - dirents = [e for e in projects_dir.iterdir() if e.is_dir()] - except OSError: - return None - for entry in dirents: - lite = _read_session_lite(entry / file_name) - if lite is not None: - return _parse_session_info_from_lite(uuid, lite) - return None - - -# --------------------------------------------------------------------------- -# get_session_messages — full transcript reconstruction -# --------------------------------------------------------------------------- - -# Transcript entry types that carry uuid + parentUuid chain links. -_TRANSCRIPT_ENTRY_TYPES = frozenset( - {"user", "assistant", "progress", "system", "attachment"} -) - -# Internal type for parsed JSONL transcript entries — mirrors the TS -# TranscriptEntry type but as a loose dict (fields: type, uuid, parentUuid, -# sessionId, message, isSidechain, isMeta, isCompactSummary, teamName). -_TranscriptEntry = dict[str, Any] - - -def _try_read_session_file(project_dir: Path, file_name: str) -> str | None: - """Tries to read a session JSONL file from a project directory.""" - try: - return (project_dir / file_name).read_text(encoding="utf-8") - except OSError: - return None - - -def _read_session_file(session_id: str, directory: str | None) -> str | None: - """Finds and reads the session JSONL file. - - If directory is provided, looks in that project directory and its git - worktrees (with prefix-fallback for Bun/Node hash mismatches on long - paths). Otherwise, searches all project directories. - - Returns the file content, or None if not found. - """ - file_name = f"{session_id}.jsonl" - - if directory: - canonical_dir = _canonicalize_path(directory) - - # Try the exact/prefix-matched project directory first - project_dir = _find_project_dir(canonical_dir) - if project_dir is not None: - content = _try_read_session_file(project_dir, file_name) - if content: - return content - - # Try worktree paths — sessions may live under a different worktree root - try: - worktree_paths = _get_worktree_paths(canonical_dir) - except Exception: - worktree_paths = [] - - for wt in worktree_paths: - if wt == canonical_dir: - continue # already tried above - wt_project_dir = _find_project_dir(wt) - if wt_project_dir is not None: - content = _try_read_session_file(wt_project_dir, file_name) - if content: - return content - - return None - - # No directory provided — search all project directories - projects_dir = _get_projects_dir() - try: - dirents = list(projects_dir.iterdir()) - except OSError: - return None - - for entry in dirents: - content = _try_read_session_file(entry, file_name) - if content: - return content - - return None - - -def _parse_transcript_entries(content: str) -> list[_TranscriptEntry]: - """Parses JSONL content into transcript entries. - - Only keeps entries that have a uuid and are transcript message types - (user/assistant/progress/system/attachment). Skips corrupt lines. - """ - entries: list[_TranscriptEntry] = [] - start = 0 - length = len(content) - - while start < length: - end = content.find("\n", start) - if end == -1: - end = length - - line = content[start:end].strip() - start = end + 1 - if not line: - continue - - try: - entry = json.loads(line) - except (json.JSONDecodeError, ValueError): - continue - - if not isinstance(entry, dict): - continue - entry_type = entry.get("type") - if entry_type in _TRANSCRIPT_ENTRY_TYPES and isinstance(entry.get("uuid"), str): - entries.append(entry) - - return entries - - -def _build_conversation_chain( - entries: list[_TranscriptEntry], -) -> list[_TranscriptEntry]: - """Builds the conversation chain by finding the leaf and walking parentUuid. - - Returns messages in chronological order (root -> leaf). - - Note: logicalParentUuid (set on compact_boundary entries) is intentionally - NOT followed. This matches VS Code IDE behavior — post-compaction, the - isCompactSummary message replaces earlier messages, so following logical - parents would duplicate content. - """ - if not entries: - return [] - - # Index by uuid for O(1) parent lookup - by_uuid: dict[str, _TranscriptEntry] = {} - for entry in entries: - by_uuid[entry["uuid"]] = entry - - # Build index of entry positions (file order) for tie-breaking - entry_index: dict[str, int] = {} - for i, entry in enumerate(entries): - entry_index[entry["uuid"]] = i - - # Find terminal messages (no children point to them via parentUuid) - parent_uuids: set[str] = set() - for entry in entries: - parent = entry.get("parentUuid") - if parent: - parent_uuids.add(parent) - - terminals = [e for e in entries if e["uuid"] not in parent_uuids] - - # From each terminal, walk back to find the nearest user/assistant leaf - leaves: list[_TranscriptEntry] = [] - for terminal in terminals: - walk_cur: _TranscriptEntry | None = terminal - walk_seen: set[str] = set() - while walk_cur is not None: - uid = walk_cur["uuid"] - if uid in walk_seen: - break - walk_seen.add(uid) - if walk_cur.get("type") in ("user", "assistant"): - leaves.append(walk_cur) - break - parent = walk_cur.get("parentUuid") - walk_cur = by_uuid.get(parent) if parent else None - - if not leaves: - return [] - - # Pick the leaf from the main chain (not sidechain/team/meta), preferring - # the highest position in the entries array (most recent in file) - main_leaves = [ - leaf - for leaf in leaves - if not leaf.get("isSidechain") - and not leaf.get("teamName") - and not leaf.get("isMeta") - ] - - def _pick_best(candidates: list[_TranscriptEntry]) -> _TranscriptEntry: - best = candidates[0] - best_idx = entry_index.get(best["uuid"], -1) - for cur in candidates[1:]: - cur_idx = entry_index.get(cur["uuid"], -1) - if cur_idx > best_idx: - best = cur - best_idx = cur_idx - return best - - leaf = _pick_best(main_leaves) if main_leaves else _pick_best(leaves) - - # Walk from leaf to root via parentUuid - chain: list[_TranscriptEntry] = [] - chain_seen: set[str] = set() - chain_cur: _TranscriptEntry | None = leaf - while chain_cur is not None: - uid = chain_cur["uuid"] - if uid in chain_seen: - break - chain_seen.add(uid) - chain.append(chain_cur) - parent = chain_cur.get("parentUuid") - chain_cur = by_uuid.get(parent) if parent else None - - chain.reverse() - return chain - - -def _is_visible_message(entry: _TranscriptEntry) -> bool: - """Returns True if the entry should be included in the returned messages.""" - entry_type = entry.get("type") - if entry_type != "user" and entry_type != "assistant": - return False - if entry.get("isMeta"): - return False - if entry.get("isSidechain"): - return False - # Note: isCompactSummary messages are intentionally included. They contain - # the summarized content from compacted conversations and are the only - # representation of that content post-compaction. This matches VS Code IDE - # behavior (transcriptToSessionMessage does not filter them). - return not entry.get("teamName") - - -def _to_session_message(entry: _TranscriptEntry) -> SessionMessage: - """Converts a transcript entry dict into a SessionMessage.""" - entry_type = entry.get("type") - # Narrow to the Literal type — _is_visible_message already guarantees - # this is "user" or "assistant". - msg_type: str = "user" if entry_type == "user" else "assistant" - return SessionMessage( - type=msg_type, # type: ignore[arg-type] - uuid=entry.get("uuid", ""), - session_id=entry.get("sessionId", ""), - message=entry.get("message"), - parent_tool_use_id=None, - ) - - -def get_session_messages( - session_id: str, - directory: str | None = None, - limit: int | None = None, - offset: int = 0, -) -> list[SessionMessage]: - """Reads a session's conversation messages from its JSONL transcript file. - - Parses the full JSONL, builds the conversation chain via ``parentUuid`` - links, and returns user/assistant messages in chronological order. - - Args: - session_id: UUID of the session to read. - directory: Project directory to find the session in. If omitted, - searches all project directories under ``~/.claude/projects/``. - limit: Maximum number of messages to return. - offset: Number of messages to skip from the start. - - Returns: - List of ``SessionMessage`` objects in chronological order. Returns - an empty list if the session is not found, the session_id is not a - valid UUID, or the transcript contains no visible messages. - - See Also: - :func:`get_session_messages_from_store` for the - :class:`SessionStore`-backed async variant. - - Example: - Read all messages from a session:: - - messages = get_session_messages( - "550e8400-e29b-41d4-a716-446655440000", - directory="/path/to/project", - ) - for msg in messages: - print(msg.type, msg.message) - - Read with pagination:: - - page = get_session_messages( - session_id, limit=10, offset=20 - ) - """ - if not _validate_uuid(session_id): - return [] - - content = _read_session_file(session_id, directory) - if not content: - return [] - - entries = _parse_transcript_entries(content) - return _entries_to_session_messages(entries, limit, offset) - - -def _entries_to_session_messages( - entries: list[_TranscriptEntry], - limit: int | None, - offset: int, -) -> list[SessionMessage]: - """Builds the conversation chain from parsed entries and applies paging. - - Shared by the filesystem and SessionStore-backed paths. - """ - chain = _build_conversation_chain(entries) - visible = [e for e in chain if _is_visible_message(e)] - messages = [_to_session_message(e) for e in visible] - - # Apply offset and limit - if limit is not None and limit > 0: - return messages[offset : offset + limit] - if offset > 0: - return messages[offset:] - return messages - - -# --------------------------------------------------------------------------- -# list_subagents / get_subagent_messages — subagent transcript reading -# --------------------------------------------------------------------------- - - -def _resolve_session_file_path(session_id: str, directory: str | None) -> Path | None: - """Resolves the on-disk path of a session JSONL file. - - Directory resolution mirrors ``_read_session_file``: when ``directory`` - is provided, looks in that project directory and its git worktrees; - otherwise searches all project directories. Returns the path of the - first non-empty match, or ``None`` if not found. - """ - file_name = f"{session_id}.jsonl" - - def _stat_candidate(project_dir: Path) -> Path | None: - candidate = project_dir / file_name - try: - if candidate.stat().st_size > 0: - return candidate - except OSError: - pass - return None - - if directory: - canonical_dir = _canonicalize_path(directory) - - project_dir = _find_project_dir(canonical_dir) - if project_dir is not None: - found = _stat_candidate(project_dir) - if found is not None: - return found - - try: - worktree_paths = _get_worktree_paths(canonical_dir) - except Exception: - worktree_paths = [] - - for wt in worktree_paths: - if wt == canonical_dir: - continue - wt_project_dir = _find_project_dir(wt) - if wt_project_dir is not None: - found = _stat_candidate(wt_project_dir) - if found is not None: - return found - - return None - - projects_dir = _get_projects_dir() - try: - dirents = list(projects_dir.iterdir()) - except OSError: - return None - - for entry in dirents: - if not entry.is_dir(): - continue - found = _stat_candidate(entry) - if found is not None: - return found - - return None - - -def _resolve_subagents_dir(session_id: str, directory: str | None) -> Path | None: - """Resolves the subagents directory for a given session. - - The session file lives at ``/.jsonl`` and the - subagents directory at ``//subagents/``. - - Returns ``None`` if the session cannot be found. - """ - resolved = _resolve_session_file_path(session_id, directory) - if resolved is None: - return None - # Strip the .jsonl suffix to derive the session directory. - session_dir = resolved.with_suffix("") - return session_dir / "subagents" - - -def _collect_agent_files(base_dir: Path) -> list[tuple[str, Path]]: - """Recursively collects ``agent-*.jsonl`` files from a directory tree. - - Subagent transcripts may live directly in ``subagents/`` or in nested - subdirectories such as ``subagents/workflows//``. - - Returns a list of ``(agent_id, file_path)`` tuples. - """ - results: list[tuple[str, Path]] = [] - - def _walk(current_dir: Path) -> None: - try: - dirents = sorted(current_dir.iterdir(), key=lambda p: p.name) - except OSError: - return - for entry in dirents: - name = entry.name - if ( - entry.is_file() - and name.startswith("agent-") - and name.endswith(".jsonl") - ): - agent_id = name[len("agent-") : -len(".jsonl")] - results.append((agent_id, entry)) - elif entry.is_dir(): - _walk(entry) - - _walk(base_dir) - return results - - -def _build_subagent_chain(entries: list[_TranscriptEntry]) -> list[_TranscriptEntry]: - """Builds the conversation chain for a subagent transcript. - - Subagent transcripts are simpler than main sessions — no compaction, - no sidechains, no preserved segments. Find the last user/assistant - entry and walk ``parentUuid`` links back to the root. - """ - if not entries: - return [] - - by_uuid: dict[str, _TranscriptEntry] = {} - for entry in entries: - by_uuid[entry["uuid"]] = entry - - # Subagent transcripts are linear — the last user/assistant entry is - # the leaf. - leaf: _TranscriptEntry | None = None - for entry in reversed(entries): - if entry.get("type") in ("user", "assistant"): - leaf = entry - break - if leaf is None: - return [] - - chain: list[_TranscriptEntry] = [] - seen: set[str] = set() - current: _TranscriptEntry | None = leaf - while current is not None: - uid = current["uuid"] - if uid in seen: - break - seen.add(uid) - chain.append(current) - parent = current.get("parentUuid") - current = by_uuid.get(parent) if parent else None - - chain.reverse() - return chain - - -def list_subagents( - session_id: str, - directory: str | None = None, -) -> list[str]: - """Lists subagent IDs for a given session by scanning the subagents directory. - - Subagent transcripts are stored at - ``~/.claude/projects///subagents/agent-.jsonl`` - (and may be nested in subdirectories such as ``workflows//``). - - Args: - session_id: UUID of the parent session. - directory: Project directory to find the session in. If omitted, - searches all project directories under ``~/.claude/projects/``. - - Returns: - List of subagent ID strings. Returns an empty list if the session - is not found, the session_id is not a valid UUID, or the session - has no subagents. - - See Also: - :func:`list_subagents_from_store` for the :class:`SessionStore`-backed - async variant. - - Example: - List subagent IDs for a session:: - - agent_ids = list_subagents( - "550e8400-e29b-41d4-a716-446655440000", - directory="/path/to/project", - ) - """ - if not _validate_uuid(session_id): - return [] - - subagents_dir = _resolve_subagents_dir(session_id, directory) - if subagents_dir is None: - return [] - - return [agent_id for agent_id, _ in _collect_agent_files(subagents_dir)] - - -def get_subagent_messages( - session_id: str, - agent_id: str, - directory: str | None = None, - limit: int | None = None, - offset: int = 0, -) -> list[SessionMessage]: - """Reads a subagent's conversation messages from its JSONL transcript file. - - Parses the subagent transcript, builds the conversation chain via - ``parentUuid`` links, and returns user/assistant messages in - chronological order. - - Args: - session_id: UUID of the parent session. - agent_id: ID of the subagent (as returned by ``list_subagents``). - directory: Project directory to find the session in. If omitted, - searches all project directories under ``~/.claude/projects/``. - limit: Maximum number of messages to return. - offset: Number of messages to skip from the start. - - Returns: - List of ``SessionMessage`` objects in chronological order. Returns - an empty list if the session or subagent is not found, the - session_id is not a valid UUID, or the transcript contains no - user/assistant messages. - - See Also: - :func:`get_subagent_messages_from_store` for the - :class:`SessionStore`-backed async variant. - - Example: - Read all messages from a subagent:: - - messages = get_subagent_messages( - "550e8400-e29b-41d4-a716-446655440000", - "abc123", - directory="/path/to/project", - ) - """ - if not _validate_uuid(session_id): - return [] - if not agent_id: - return [] - - subagents_dir = _resolve_subagents_dir(session_id, directory) - if subagents_dir is None: - return [] - - # The agent file may be directly in subagents/ or in a nested - # subdirectory — scan to find it. - match: Path | None = None - for found_id, file_path in _collect_agent_files(subagents_dir): - if found_id == agent_id: - match = file_path - break - if match is None: - return [] - - try: - content = match.read_text(encoding="utf-8") - except OSError: - return [] - if not content: - return [] - - entries = _parse_transcript_entries(content) - return _entries_to_subagent_messages(entries, limit, offset) - - -def _entries_to_subagent_messages( - entries: list[_TranscriptEntry], - limit: int | None, - offset: int, -) -> list[SessionMessage]: - """Builds the subagent chain from parsed entries and applies paging. - - Shared by the filesystem and SessionStore-backed paths. - """ - chain = _build_subagent_chain(entries) - messages = [ - _to_session_message(e) for e in chain if e.get("type") in ("user", "assistant") - ] - - if limit is not None and limit > 0: - return messages[offset : offset + limit] - if offset > 0: - return messages[offset:] - return messages - - -# --------------------------------------------------------------------------- -# SessionStore-backed implementations -# --------------------------------------------------------------------------- - - -def project_key_for_directory(directory: str | Path | None = None) -> str: - """Derive the :class:`SessionStore` ``project_key`` for a directory. - - Defaults to the current working directory. Uses the same realpath + NFC - normalization + djb2-hashed sanitization the CLI uses for project - directory names, so keys match between local-disk transcripts and - store-mirrored transcripts even on filesystems that decompose Unicode - (macOS HFS+). - """ - abs_path = _canonicalize_path(str(directory) if directory is not None else ".") - return _sanitize_path(abs_path) - - -def _entries_to_jsonl(entries: list[Any]) -> str: - """Serialize store entries to a JSONL string (one ``json.dumps`` per line). - - The ``SessionStore.load`` contract permits adapters to reorder object keys - (e.g. Postgres JSONB), but ``_parse_session_info_from_lite`` scans for - ``{"type":"tag"`` as a line prefix. Hoist ``type`` to the front so the - store path matches the byte shape the disk path produces. - """ - - def _type_first(e: Any) -> Any: - if isinstance(e, dict) and "type" in e: - return {"type": e["type"], **e} - return e - - return ( - "\n".join(json.dumps(_type_first(e), separators=(",", ":")) for e in entries) - + "\n" - ) - - -def _jsonl_to_lite(jsonl: str, mtime: int) -> _LiteSessionFile: - """Build the head/tail/size lite shape from an in-memory JSONL string. - - Matches ``_read_session_lite``'s byte semantics so the store path exposes - the same slice to ``_parse_session_info_from_lite`` as the disk path - would for the same transcript. - """ - buf = jsonl.encode("utf-8") - size = len(buf) - head = buf[:LITE_READ_BUF_SIZE].decode("utf-8", errors="replace") - tail = ( - buf[max(0, size - LITE_READ_BUF_SIZE) :].decode("utf-8", errors="replace") - if size > LITE_READ_BUF_SIZE - else head - ) - return _LiteSessionFile(mtime=mtime, size=size, head=head, tail=tail) - - -def _mtime_from_jsonl_tail(jsonl: str) -> int: - """Best-effort mtime: parse the last entry's ``timestamp`` field. - - Falls back to the current wall-clock time when absent or unparseable. - """ - trimmed = jsonl.rstrip() - last_line = trimmed[trimmed.rfind("\n") + 1 :] - try: - obj = json.loads(last_line) - except (json.JSONDecodeError, ValueError): - obj = None - if isinstance(obj, dict): - ts = obj.get("timestamp") - if isinstance(ts, str): - try: - norm = ts.replace("Z", "+00:00") if ts.endswith("Z") else ts - return int(datetime.fromisoformat(norm).timestamp() * 1000) - except ValueError: - pass - return int(time.time() * 1000) - - -def _filter_transcript_entries(entries: list[Any]) -> list[_TranscriptEntry]: - """Filter store-loaded entries to transcript message types with a ``uuid``. - - Mirrors ``_parse_transcript_entries`` for the already-parsed object path - so chain-building never sees metadata-only entries (custom-title, tag, - agent_metadata, etc.). - """ - result: list[_TranscriptEntry] = [] - for e in entries: - if ( - isinstance(e, dict) - and e.get("type") in _TRANSCRIPT_ENTRY_TYPES - and isinstance(e.get("uuid"), str) - ): - result.append(e) - return result - - -async def _load_store_entries_as_jsonl( - store: SessionStore, session_id: str, directory: str | None -) -> str | None: - """Load entries from a SessionStore and serialize to a JSONL string. - - Returns ``None`` if the session has no entries. - """ - project_key = project_key_for_directory(directory) - key: SessionKey = {"project_key": project_key, "session_id": session_id} - entries = await store.load(key) - if not entries: - return None - return _entries_to_jsonl(entries) - - -async def _derive_infos_via_load( - session_store: SessionStore, - listing: list[Any], - directory: str | None, - project_path: str, -) -> list[SDKSessionInfo]: - """Derive ``SDKSessionInfo`` for each ``listing`` entry via per-session - ``store.load()`` + lite-parse. - - Loads run concurrently with a fixed bound so large listings don't exhaust - adapter connection pools or hit backend rate limits; adapter errors degrade - that row to an empty summary instead of failing the whole list. Sidechain - and no-summary sessions are dropped. - """ - sem = asyncio.Semaphore(_STORE_LIST_LOAD_CONCURRENCY) - - async def _bounded_load(sid: str) -> str | None: - async with sem: - return await _load_store_entries_as_jsonl(session_store, sid, directory) - - settled = await asyncio.gather( - *(_bounded_load(e["session_id"]) for e in listing), - return_exceptions=True, - ) - results: list[SDKSessionInfo] = [] - for entry, outcome in zip(listing, settled, strict=True): - sid = entry["session_id"] - mtime = entry["mtime"] - if isinstance(outcome, BaseException): - results.append( - SDKSessionInfo(session_id=sid, summary="", last_modified=mtime) - ) - continue - if outcome is None: - continue - parsed = _parse_session_info_from_lite( - sid, _jsonl_to_lite(outcome, mtime), project_path - ) - if parsed is None: - # Sidechain or no extractable summary — drop, matching the - # filesystem path. - continue - parsed.last_modified = mtime - results.append(parsed) - return results - - -async def list_sessions_from_store( - session_store: SessionStore, - directory: str | None = None, - limit: int | None = None, - offset: int = 0, -) -> list[SDKSessionInfo]: - """List sessions from a :class:`SessionStore`. - - Async, store-backed counterpart to :func:`list_sessions`. Loads each - session's entries to derive a real summary via the same lite-parse used - by the filesystem path, so disk and store paths produce identical - results for the same transcript content. - - Args: - session_store: The store to read from. Must implement - :meth:`SessionStore.list_session_summaries` or - :meth:`SessionStore.list_sessions` (or both). - directory: Project directory used to compute the ``project_key``. - Defaults to the current working directory. - limit: Maximum number of sessions to return. - offset: Number of sessions to skip from the start of the sorted - result set. - - Returns: - List of ``SDKSessionInfo`` sorted by ``last_modified`` descending. - - Raises: - ValueError: If ``session_store`` implements neither - :meth:`SessionStore.list_session_summaries` nor - :meth:`SessionStore.list_sessions`. - - Note: - ``include_worktrees`` is a filesystem concept and is not honored on - the store path — the store operates on a single ``project_key``. - - .. note:: - If the store implements ``list_session_summaries``, this is one batch - summary call plus one cheap ``list_sessions()`` enumeration to - gap-fill sessions missing a sidecar or whose sidecar is stale - (``summary.mtime < list_sessions.mtime``) — zero per-session - ``load()`` calls when sidecars are complete and fresh. Otherwise - falls back to one ``store.load()`` per session (bounded at 16 - concurrent), which on remote backends with many or large sessions - can be expensive (e.g., S3 egress, Postgres large-row reads). - - Gap-fill requires ``list_sessions``: if the store implements - ``list_session_summaries`` but not ``list_sessions``, sessions - without a sidecar cannot be discovered and will be absent from the - result. - """ - project_path = _canonicalize_path(str(directory) if directory is not None else ".") - project_key = _sanitize_path(project_path) - has_list_sessions = _store_implements(session_store, "list_sessions") - - # Fast path: if the store maintains incremental summaries, fetch them in - # one call instead of N per-session load()s. - if _store_implements(session_store, "list_session_summaries"): - from .session_summary import summary_entry_to_sdk_info - - try: - summaries = await session_store.list_session_summaries(project_key) - except NotImplementedError: - pass - else: - # Build a unified slot list. Fresh summaries (mtime >= the - # session's current mtime from list_sessions) get their info up - # front; sessions present in list_sessions() but missing OR with a - # stale sidecar (summary.mtime < known mtime) get a placeholder - # slot routed through the same gap-fill path so the fold is - # recomputed from source entries. - # Summary-backed sidechain/empty sessions are dropped here (free — - # already determined) so they don't consume offset/limit positions, - # matching the disk and slow-path filter-then-paginate semantics. - if has_list_sessions: - listing = list(await session_store.list_sessions(project_key)) - known_mtimes = {e["session_id"]: e["mtime"] for e in listing} - else: - listing = [] - known_mtimes = {} - logger.debug( - "list_session_summaries without list_sessions: gap-fill " - "skipped; sessions lacking a sidecar will be omitted" - ) - - slots: list[dict[str, Any]] = [] - fresh_summary_ids: set[str] = set() - for s in summaries: - sid = s["session_id"] - if has_list_sessions: - known = known_mtimes.get(sid) - if known is None: - # Summary for a session list_sessions() no longer - # reports — drop it. - continue - if s["mtime"] < known: - # Stale sidecar — let gap-fill re-fold from source. - continue - info = summary_entry_to_sdk_info(s, project_path) - if info is None: - fresh_summary_ids.add(sid) - continue - slots.append({"mtime": s["mtime"], "info": info}) - fresh_summary_ids.add(sid) - if has_list_sessions: - slots.extend( - {"mtime": e["mtime"], "session_id": e["session_id"], "info": None} - for e in listing - if e["session_id"] not in fresh_summary_ids - ) - - # Paginate BEFORE per-session load so gap-fill load() count is - # bounded by page size, not total missing — 500 sessions lacking - # sidecars with limit=10 issues at most 10 load()s, not 500. - slots.sort(key=lambda sl: sl["mtime"], reverse=True) - # Mirror _apply_sort_limit_offset's guards so negative/zero - # offset and non-positive limit behave identically to the slow - # and disk paths. - page = slots[offset:] if offset > 0 else slots - if limit is not None and limit > 0: - page = page[:limit] - - to_fill = [sl for sl in page if sl["info"] is None] - if to_fill: - filled = await _derive_infos_via_load( - session_store, to_fill, directory, project_path - ) - by_sid = {f.session_id: f for f in filled} - for sl in to_fill: - sl["info"] = by_sid.get(sl["session_id"]) - - # Gap-fill placeholders that resolved to None (sidechain / no - # extractable summary after load) are dropped here, AFTER - # pagination — that case alone can short-page. Summary-backed - # slots were already pre-filtered above, so a store with complete - # and fresh sidecars never short-pages; a present-but-stale - # sidecar is routed through gap-fill (same as a missing one) and - # can short-page if load() yields no extractable summary. - return [sl["info"] for sl in page if sl["info"] is not None] - - if not has_list_sessions: - raise ValueError( - "session_store implements neither list_session_summaries() nor " - "list_sessions() -- cannot list sessions. Provide a store with at " - "least one of those methods." - ) - # Copy — store.list_sessions() may return a reference to internal state. - listing = list(await session_store.list_sessions(project_key)) - # Derive a real summary per session by loading its entries and reusing - # the filesystem path's lite-parse. Filtering (sidechain/empty drop) - # happens before pagination so ``limit``/``offset`` index the same - # filtered set as the disk path. - results = await _derive_infos_via_load( - session_store, listing, directory, project_path - ) - return _apply_sort_limit_offset(results, limit, offset) - - -async def get_session_info_from_store( - session_store: SessionStore, - session_id: str, - directory: str | None = None, -) -> SDKSessionInfo | None: - """Read metadata for a single session from a :class:`SessionStore`. - - Async, store-backed counterpart to :func:`get_session_info`. - - Args: - session_store: The store to read from. - session_id: UUID of the session to look up. - directory: Project directory used to compute the ``project_key``. - Defaults to the current working directory. - - Returns: - ``SDKSessionInfo`` for the session, or ``None`` if the session is - not found, the ``session_id`` is not a valid UUID, the session is - a sidechain session, or it has no extractable summary. - """ - if not _validate_uuid(session_id): - return None - jsonl = await _load_store_entries_as_jsonl(session_store, session_id, directory) - if jsonl is None: - return None - lite = _jsonl_to_lite(jsonl, _mtime_from_jsonl_tail(jsonl)) - project_path = _canonicalize_path(str(directory) if directory is not None else ".") - return _parse_session_info_from_lite(session_id, lite, project_path) - - -async def get_session_messages_from_store( - session_store: SessionStore, - session_id: str, - directory: str | None = None, - limit: int | None = None, - offset: int = 0, -) -> list[SessionMessage]: - """Read a session's conversation messages from a :class:`SessionStore`. - - Async, store-backed counterpart to :func:`get_session_messages`. Feeds - ``session_store.load()`` results directly into the chain builder — no - JSONL round-trip. - - Args: - session_store: The store to read from. - session_id: UUID of the session to read. - directory: Project directory used to compute the ``project_key``. - Defaults to the current working directory. - limit: Maximum number of messages to return. - offset: Number of messages to skip from the start. - - Returns: - List of ``SessionMessage`` objects in chronological order. Empty - list if the session is not found or ``session_id`` is invalid. - """ - if not _validate_uuid(session_id): - return [] - project_key = project_key_for_directory(directory) - key: SessionKey = {"project_key": project_key, "session_id": session_id} - entries = await session_store.load(key) - if not entries: - return [] - return _entries_to_session_messages( - _filter_transcript_entries(entries), limit, offset - ) - - -async def list_subagents_from_store( - session_store: SessionStore, - session_id: str, - directory: str | None = None, -) -> list[str]: - """List subagent IDs for a session from a :class:`SessionStore`. - - Async, store-backed counterpart to :func:`list_subagents`. - - Args: - session_store: The store to read from. Must implement - :meth:`SessionStore.list_subkeys`. - session_id: UUID of the parent session. - directory: Project directory used to compute the ``project_key``. - Defaults to the current working directory. - - Returns: - List of subagent ID strings. Empty list if ``session_id`` is - invalid or the session has no subagents. - - Raises: - ValueError: If ``session_store`` does not implement - :meth:`SessionStore.list_subkeys`. - """ - if not _validate_uuid(session_id): - return [] - if not _store_implements(session_store, "list_subkeys"): - raise ValueError( - "session_store does not implement list_subkeys() -- cannot list " - "subagents. Provide a store with a list_subkeys() method." - ) - project_key = project_key_for_directory(directory) - subkeys = await session_store.list_subkeys( - {"project_key": project_key, "session_id": session_id} - ) - seen: set[str] = set() - ids: list[str] = [] - for subpath in subkeys: - if not subpath.startswith("subagents/"): - continue - last = subpath.rsplit("/", 1)[-1] - if last.startswith("agent-"): - agent_id = last[len("agent-") :] - if agent_id not in seen: - seen.add(agent_id) - ids.append(agent_id) - return ids - - -async def get_subagent_messages_from_store( - session_store: SessionStore, - session_id: str, - agent_id: str, - directory: str | None = None, - limit: int | None = None, - offset: int = 0, -) -> list[SessionMessage]: - """Read a subagent's conversation messages from a :class:`SessionStore`. - - Async, store-backed counterpart to :func:`get_subagent_messages`. - Subagents may live at ``subagents/agent-`` or nested under - ``subagents/workflows//agent-``. Scans subkeys when the - store implements :meth:`SessionStore.list_subkeys`; otherwise tries - the direct path. - - Args: - session_store: The store to read from. - session_id: UUID of the parent session. - agent_id: ID of the subagent. - directory: Project directory used to compute the ``project_key``. - Defaults to the current working directory. - limit: Maximum number of messages to return. - offset: Number of messages to skip from the start. - - Returns: - List of ``SessionMessage`` objects in chronological order. Empty - list if the session/subagent is not found. - """ - if not _validate_uuid(session_id): - return [] - if not agent_id: - return [] - project_key = project_key_for_directory(directory) - - subpath = f"subagents/agent-{agent_id}" - if _store_implements(session_store, "list_subkeys"): - subkeys = await session_store.list_subkeys( - {"project_key": project_key, "session_id": session_id} - ) - target = f"agent-{agent_id}" - match = next( - ( - sk - for sk in subkeys - if sk.startswith("subagents/") and sk.rsplit("/", 1)[-1] == target - ), - None, - ) - if match is None: - return [] - subpath = match - - key: SessionKey = { - "project_key": project_key, - "session_id": session_id, - "subpath": subpath, - } - entries = await session_store.load(key) - if not entries: - return [] - - # Drop synthetic agent_metadata entries injected by the mirror hook — - # they describe the .meta.json sidecar, not transcript lines. - transcript = [ - e - for e in entries - if not (isinstance(e, dict) and e.get("type") == "agent_metadata") - ] - if not transcript: - return [] - - return _entries_to_subagent_messages( - _filter_transcript_entries(transcript), limit, offset - ) diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/transcript_mirror_batcher.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/transcript_mirror_batcher.py deleted file mode 100644 index d1eee6a9..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/transcript_mirror_batcher.py +++ /dev/null @@ -1,218 +0,0 @@ -"""Batching layer between ``transcript_mirror`` stdout frames and a SessionStore. - -The CLI subprocess emits ``{"type": "transcript_mirror", "filePath": ..., "entries": [...]}`` -frames interleaved with normal SDK messages. The receive loop peels these off -and hands them to :class:`TranscriptMirrorBatcher.enqueue`, which accumulates -them and flushes to :meth:`SessionStore.append` either when a ``result`` -message arrives (explicit flush) or when the pending buffer exceeds size -thresholds (eager background flush). This keeps adapter latency off the -hot path during model streaming. -""" - -from __future__ import annotations - -import asyncio -import json -import logging -from collections.abc import Awaitable, Callable -from dataclasses import dataclass, field - -from ..types import SessionKey, SessionStore, SessionStoreEntry -from .session_store import file_path_to_session_key - -logger = logging.getLogger(__name__) - -# Eager-flush thresholds. Exported for tests. -MAX_PENDING_ENTRIES = 500 -MAX_PENDING_BYTES = 1 << 20 # 1 MiB -SEND_TIMEOUT_SECONDS = 60.0 - -# Bounded retry for transient adapter failures. Backoff list length must be -# MAX_ATTEMPTS - 1 (one delay between each pair of attempts). -MIRROR_APPEND_MAX_ATTEMPTS = 3 -MIRROR_APPEND_BACKOFF_S = (0.2, 0.8) - - -@dataclass -class _MirrorEntry: - file_path: str - entries: list[SessionStoreEntry] - bytes: int - - -@dataclass -class TranscriptMirrorBatcher: - """Accumulates ``transcript_mirror`` frames and flushes them to a store. - - ``enqueue`` is fire-and-forget; ``flush`` is async. The pending queue is - bounded — when it exceeds ``max_pending_entries`` or ``max_pending_bytes`` - an eager flush fires in the background so memory stays flat during long - turns where no ``result`` (and thus no explicit ``flush()``) arrives. - - Adapter failures are retried (``MIRROR_APPEND_MAX_ATTEMPTS`` attempts - total) with short backoff; timeouts are not retried since the in-flight - call may still land. Only after the final attempt fails is the batch - dropped and reported via ``on_error``. Failures never raise — the - local-disk transcript is already durable so the session must continue - unaffected. Adapters should dedupe by ``entry["uuid"]`` when present - (some entry types lack a uuid) since a retried batch may partially - overlap a prior partial write. - """ - - store: SessionStore - projects_dir: str - on_error: Callable[[SessionKey | None, str], Awaitable[None]] - send_timeout: float = SEND_TIMEOUT_SECONDS - max_pending_entries: int = MAX_PENDING_ENTRIES - max_pending_bytes: int = MAX_PENDING_BYTES - - _pending: list[_MirrorEntry] = field(default_factory=list) - _pending_entries: int = 0 - _pending_bytes: int = 0 - _flush_task: asyncio.Task[None] | None = None - _lock: asyncio.Lock = field(default_factory=asyncio.Lock) - - def enqueue(self, file_path: str, entries: list[SessionStoreEntry]) -> None: - """Buffer a frame; schedule an eager flush if thresholds are exceeded.""" - # Approximate wire size — one stringify per frame (not per entry) keeps - # this cheap relative to the json.loads the transport already did. - size = len(json.dumps(entries)) - self._pending.append(_MirrorEntry(file_path, entries, size)) - self._pending_entries += len(entries) - self._pending_bytes += size - if ( - self._pending_entries > self.max_pending_entries - or self._pending_bytes > self.max_pending_bytes - ): - # Fire-and-forget; the lock serializes against any in-flight flush - # so append ordering holds. drain() never raises, but guard anyway - # so a future regression can't surface as an unhandled exception. - self._flush_task = asyncio.ensure_future(self._drain()) - self._flush_task.add_done_callback(lambda t: t.exception()) - - async def flush(self) -> None: - """Flush all pending entries. Awaits any in-flight eager flush first.""" - task = asyncio.ensure_future(self._drain()) - self._flush_task = task - await task - # Compare-and-null: enqueue() may have started a newer drain while we - # were awaiting; don't clobber it or the next drain() would miss the - # serialization wait and could overlap. - if self._flush_task is task: - self._flush_task = None - - async def close(self) -> None: - """Final flush before teardown. Never raises.""" - try: - await self.flush() - except Exception as e: # pragma: no cover - defensive - logger.debug(f"[TranscriptMirrorBatcher] close flush failed: {e}") - - async def _drain(self) -> None: - """Detach the pending buffer, await any prior flush, then send. - - Detaching happens before acquiring the lock so ``enqueue`` can keep - accumulating into a fresh buffer while a prior flush is in flight. - Never raises — adapter and ``on_error`` callback errors are caught - and logged. - """ - items = self._pending - self._pending = [] - self._pending_entries = 0 - self._pending_bytes = 0 - errors: list[tuple[SessionKey, str]] = [] - async with self._lock: - if not items: - return - try: - await self._do_flush(items, errors) - except Exception as e: # pragma: no cover - defensive - # _do_flush already wraps store.append; this guards any - # remaining unguarded path so the "Never raises" contract - # holds against future regressions. - logger.error("[TranscriptMirrorBatcher] _do_flush raised: %s", e) - return - # Report errors after releasing the lock so a slow on_error callback - # cannot block subsequent drains (which only need the lock for - # append-ordering). - for key, msg in errors: - try: - await self.on_error(key, msg) - except Exception as cb_err: # pragma: no cover - defensive - logger.error( - "[TranscriptMirrorBatcher] on_error callback raised: %s", - cb_err, - ) - - async def _do_flush( - self, items: list[_MirrorEntry], errors: list[tuple[SessionKey, str]] - ) -> None: - # Coalesce by file_path so each unique file gets one append per flush - # instead of one per enqueued frame. dict preserves first-seen order; - # entries within a path keep enqueue order. - by_path: dict[str, list[SessionStoreEntry]] = {} - for item in items: - bucket = by_path.get(item.file_path) - if bucket is not None: - bucket.extend(item.entries) - else: - by_path[item.file_path] = list(item.entries) - - for file_path, entries in by_path.items(): - if not entries: - # Avoid creating phantom keys in adapters that touch storage - # on append([]) — nothing to write. - continue - key = file_path_to_session_key(file_path, self.projects_dir) - if key is None: - logger.warning( - "[SessionStore] dropping mirror frame: filePath %s is not " - "under %s -- subprocess CLAUDE_CONFIG_DIR likely differs " - "from parent (custom env / container?)", - file_path, - self.projects_dir, - ) - continue - last_err: Exception | None = None - succeeded = False - for attempt in range(MIRROR_APPEND_MAX_ATTEMPTS): - if attempt > 0: - await asyncio.sleep(MIRROR_APPEND_BACKOFF_S[attempt - 1]) - try: - await asyncio.wait_for( - self.store.append(key, entries), timeout=self.send_timeout - ) - succeeded = True - break - except asyncio.TimeoutError as e: - # Don't retry on timeout: wait_for cancels the task but - # cancellation is best-effort for adapters wrapping - # non-cancellable I/O, so the in-flight call may still - # land — a retry would launch a concurrent duplicate. - # Also keeps worst-case lock hold at ~send_timeout rather - # than ~3×send_timeout + backoff. - last_err = e - logger.debug( - "[TranscriptMirrorBatcher] append timed out after " - "%.1fs for %s — not retrying", - self.send_timeout, - file_path, - ) - break - except Exception as e: # noqa: BLE001 - adapter is user code - last_err = e - logger.debug( - "[TranscriptMirrorBatcher] append attempt %d/%d failed " - "for %s: %s", - attempt + 1, - MIRROR_APPEND_MAX_ATTEMPTS, - file_path, - e, - ) - if not succeeded: - logger.error( - "[TranscriptMirrorBatcher] flush failed for %s: %s", - file_path, - last_err, - ) - errors.append((key, str(last_err))) diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/transport/__init__.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/transport/__init__.py deleted file mode 100644 index 6dedef61..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/transport/__init__.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Transport implementations for Claude SDK.""" - -from abc import ABC, abstractmethod -from collections.abc import AsyncIterator -from typing import Any - - -class Transport(ABC): - """Abstract transport for Claude communication. - - WARNING: This internal API is exposed for custom transport implementations - (e.g., remote Claude Code connections). The Claude Code team may change or - or remove this abstract class in any future release. Custom implementations - must be updated to match interface changes. - - This is a low-level transport interface that handles raw I/O with the Claude - process or service. The Query class builds on top of this to implement the - control protocol and message routing. - """ - - @abstractmethod - async def connect(self) -> None: - """Connect the transport and prepare for communication. - - For subprocess transports, this starts the process. - For network transports, this establishes the connection. - """ - pass - - @abstractmethod - async def write(self, data: str) -> None: - """Write raw data to the transport. - - Args: - data: Raw string data to write (typically JSON + newline) - """ - pass - - @abstractmethod - def read_messages(self) -> AsyncIterator[dict[str, Any]]: - """Read and parse messages from the transport. - - Yields: - Parsed JSON messages from the transport - """ - pass - - @abstractmethod - async def close(self) -> None: - """Close the transport connection and clean up resources.""" - pass - - @abstractmethod - def is_ready(self) -> bool: - """Check if transport is ready for communication. - - Returns: - True if transport is ready to send/receive messages - """ - pass - - @abstractmethod - async def end_input(self) -> None: - """End the input stream (close stdin for process transports).""" - pass - - -__all__ = ["Transport"] diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/transport/subprocess_cli.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/transport/subprocess_cli.py deleted file mode 100644 index 9a1d7458..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_internal/transport/subprocess_cli.py +++ /dev/null @@ -1,726 +0,0 @@ -"""Subprocess transport implementation using Claude Code CLI.""" - -import json -import logging -import os -import platform -import re -import shutil -from collections.abc import AsyncIterable, AsyncIterator -from contextlib import suppress -from pathlib import Path -from subprocess import PIPE -from typing import Any, cast - -import anyio -from anyio.abc import Process -from anyio.streams.text import TextReceiveStream, TextSendStream - -from ..._errors import CLIConnectionError, CLINotFoundError, ProcessError -from ..._errors import CLIJSONDecodeError as SDKJSONDecodeError -from ..._version import __version__ -from ...types import ClaudeAgentOptions, SystemPromptFile, SystemPromptPreset -from .._task_compat import TaskHandle, spawn_detached -from . import Transport - -logger = logging.getLogger(__name__) - -_DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024 # 1MB buffer limit -MINIMUM_CLAUDE_CODE_VERSION = "2.0.0" - - -class SubprocessCLITransport(Transport): - """Subprocess transport using Claude Code CLI.""" - - def __init__( - self, - prompt: str | AsyncIterable[dict[str, Any]], - options: ClaudeAgentOptions, - ): - self._prompt = prompt - # Always use streaming mode internally (matching TypeScript SDK) - # This allows agents and other large configs to be sent via initialize request - self._is_streaming = True - self._options = options - self._cli_path: str | None = ( - str(options.cli_path) if options.cli_path is not None else None - ) - self._cwd = str(options.cwd) if options.cwd else None - self._process: Process | None = None - self._stdout_stream: TextReceiveStream | None = None - self._stdin_stream: TextSendStream | None = None - self._stderr_stream: TextReceiveStream | None = None - self._stderr_task: TaskHandle | None = None - self._ready = False - self._exit_error: Exception | None = None # Track process exit errors - self._max_buffer_size = ( - options.max_buffer_size - if options.max_buffer_size is not None - else _DEFAULT_MAX_BUFFER_SIZE - ) - self._write_lock: anyio.Lock = anyio.Lock() - - def _find_cli(self) -> str: - """Find Claude Code CLI binary.""" - # First, check for bundled CLI - bundled_cli = self._find_bundled_cli() - if bundled_cli: - return bundled_cli - - # Fall back to system-wide search - if cli := shutil.which("claude"): - return cli - - locations = [ - Path.home() / ".npm-global/bin/claude", - Path("/usr/local/bin/claude"), - Path.home() / ".local/bin/claude", - Path.home() / "node_modules/.bin/claude", - Path.home() / ".yarn/bin/claude", - Path.home() / ".claude/local/claude", - ] - - for path in locations: - if path.exists() and path.is_file(): - return str(path) - - raise CLINotFoundError( - "Claude Code not found. Install with:\n" - " npm install -g @anthropic-ai/claude-code\n" - "\nIf already installed locally, try:\n" - ' export PATH="$HOME/node_modules/.bin:$PATH"\n' - "\nOr provide the path via ClaudeAgentOptions:\n" - " ClaudeAgentOptions(cli_path='/path/to/claude')" - ) - - def _find_bundled_cli(self) -> str | None: - """Find bundled CLI binary if it exists.""" - # Determine the CLI binary name based on platform - cli_name = "claude.exe" if platform.system() == "Windows" else "claude" - - # Get the path to the bundled CLI - # The _bundled directory is in the same package as this module - bundled_path = Path(__file__).parent.parent.parent / "_bundled" / cli_name - - if bundled_path.exists() and bundled_path.is_file(): - logger.info(f"Using bundled Claude Code CLI: {bundled_path}") - return str(bundled_path) - - return None - - def _build_settings_value(self) -> str | None: - """Build settings value, merging sandbox settings if provided. - - Returns the settings value as either: - - A JSON string (if sandbox is provided or settings is JSON) - - A file path (if only settings path is provided without sandbox) - - None if neither settings nor sandbox is provided - """ - has_settings = self._options.settings is not None - has_sandbox = self._options.sandbox is not None - - if not has_settings and not has_sandbox: - return None - - # If only settings path and no sandbox, pass through as-is - if has_settings and not has_sandbox: - return self._options.settings - - # If we have sandbox settings, we need to merge into a JSON object - settings_obj: dict[str, Any] = {} - - if has_settings: - assert self._options.settings is not None - settings_str = self._options.settings.strip() - # Check if settings is a JSON string or a file path - if settings_str.startswith("{") and settings_str.endswith("}"): - # Parse JSON string - try: - settings_obj = json.loads(settings_str) - except json.JSONDecodeError: - # If parsing fails, treat as file path - logger.warning( - f"Failed to parse settings as JSON, treating as file path: {settings_str}" - ) - # Read the file - settings_path = Path(settings_str) - if settings_path.exists(): - with settings_path.open(encoding="utf-8") as f: - settings_obj = json.load(f) - else: - # It's a file path - read and parse - settings_path = Path(settings_str) - if settings_path.exists(): - with settings_path.open(encoding="utf-8") as f: - settings_obj = json.load(f) - else: - logger.warning(f"Settings file not found: {settings_path}") - - # Merge sandbox settings - if has_sandbox: - settings_obj["sandbox"] = self._options.sandbox - - return json.dumps(settings_obj) - - def _apply_skills_defaults( - self, - ) -> tuple[list[str], list[str] | None]: - """Compute effective allowed_tools and setting_sources for skills. - - When ``options.skills`` is ``"all"``, injects the bare ``Skill`` tool; - when it is a list, injects ``Skill(name)`` for each entry. In either - case ``setting_sources`` defaults to ``["user", "project"]`` when - unset so the CLI discovers installed skills without the caller having - to wire up both options manually. ``None`` is a no-op. - - Does not mutate the original options object. - """ - allowed_tools: list[str] = list(self._options.allowed_tools) - setting_sources: list[str] | None = ( - list(self._options.setting_sources) - if self._options.setting_sources is not None - else None - ) - - skills = self._options.skills - if skills is None: - return allowed_tools, setting_sources - - if skills == "all": - if "Skill" not in allowed_tools: - allowed_tools.append("Skill") - else: - for name in skills: - pattern = f"Skill({name})" - if pattern not in allowed_tools: - allowed_tools.append(pattern) - - if setting_sources is None: - setting_sources = ["user", "project"] - - return allowed_tools, setting_sources - - def _build_command(self) -> list[str]: - """Build CLI command with arguments.""" - if self._cli_path is None: - raise CLINotFoundError("CLI path not resolved. Call connect() first.") - cmd = [self._cli_path, "--output-format", "stream-json", "--verbose"] - - if self._options.system_prompt is None: - cmd.extend(["--system-prompt", ""]) - elif isinstance(self._options.system_prompt, str): - cmd.extend(["--system-prompt", self._options.system_prompt]) - else: - sp = self._options.system_prompt - if sp.get("type") == "file": - cmd.extend(["--system-prompt-file", cast(SystemPromptFile, sp)["path"]]) - elif sp.get("type") == "preset" and "append" in sp: - cmd.extend( - ["--append-system-prompt", cast(SystemPromptPreset, sp)["append"]] - ) - - # Handle tools option (base set of tools) - if self._options.tools is not None: - tools = self._options.tools - if isinstance(tools, list): - if len(tools) == 0: - cmd.extend(["--tools", ""]) - else: - cmd.extend(["--tools", ",".join(tools)]) - else: - # Preset object - 'claude_code' preset maps to 'default' - cmd.extend(["--tools", "default"]) - - effective_allowed_tools, effective_setting_sources = ( - self._apply_skills_defaults() - ) - - if effective_allowed_tools: - cmd.extend(["--allowedTools", ",".join(effective_allowed_tools)]) - - if self._options.max_turns: - cmd.extend(["--max-turns", str(self._options.max_turns)]) - - if self._options.max_budget_usd is not None: - cmd.extend(["--max-budget-usd", str(self._options.max_budget_usd)]) - - if self._options.disallowed_tools: - cmd.extend(["--disallowedTools", ",".join(self._options.disallowed_tools)]) - - if self._options.task_budget is not None: - cmd.extend(["--task-budget", str(self._options.task_budget["total"])]) - - if self._options.model: - cmd.extend(["--model", self._options.model]) - - if self._options.fallback_model: - cmd.extend(["--fallback-model", self._options.fallback_model]) - - if self._options.betas: - cmd.extend(["--betas", ",".join(self._options.betas)]) - - if self._options.permission_prompt_tool_name: - cmd.extend( - ["--permission-prompt-tool", self._options.permission_prompt_tool_name] - ) - - if self._options.permission_mode: - cmd.extend(["--permission-mode", self._options.permission_mode]) - - if self._options.continue_conversation: - cmd.append("--continue") - - if self._options.resume: - cmd.extend(["--resume", self._options.resume]) - - if self._options.session_id: - cmd.extend(["--session-id", self._options.session_id]) - - # Handle settings and sandbox: merge sandbox into settings if both are provided - settings_value = self._build_settings_value() - if settings_value: - cmd.extend(["--settings", settings_value]) - - if self._options.add_dirs: - # Convert all paths to strings and add each directory - for directory in self._options.add_dirs: - cmd.extend(["--add-dir", str(directory)]) - - if self._options.mcp_servers: - if isinstance(self._options.mcp_servers, dict): - # Process all servers, stripping instance field from SDK servers - servers_for_cli: dict[str, Any] = {} - for name, config in self._options.mcp_servers.items(): - if isinstance(config, dict) and config.get("type") == "sdk": - # For SDK servers, pass everything except the instance field - sdk_config: dict[str, object] = { - k: v for k, v in config.items() if k != "instance" - } - servers_for_cli[name] = sdk_config - else: - # For external servers, pass as-is - servers_for_cli[name] = config - - # Pass all servers to CLI - if servers_for_cli: - cmd.extend( - [ - "--mcp-config", - json.dumps({"mcpServers": servers_for_cli}), - ] - ) - else: - # String or Path format: pass directly as file path or JSON string - cmd.extend(["--mcp-config", str(self._options.mcp_servers)]) - - if self._options.include_partial_messages: - cmd.append("--include-partial-messages") - - if self._options.fork_session: - cmd.append("--fork-session") - - if self._options.session_store is not None: - cmd.append("--session-mirror") - - # Agents are always sent via initialize request (matching TypeScript SDK) - # No --agents CLI flag needed - - if effective_setting_sources is not None: - cmd.append(f"--setting-sources={','.join(effective_setting_sources)}") - - # Add plugin directories - if self._options.plugins: - for plugin in self._options.plugins: - if plugin["type"] == "local": - cmd.extend(["--plugin-dir", plugin["path"]]) - else: - raise ValueError(f"Unsupported plugin type: {plugin['type']}") - - # Add extra args for future CLI flags - for flag, value in self._options.extra_args.items(): - if value is None: - # Boolean flag without value - cmd.append(f"--{flag}") - else: - # Flag with value - cmd.extend([f"--{flag}", str(value)]) - - # Resolve thinking config -> --thinking / --max-thinking-tokens - # `thinking` takes precedence over the deprecated `max_thinking_tokens` - if self._options.thinking is not None: - t = self._options.thinking - if t["type"] == "adaptive": - cmd.extend(["--thinking", "adaptive"]) - elif t["type"] == "enabled": - cmd.extend(["--max-thinking-tokens", str(t["budget_tokens"])]) - elif t["type"] == "disabled": - cmd.extend(["--thinking", "disabled"]) - - # Narrow off the Disabled variant first so mypy knows `t["display"]` is a str - # rather than widening to `object` across the union. - if t["type"] != "disabled" and "display" in t: - cmd.extend(["--thinking-display", t["display"]]) - elif self._options.max_thinking_tokens is not None: - cmd.extend( - ["--max-thinking-tokens", str(self._options.max_thinking_tokens)] - ) - - if self._options.effort is not None: - cmd.extend(["--effort", self._options.effort]) - - # Extract schema from output_format structure if provided - # Expected: {"type": "json_schema", "schema": {...}} - if ( - self._options.output_format is not None - and isinstance(self._options.output_format, dict) - and self._options.output_format.get("type") == "json_schema" - ): - schema = self._options.output_format.get("schema") - if schema is not None: - cmd.extend(["--json-schema", json.dumps(schema)]) - - # Always use streaming mode with stdin (matching TypeScript SDK) - # This allows agents and other large configs to be sent via initialize request - cmd.extend(["--input-format", "stream-json"]) - - return cmd - - async def connect(self) -> None: - """Start subprocess.""" - if self._process: - return - - if self._cli_path is None: - self._cli_path = await anyio.to_thread.run_sync(self._find_cli) - - if not os.environ.get("CLAUDE_AGENT_SDK_SKIP_VERSION_CHECK"): - await self._check_claude_version() - - cmd = self._build_command() - try: - # Merge environment variables. CLAUDE_CODE_ENTRYPOINT defaults to - # sdk-py regardless of inherited process env; options.env can override - # it. CLAUDE_AGENT_SDK_VERSION is always set by the SDK. - # Filter out CLAUDECODE so SDK-spawned subprocesses don't think - # they're running inside a Claude Code parent (see #573). - inherited_env = {k: v for k, v in os.environ.items() if k != "CLAUDECODE"} - process_env = { - **inherited_env, - "CLAUDE_CODE_ENTRYPOINT": "sdk-py", - **self._options.env, - "CLAUDE_AGENT_SDK_VERSION": __version__, - } - - # Propagate active OTEL trace context to the CLI so its spans - # parent under the caller's distributed trace. No-op if - # opentelemetry-api is not installed or there's no active span. - try: - from opentelemetry import propagate - - carrier: dict[str, str] = {} - propagate.inject(carrier) - if "traceparent" in carrier: - # Active span present: scrub stale inherited W3C context - # (CI/k8s ambient env) before writing the fresh values, so - # an inherited TRACESTATE isn't paired with a new - # TRACEPARENT. Explicit ClaudeAgentOptions.env always wins. - # Gate on the traceparent key (not carrier truthiness) so a - # baggage-only / non-W3C carrier doesn't scrub a valid - # inherited TRACEPARENT. - for key in ("TRACEPARENT", "TRACESTATE"): - if key not in self._options.env: - process_env.pop(key, None) - for k, v in carrier.items(): - key = k.upper() - if key not in self._options.env: - process_env[key] = v - except Exception: # noqa: BLE001 - best-effort tracing must never break connect() - logger.debug("OTEL trace context injection failed", exc_info=True) - - # Enable file checkpointing if requested - if self._options.enable_file_checkpointing: - process_env["CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING"] = "true" - - if self._cwd: - process_env["PWD"] = self._cwd - - # Pipe stderr only when the caller registered a callback. - stderr_dest = PIPE if self._options.stderr is not None else None - - self._process = await anyio.open_process( - cmd, - stdin=PIPE, - stdout=PIPE, - stderr=stderr_dest, - cwd=self._cwd, - env=process_env, - user=self._options.user, - ) - - if self._process.stdout: - self._stdout_stream = TextReceiveStream(self._process.stdout) - - # Setup stderr stream if piped - if stderr_dest is PIPE and self._process.stderr: - self._stderr_stream = TextReceiveStream(self._process.stderr) - # Spawn the stderr reader via spawn_detached (not a manually- - # entered TaskGroup) so cleanup has no trio task-affinity — - # same pattern as Query._read_task. - self._stderr_task = spawn_detached(self._handle_stderr()) - - # Setup stdin for streaming (always used now) - if self._process.stdin: - self._stdin_stream = TextSendStream(self._process.stdin) - - self._ready = True - - except FileNotFoundError as e: - # Check if the error comes from the working directory or the CLI - if self._cwd and not Path(self._cwd).exists(): - error = CLIConnectionError( - f"Working directory does not exist: {self._cwd}" - ) - self._exit_error = error - raise error from e - error = CLINotFoundError(f"Claude Code not found at: {self._cli_path}") - self._exit_error = error - raise error from e - except Exception as e: - error = CLIConnectionError(f"Failed to start Claude Code: {e}") - self._exit_error = error - raise error from e - - async def _handle_stderr(self) -> None: - """Handle stderr stream - read and invoke callbacks.""" - if not self._stderr_stream: - return - - try: - async for line in self._stderr_stream: - line_str = line.rstrip() - if not line_str: - continue - - # Call the stderr callback if provided - if self._options.stderr: - self._options.stderr(line_str) - except anyio.ClosedResourceError: - pass # Stream closed, exit normally - except Exception: - pass # Ignore other errors during stderr reading - - async def close(self) -> None: - """Close the transport and clean up resources.""" - if not self._process: - self._ready = False - return - - # Cancel stderr reader if active - if self._stderr_task is not None and not self._stderr_task.done(): - self._stderr_task.cancel() - with suppress(Exception): - await self._stderr_task.wait() - self._stderr_task = None - - # Close stdin stream (acquire lock to prevent race with concurrent writes) - async with self._write_lock: - self._ready = False # Set inside lock to prevent TOCTOU with write() - if self._stdin_stream: - with suppress(Exception): - await self._stdin_stream.aclose() - self._stdin_stream = None - - if self._stderr_stream: - with suppress(Exception): - await self._stderr_stream.aclose() - self._stderr_stream = None - - # Wait for graceful shutdown after stdin EOF, then terminate if needed. - # The subprocess needs time to flush its session file after receiving - # EOF on stdin. Without this grace period, SIGTERM can interrupt the - # write and cause the last assistant message to be lost (see #625). - if self._process.returncode is None: - try: - with anyio.fail_after(5): - await self._process.wait() - except TimeoutError: - # Graceful shutdown timed out — force terminate - with suppress(ProcessLookupError): - self._process.terminate() - try: - with anyio.fail_after(5): - await self._process.wait() - except TimeoutError: - # SIGTERM handler blocked — force kill (SIGKILL) - with suppress(ProcessLookupError): - self._process.kill() - with suppress(Exception): - await self._process.wait() - - self._process = None - self._stdout_stream = None - self._stdin_stream = None - self._stderr_stream = None - self._exit_error = None - - async def write(self, data: str) -> None: - """Write raw data to the transport.""" - async with self._write_lock: - # All checks inside lock to prevent TOCTOU races with close()/end_input() - if not self._ready or not self._stdin_stream: - raise CLIConnectionError("ProcessTransport is not ready for writing") - - if self._process and self._process.returncode is not None: - raise CLIConnectionError( - f"Cannot write to terminated process (exit code: {self._process.returncode})" - ) - - if self._exit_error: - raise CLIConnectionError( - f"Cannot write to process that exited with error: {self._exit_error}" - ) from self._exit_error - - try: - await self._stdin_stream.send(data) - except Exception as e: - self._ready = False - self._exit_error = CLIConnectionError( - f"Failed to write to process stdin: {e}" - ) - raise self._exit_error from e - - async def end_input(self) -> None: - """End the input stream (close stdin).""" - async with self._write_lock: - if self._stdin_stream: - with suppress(Exception): - await self._stdin_stream.aclose() - self._stdin_stream = None - - def read_messages(self) -> AsyncIterator[dict[str, Any]]: - """Read and parse messages from the transport.""" - return self._read_messages_impl() - - async def _read_messages_impl(self) -> AsyncIterator[dict[str, Any]]: - """Internal implementation of read_messages.""" - if not self._process or not self._stdout_stream: - raise CLIConnectionError("Not connected") - - json_buffer = "" - - # Process stdout messages - try: - async for line in self._stdout_stream: - line_str = line.strip() - if not line_str: - continue - - # Accumulate partial JSON until we can parse it - # Note: TextReceiveStream can truncate long lines, so we need to buffer - # and speculatively parse until we get a complete JSON object - json_lines = line_str.split("\n") - - for json_line in json_lines: - json_line = json_line.strip() - if not json_line: - continue - - # Skip non-JSON lines (e.g. [SandboxDebug]) when not - # mid-parse — they corrupt the buffer otherwise (#347). - if not json_buffer and not json_line.startswith("{"): - logger.debug( - "Skipping non-JSON line from CLI stdout: %s", - json_line[:200], - ) - continue - - # Keep accumulating partial JSON until we can parse it - json_buffer += json_line - - if len(json_buffer) > self._max_buffer_size: - buffer_length = len(json_buffer) - json_buffer = "" - raise SDKJSONDecodeError( - f"JSON message exceeded maximum buffer size of {self._max_buffer_size} bytes", - ValueError( - f"Buffer size {buffer_length} exceeds limit {self._max_buffer_size}" - ), - ) - - try: - data = json.loads(json_buffer) - json_buffer = "" - yield data - except json.JSONDecodeError: - # We are speculatively decoding the buffer until we get - # a full JSON object. If there is an actual issue, we - # raise an error after exceeding the configured limit. - continue - - except anyio.ClosedResourceError: - pass - except GeneratorExit: - # Client disconnected - pass - - # Check process completion and handle errors - try: - returncode = await self._process.wait() - except Exception: - returncode = -1 - - # Use exit code for error detection - if returncode is not None and returncode != 0: - self._exit_error = ProcessError( - f"Command failed with exit code {returncode}", - exit_code=returncode, - stderr="Check stderr output for details", - ) - raise self._exit_error - - async def _check_claude_version(self) -> None: - """Check Claude Code version and warn if below minimum.""" - if self._cli_path is None: - raise CLINotFoundError("CLI path not resolved. Call connect() first.") - version_process = None - try: - with anyio.fail_after(2): # 2 second timeout - version_process = await anyio.open_process( - [self._cli_path, "-v"], - stdout=PIPE, - stderr=PIPE, - ) - - if version_process.stdout: - stdout_bytes = await version_process.stdout.receive() - version_output = stdout_bytes.decode().strip() - - match = re.match(r"([0-9]+\.[0-9]+\.[0-9]+)", version_output) - if match: - version = match.group(1) - version_parts = [int(x) for x in version.split(".")] - min_parts = [ - int(x) for x in MINIMUM_CLAUDE_CODE_VERSION.split(".") - ] - - if version_parts < min_parts: - logger.warning( - "Claude Code version %s at %s is unsupported in the Agent SDK. " - "Minimum required version is %s. " - "Some features may not work correctly.", - version, - self._cli_path, - MINIMUM_CLAUDE_CODE_VERSION, - ) - except Exception: - pass - finally: - if version_process: - with suppress(Exception): - version_process.terminate() - with suppress(Exception): - await version_process.wait() - - def is_ready(self) -> bool: - """Check if transport is ready for communication.""" - return self._ready diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_version.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/_version.py deleted file mode 100644 index 6425d685..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/_version.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Version information for claude-agent-sdk.""" - -__version__ = "0.1.70" diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/client.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/client.py deleted file mode 100644 index 25d4d353..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/client.py +++ /dev/null @@ -1,625 +0,0 @@ -"""Claude SDK Client for interacting with Claude Code.""" - -import json -import os -from collections.abc import AsyncIterable, AsyncIterator -from dataclasses import asdict, replace -from typing import TYPE_CHECKING, Any - -from . import Transport -from ._errors import CLIConnectionError - -if TYPE_CHECKING: - from ._internal.session_resume import MaterializedResume -from .types import ( - ClaudeAgentOptions, - ContextUsageResponse, - HookEvent, - HookMatcher, - McpStatusResponse, - Message, - PermissionMode, - ResultMessage, -) - - -class ClaudeSDKClient: - """ - Client for bidirectional, interactive conversations with Claude Code. - - This client provides full control over the conversation flow with support - for streaming, interrupts, and dynamic message sending. For simple one-shot - queries, consider using the query() function instead. - - Key features: - - **Bidirectional**: Send and receive messages at any time - - **Stateful**: Maintains conversation context across messages - - **Interactive**: Send follow-ups based on responses - - **Control flow**: Support for interrupts and session management - - When to use ClaudeSDKClient: - - Building chat interfaces or conversational UIs - - Interactive debugging or exploration sessions - - Multi-turn conversations with context - - When you need to react to Claude's responses - - Real-time applications with user input - - When you need interrupt capabilities - - When to use query() instead: - - Simple one-off questions - - Batch processing of prompts - - Fire-and-forget automation scripts - - When all inputs are known upfront - - Stateless operations - - See examples/streaming_mode.py for full examples of ClaudeSDKClient in - different scenarios. - - Caveat: As of v0.0.20, you cannot use a ClaudeSDKClient instance across - different async runtime contexts (e.g., different trio nurseries or asyncio - task groups). The client internally maintains a persistent anyio task group - for reading messages that remains active from connect() until disconnect(). - This means you must complete all operations with the client within the same - async context where it was connected. Ideally, this limitation should not - exist. - """ - - def __init__( - self, - options: ClaudeAgentOptions | None = None, - transport: Transport | None = None, - ): - """Initialize Claude SDK client.""" - if options is None: - options = ClaudeAgentOptions() - self.options = options - self._custom_transport = transport - self._transport: Transport | None = None - self._query: Any | None = None - self._materialized: MaterializedResume | None = None - - def _convert_hooks_to_internal_format( - self, hooks: dict[HookEvent, list[HookMatcher]] - ) -> dict[str, list[dict[str, Any]]]: - """Convert HookMatcher format to internal Query format.""" - internal_hooks: dict[str, list[dict[str, Any]]] = {} - for event, matchers in hooks.items(): - internal_hooks[event] = [] - for matcher in matchers: - # Convert HookMatcher to internal dict format - internal_matcher: dict[str, Any] = { - "matcher": matcher.matcher if hasattr(matcher, "matcher") else None, - "hooks": matcher.hooks if hasattr(matcher, "hooks") else [], - } - if hasattr(matcher, "timeout") and matcher.timeout is not None: - internal_matcher["timeout"] = matcher.timeout - internal_hooks[event].append(internal_matcher) - return internal_hooks - - async def connect( - self, prompt: str | AsyncIterable[dict[str, Any]] | None = None - ) -> None: - """Connect to Claude with a prompt or message stream.""" - - from ._internal.session_resume import materialize_resume_session - from ._internal.session_store_validation import validate_session_store_options - - # Auto-connect with empty async iterable if no prompt is provided - async def _empty_stream() -> AsyncIterator[dict[str, Any]]: - # Never yields, but indicates that this function is an iterator and - # keeps the connection open. - # This yield is never reached but makes this an async generator - return - yield {} # type: ignore[unreachable] - - # String prompts are sent via transport.write() below, so the transport - # only needs an AsyncIterable (or an empty stream for None/str cases). - actual_prompt = prompt if isinstance(prompt, AsyncIterable) else _empty_stream() - - # Fail fast on invalid session_store option combinations before - # spawning the subprocess. - validate_session_store_options(self.options) - - # resume/continue + session_store: load the session from the store - # into a temp CLAUDE_CONFIG_DIR for the subprocess to resume from. - # When materialized, override resume/continue/env on a copy of options - # so the subprocess points at the temp dir; when None, fall through - # to normal handling (fresh session or local-disk resume). Skipped - # when a custom transport was supplied — the materialized options - # never reach a pre-constructed transport, so loading the store and - # writing .credentials.json to a temp dir would be wasted work. - self._materialized = ( - await materialize_resume_session(self.options) - if self._custom_transport is None - else None - ) - try: - await self._connect_inner(prompt, actual_prompt) - except BaseException: - # If connect fails after the subprocess has spawned (e.g. at - # query.initialize()), close the subprocess/read task *before* - # removing the temp CLAUDE_CONFIG_DIR it points at. disconnect() - # already orders close() → cleanup() and is None-safe for - # pre-spawn failures, so reuse it here. - await self.disconnect() - raise - - async def _connect_inner( - self, - prompt: str | AsyncIterable[dict[str, Any]] | None, - actual_prompt: AsyncIterable[dict[str, Any]], - ) -> None: - from ._internal.query import Query - from ._internal.session_resume import ( - apply_materialized_options, - build_mirror_batcher, - ) - from ._internal.transport.subprocess_cli import SubprocessCLITransport - - # Validate and configure permission settings (matching TypeScript SDK logic) - if self.options.can_use_tool: - # canUseTool callback requires streaming mode (AsyncIterable prompt) - if isinstance(prompt, str): - raise ValueError( - "can_use_tool callback requires streaming mode. " - "Please provide prompt as an AsyncIterable instead of a string." - ) - - # canUseTool and permission_prompt_tool_name are mutually exclusive - if self.options.permission_prompt_tool_name: - raise ValueError( - "can_use_tool callback cannot be used with permission_prompt_tool_name. " - "Please use one or the other." - ) - - # Automatically set permission_prompt_tool_name to "stdio" for control protocol - options = replace(self.options, permission_prompt_tool_name="stdio") - else: - options = self.options - - if self._materialized is not None: - options = apply_materialized_options(options, self._materialized) - - # Use provided custom transport or create subprocess transport - if self._custom_transport: - self._transport = self._custom_transport - else: - self._transport = SubprocessCLITransport( - prompt=actual_prompt, - options=options, - ) - await self._transport.connect() - - # Extract SDK MCP servers from options - sdk_mcp_servers = {} - if self.options.mcp_servers and isinstance(self.options.mcp_servers, dict): - for name, config in self.options.mcp_servers.items(): - if isinstance(config, dict) and config.get("type") == "sdk": - sdk_mcp_servers[name] = config["instance"] # type: ignore[typeddict-item] - - # Calculate initialize timeout from CLAUDE_CODE_STREAM_CLOSE_TIMEOUT env var if set - # CLAUDE_CODE_STREAM_CLOSE_TIMEOUT is in milliseconds, convert to seconds - initialize_timeout_ms = int( - os.environ.get("CLAUDE_CODE_STREAM_CLOSE_TIMEOUT", "60000") - ) - initialize_timeout = max(initialize_timeout_ms / 1000.0, 60.0) - - # Extract exclude_dynamic_sections from preset system prompt for the - # initialize request (older CLIs ignore unknown initialize fields). - exclude_dynamic_sections: bool | None = None - sp = self.options.system_prompt - if isinstance(sp, dict) and sp.get("type") == "preset": - eds = sp.get("exclude_dynamic_sections") - if isinstance(eds, bool): - exclude_dynamic_sections = eds - - # Convert agents to dict format for initialize request - agents_dict: dict[str, dict[str, Any]] | None = None - if self.options.agents: - agents_dict = { - name: {k: v for k, v in asdict(agent_def).items() if v is not None} - for name, agent_def in self.options.agents.items() - } - - # Create Query to handle control protocol - self._query = Query( - transport=self._transport, - is_streaming_mode=True, # ClaudeSDKClient always uses streaming mode - can_use_tool=self.options.can_use_tool, - hooks=self._convert_hooks_to_internal_format(self.options.hooks) - if self.options.hooks - else None, - sdk_mcp_servers=sdk_mcp_servers, - initialize_timeout=initialize_timeout, - agents=agents_dict, - exclude_dynamic_sections=exclude_dynamic_sections, - skills=self.options.skills, - ) - - if self.options.session_store is not None: - q = self._query - - async def _on_mirror_error(key: Any, error: str) -> None: - q.report_mirror_error(key, error) - - self._query.set_transcript_mirror_batcher( - build_mirror_batcher( - store=self.options.session_store, - materialized=self._materialized, - env=self.options.env, - on_error=_on_mirror_error, - ) - ) - - # Start reading messages and initialize - await self._query.start() - await self._query.initialize() - - # If we have an initial prompt, send it - if isinstance(prompt, str): - message = { - "type": "user", - "message": {"role": "user", "content": prompt}, - "parent_tool_use_id": None, - "session_id": "default", - } - await self._transport.write(json.dumps(message) + "\n") - elif prompt is not None and isinstance(prompt, AsyncIterable): - self._query.spawn_task(self._query.stream_input(prompt)) - - async def receive_messages(self) -> AsyncIterator[Message]: - """Receive all messages from Claude.""" - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - - from ._internal.message_parser import parse_message - - async for data in self._query.receive_messages(): - message = parse_message(data) - if message is not None: - yield message - - async def query( - self, prompt: str | AsyncIterable[dict[str, Any]], session_id: str = "default" - ) -> None: - """ - Send a new request in streaming mode. - - Args: - prompt: Either a string message or an async iterable of message dictionaries - session_id: Session identifier for the conversation - """ - if not self._query or not self._transport: - raise CLIConnectionError("Not connected. Call connect() first.") - - # Handle string prompts - if isinstance(prompt, str): - message = { - "type": "user", - "message": {"role": "user", "content": prompt}, - "parent_tool_use_id": None, - "session_id": session_id, - } - await self._transport.write(json.dumps(message) + "\n") - else: - # Handle AsyncIterable prompts - stream them - async for msg in prompt: - # Ensure session_id is set on each message - if "session_id" not in msg: - msg["session_id"] = session_id - await self._transport.write(json.dumps(msg) + "\n") - - async def interrupt(self) -> None: - """Send interrupt signal (only works with streaming mode).""" - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - await self._query.interrupt() - - async def set_permission_mode(self, mode: PermissionMode) -> None: - """Change permission mode during conversation (only works with streaming mode). - - Args: - mode: The permission mode to set. Valid options: - - 'default': CLI prompts for dangerous tools - - 'acceptEdits': Auto-accept file edits - - 'plan': Plan-only mode (no tool execution) - - 'bypassPermissions': Allow all tools (use with caution) - - 'dontAsk': Deny anything not pre-approved by allow rules - - 'auto': A model classifier approves or denies each tool call - - Example: - ```python - async with ClaudeSDKClient() as client: - # Start with default permissions - await client.query("Help me analyze this codebase") - - # Review mode done, switch to auto-accept edits - await client.set_permission_mode('acceptEdits') - await client.query("Now implement the fix we discussed") - ``` - """ - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - await self._query.set_permission_mode(mode) - - async def set_model(self, model: str | None = None) -> None: - """Change the AI model during conversation (only works with streaming mode). - - Args: - model: The model to use, or None to use default. Examples: - - 'claude-sonnet-4-5' - - 'claude-opus-4-1-20250805' - - 'claude-opus-4-20250514' - - Example: - ```python - async with ClaudeSDKClient() as client: - # Start with default model - await client.query("Help me understand this problem") - - # Switch to a different model for implementation - await client.set_model('claude-sonnet-4-5') - await client.query("Now implement the solution") - ``` - """ - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - await self._query.set_model(model) - - async def rewind_files(self, user_message_id: str) -> None: - """Rewind tracked files to their state at a specific user message. - - Requires: - - `enable_file_checkpointing=True` to track file changes - - `extra_args={"replay-user-messages": None}` to receive UserMessage - objects with `uuid` in the response stream - - Args: - user_message_id: UUID of the user message to rewind to. This should be - the `uuid` field from a `UserMessage` received during the conversation. - - Example: - ```python - options = ClaudeAgentOptions( - enable_file_checkpointing=True, - extra_args={"replay-user-messages": None}, - ) - async with ClaudeSDKClient(options) as client: - await client.query("Make some changes to my files") - async for msg in client.receive_response(): - if isinstance(msg, UserMessage) and msg.uuid: - checkpoint_id = msg.uuid # Save this for later - - # Later, rewind to that point - await client.rewind_files(checkpoint_id) - ``` - """ - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - await self._query.rewind_files(user_message_id) - - async def reconnect_mcp_server(self, server_name: str) -> None: - """Reconnect a disconnected or failed MCP server (only works with streaming mode). - - Use this to retry connecting to an MCP server that failed to connect - or was disconnected. Raises an exception if the reconnection fails. - - Args: - server_name: The name of the MCP server to reconnect - - Example: - ```python - async with ClaudeSDKClient(options) as client: - status = await client.get_mcp_status() - for server in status.get("mcpServers", []): - if server["status"] == "failed": - await client.reconnect_mcp_server(server["name"]) - ``` - """ - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - await self._query.reconnect_mcp_server(server_name) - - async def toggle_mcp_server(self, server_name: str, enabled: bool) -> None: - """Enable or disable an MCP server (only works with streaming mode). - - Disabling a server disconnects it and removes its tools from the - available tool set. Enabling a server reconnects it and makes its - tools available again. Raises an exception on failure. - - Args: - server_name: The name of the MCP server to toggle - enabled: True to enable the server, False to disable it - - Example: - ```python - async with ClaudeSDKClient(options) as client: - # Temporarily disable a server - await client.toggle_mcp_server("my-server", enabled=False) - await client.query("Do something without my-server tools") - - # Re-enable it later - await client.toggle_mcp_server("my-server", enabled=True) - ``` - """ - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - await self._query.toggle_mcp_server(server_name, enabled) - - async def stop_task(self, task_id: str) -> None: - """Stop a running task (only works with streaming mode). - - After this resolves, a `task_notification` system message with - status `'stopped'` will be emitted by the CLI in the message stream. - - Args: - task_id: The task ID from `task_notification` events. - - Example: - ```python - async with ClaudeSDKClient() as client: - await client.query("Start a long-running task") - - # Listen for task_notification to get task_id, then: - await client.stop_task("task-abc123") - # A task_notification with status 'stopped' will follow - ``` - """ - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - await self._query.stop_task(task_id) - - async def get_mcp_status(self) -> McpStatusResponse: - """Get current MCP server connection status (only works with streaming mode). - - Queries the Claude Code CLI for the live connection status of all - configured MCP servers. - - Returns: - McpStatusResponse dictionary with an 'mcpServers' key containing - a list of McpServerStatus entries. Each entry includes: - - 'name': Server name (str) - - 'status': Connection status ('connected', 'pending', 'failed', - 'needs-auth', 'disabled') - - 'serverInfo': MCP server name/version (when connected) - - 'error': Error message (when status is 'failed') - - 'config': Server configuration (stdio/sse/http/sdk/claudeai-proxy) - - 'scope': Configuration scope (e.g., project, user, local) - - 'tools': List of tools provided by the server (when connected) - - Example: - ```python - async with ClaudeSDKClient(options) as client: - status = await client.get_mcp_status() - for server in status["mcpServers"]: - print(f"{server['name']}: {server['status']}") - if server["status"] == "failed": - print(f" Error: {server.get('error')}") - ``` - """ - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - result: McpStatusResponse = await self._query.get_mcp_status() - return result - - async def get_context_usage(self) -> ContextUsageResponse: - """Get a breakdown of current context window usage by category. - - Returns the same data shown by the `/context` command in the CLI, - including token counts per category, total usage, and detailed - breakdowns of MCP tools, memory files, and agents. - - Returns: - ContextUsageResponse dictionary with keys including: - - 'categories': List of categories with name, tokens, color - - 'totalTokens': Total tokens in context - - 'maxTokens': Effective context limit - - 'percentage': Percent of context used (0-100) - - 'model': Model the usage is calculated for - - 'mcpTools': Per-tool token breakdown for MCP servers - - 'memoryFiles': Per-file token breakdown for CLAUDE.md files - - 'agents': Per-agent token breakdown - - Example: - ```python - async with ClaudeSDKClient() as client: - await client.query("Read this file") - async for _ in client.receive_response(): - pass - - usage = await client.get_context_usage() - print(f"Using {usage['percentage']:.1f}% of context") - for cat in usage['categories']: - print(f" {cat['name']}: {cat['tokens']} tokens") - ``` - """ - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - result: ContextUsageResponse = await self._query.get_context_usage() - return result - - async def get_server_info(self) -> dict[str, Any] | None: - """Get server initialization info including available commands and output styles. - - Returns initialization information from the Claude Code server including: - - Available commands (slash commands, system commands, etc.) - - Current and available output styles - - Server capabilities - - Returns: - Dictionary with server info, or None if not in streaming mode - - Example: - ```python - async with ClaudeSDKClient() as client: - info = await client.get_server_info() - if info: - print(f"Commands available: {len(info.get('commands', []))}") - print(f"Output style: {info.get('output_style', 'default')}") - ``` - """ - if not self._query: - raise CLIConnectionError("Not connected. Call connect() first.") - # Return the initialization result that was already obtained during connect - return getattr(self._query, "_initialization_result", None) - - async def receive_response(self) -> AsyncIterator[Message]: - """ - Receive messages from Claude until and including a ResultMessage. - - This async iterator yields all messages in sequence and automatically terminates - after yielding a ResultMessage (which indicates the response is complete). - It's a convenience method over receive_messages() for single-response workflows. - - **Stopping Behavior:** - - Yields each message as it's received - - Terminates immediately after yielding a ResultMessage - - The ResultMessage IS included in the yielded messages - - If no ResultMessage is received, the iterator continues indefinitely - - Yields: - Message: Each message received (UserMessage, AssistantMessage, SystemMessage, ResultMessage) - - Example: - ```python - async with ClaudeSDKClient() as client: - await client.query("What's the capital of France?") - - async for msg in client.receive_response(): - if isinstance(msg, AssistantMessage): - for block in msg.content: - if isinstance(block, TextBlock): - print(f"Claude: {block.text}") - elif isinstance(msg, ResultMessage): - print(f"Cost: ${msg.total_cost_usd:.4f}") - # Iterator will terminate after this message - ``` - - Note: - To collect all messages: `messages = [msg async for msg in client.receive_response()]` - The final message in the list will always be a ResultMessage. - """ - async for message in self.receive_messages(): - yield message - if isinstance(message, ResultMessage): - return - - async def disconnect(self) -> None: - """Disconnect from Claude.""" - if self._query: - await self._query.close() - self._query = None - self._transport = None - if self._materialized is not None: - await self._materialized.cleanup() - self._materialized = None - - async def __aenter__(self) -> "ClaudeSDKClient": - """Enter async context - automatically connects with empty stream for interactive use.""" - await self.connect() - return self - - async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> bool: - """Exit async context - always disconnects.""" - await self.disconnect() - return False diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/py.typed b/.venv/lib/python3.12/site-packages/claude_agent_sdk/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/query.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/query.py deleted file mode 100644 index a91ed519..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/query.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Query function for one-shot interactions with Claude Code.""" - -from collections.abc import AsyncIterable, AsyncIterator -from typing import Any - -from ._internal.client import InternalClient -from ._internal.transport import Transport -from .types import ClaudeAgentOptions, Message - - -async def query( - *, - prompt: str | AsyncIterable[dict[str, Any]], - options: ClaudeAgentOptions | None = None, - transport: Transport | None = None, -) -> AsyncIterator[Message]: - """ - Query Claude Code for one-shot or unidirectional streaming interactions. - - This function is ideal for simple, stateless queries where you don't need - bidirectional communication or conversation management. For interactive, - stateful conversations, use ClaudeSDKClient instead. - - Key differences from ClaudeSDKClient: - - **Unidirectional**: Send all messages upfront, receive all responses - - **Stateless**: Each query is independent, no conversation state - - **Simple**: Fire-and-forget style, no connection management - - **No interrupts**: Cannot interrupt or send follow-up messages - - When to use query(): - - Simple one-off questions ("What is 2+2?") - - Batch processing of independent prompts - - Code generation or analysis tasks - - Automated scripts and CI/CD pipelines - - When you know all inputs upfront - - When to use ClaudeSDKClient: - - Interactive conversations with follow-ups - - Chat applications or REPL-like interfaces - - When you need to send messages based on responses - - When you need interrupt capabilities - - Long-running sessions with state - - Args: - prompt: The prompt to send to Claude. Can be a string for single-shot queries - or an AsyncIterable[dict] for streaming mode with continuous interaction. - In streaming mode, each dict should have the structure: - { - "type": "user", - "message": {"role": "user", "content": "..."}, - "parent_tool_use_id": None, - "session_id": "..." - } - options: Optional configuration (defaults to ClaudeAgentOptions() if None). - Set options.permission_mode to control tool execution: - - 'default': CLI prompts for dangerous tools - - 'acceptEdits': Auto-accept file edits - - 'plan': Plan-only mode (no tool execution) - - 'bypassPermissions': Allow all tools (use with caution) - - 'dontAsk': Deny anything not pre-approved by allow rules - - 'auto': A model classifier approves or denies each tool call - Set options.cwd for working directory. - transport: Optional transport implementation. If provided, this will be used - instead of the default transport selection based on options. - The transport will be automatically configured with the prompt and options. - - Yields: - Messages from the conversation - - Example - Simple query: - ```python - # One-off question - async for message in query(prompt="What is the capital of France?"): - print(message) - ``` - - Example - With options: - ```python - # Code generation with specific settings - async for message in query( - prompt="Create a Python web server", - options=ClaudeAgentOptions( - system_prompt="You are an expert Python developer", - cwd="/home/user/project" - ) - ): - print(message) - ``` - - Example - Streaming mode (still unidirectional): - ```python - async def prompts(): - yield {"type": "user", "message": {"role": "user", "content": "Hello"}} - yield {"type": "user", "message": {"role": "user", "content": "How are you?"}} - - # All prompts are sent, then all responses received - async for message in query(prompt=prompts()): - print(message) - ``` - - Example - With custom transport: - ```python - from claude_agent_sdk import query, Transport - - class MyCustomTransport(Transport): - # Implement custom transport logic - pass - - transport = MyCustomTransport() - async for message in query( - prompt="Hello", - transport=transport - ): - print(message) - ``` - - """ - if options is None: - options = ClaudeAgentOptions() - - client = InternalClient() - - async for message in client.process_query( - prompt=prompt, options=options, transport=transport - ): - yield message diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/testing/__init__.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/testing/__init__.py deleted file mode 100644 index 123cec69..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/testing/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -"""Test utilities for SDK extension authors. - -Importing this subpackage does not require ``pytest`` — assertions use plain -``assert`` so the harness works under any test runner. -""" - -from .session_store_conformance import run_session_store_conformance - -__all__ = ["run_session_store_conformance"] diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/testing/session_store_conformance.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/testing/session_store_conformance.py deleted file mode 100644 index 2b7f8a7f..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/testing/session_store_conformance.py +++ /dev/null @@ -1,327 +0,0 @@ -"""Shared conformance test suite for :class:`SessionStore` adapters. - -Call :func:`run_session_store_conformance` from an async test to assert the -14 behavioral contracts every adapter must satisfy. Tests for optional -methods (``list_sessions``, ``list_session_summaries``, ``delete``, -``list_subkeys``) are skipped when named in ``skip_optional`` or when the -store does not override that method. - -Example:: - - import pytest - from claude_agent_sdk.testing import run_session_store_conformance - - @pytest.mark.asyncio - async def test_my_store_conformance(): - await run_session_store_conformance(MyRedisStore) -""" - -from __future__ import annotations - -import inspect -import math -from collections.abc import Awaitable, Callable -from typing import Any - -from ..types import SessionKey, SessionStore - -OptionalMethod = ( - str # "list_sessions" | "list_session_summaries" | "delete" | "list_subkeys" -) -_OPTIONAL_METHODS: frozenset[str] = frozenset( - {"list_sessions", "list_session_summaries", "delete", "list_subkeys"} -) - -_KEY: SessionKey = {"project_key": "proj", "session_id": "sess"} - - -def _has_optional( - store: SessionStore, method: OptionalMethod, skip_optional: frozenset[str] -) -> bool: - """True if ``store`` supports ``method`` and it isn't explicitly skipped.""" - if method in skip_optional: - return False - impl = getattr(store, method, None) - if impl is None: - return False - # Distinguish a real override from the Protocol's default that raises - # NotImplementedError: an override lives on the instance's class, the - # default lives on SessionStore itself. - default = getattr(SessionStore, method, None) - return getattr(type(store), method, None) is not default - - -async def run_session_store_conformance( - make_store: Callable[[], SessionStore | Awaitable[SessionStore]], - *, - skip_optional: frozenset[str] = frozenset(), -) -> None: - """Assert the 14 :class:`SessionStore` behavioral contracts. - - ``make_store`` is invoked once per contract to provide isolation. It may be - sync or async. Contracts for optional methods (``list_sessions``, - ``list_session_summaries``, ``delete``, ``list_subkeys``) are skipped when - named in ``skip_optional`` or when the store does not override that method. - """ - invalid = skip_optional - _OPTIONAL_METHODS - assert not invalid, f"unknown optional methods in skip_optional: {invalid}" - - async def fresh() -> SessionStore: - result = make_store() - if inspect.isawaitable(result): - return await result - return result - - probe = await fresh() - has_list_sessions = _has_optional(probe, "list_sessions", skip_optional) - has_list_summaries = _has_optional(probe, "list_session_summaries", skip_optional) - has_delete = _has_optional(probe, "delete", skip_optional) - has_list_subkeys = _has_optional(probe, "list_subkeys", skip_optional) - - # --- Required: append + load ------------------------------------------- - - # 1. append then load returns same entries in same order - store = await fresh() - await store.append(_KEY, [_e({"uuid": "b", "n": 1}), _e({"uuid": "a", "n": 2})]) - loaded = await store.load(_KEY) - # Deep-equal is the contract; byte-equal serialization is intentionally - # NOT checked (Postgres JSONB may reorder keys — SDK never byte-compares). - assert loaded == [_e({"uuid": "b", "n": 1}), _e({"uuid": "a", "n": 2})] - - # 2. load unknown key returns None - store = await fresh() - assert await store.load({"project_key": "proj", "session_id": "nope"}) is None - await store.append(_KEY, [_e({"uuid": "x", "n": 1})]) - assert await store.load({**_KEY, "subpath": "nope"}) is None - - # 3. multiple append calls preserve call order - store = await fresh() - await store.append(_KEY, [_e({"uuid": "z", "n": 1})]) - await store.append(_KEY, [_e({"uuid": "a", "n": 2}), _e({"uuid": "m", "n": 3})]) - await store.append(_KEY, [_e({"uuid": "b", "n": 4})]) - assert await store.load(_KEY) == [ - _e({"uuid": "z", "n": 1}), - _e({"uuid": "a", "n": 2}), - _e({"uuid": "m", "n": 3}), - _e({"uuid": "b", "n": 4}), - ] - - # 4. append([]) is a no-op - store = await fresh() - await store.append(_KEY, [_e({"uuid": "a", "n": 1})]) - await store.append(_KEY, []) - assert await store.load(_KEY) == [_e({"uuid": "a", "n": 1})] - - # 5. subpath keys are stored independently of main - store = await fresh() - sub: SessionKey = {**_KEY, "subpath": "subagents/agent-1"} - await store.append(_KEY, [_e({"uuid": "m", "n": 1})]) - await store.append(sub, [_e({"uuid": "s", "n": 1})]) - assert await store.load(_KEY) == [_e({"uuid": "m", "n": 1})] - assert await store.load(sub) == [_e({"uuid": "s", "n": 1})] - - # 6. project_key isolation - store = await fresh() - await store.append({"project_key": "A", "session_id": "s1"}, [_e({"from": "A"})]) - await store.append({"project_key": "B", "session_id": "s1"}, [_e({"from": "B"})]) - assert await store.load({"project_key": "A", "session_id": "s1"}) == [ - _e({"from": "A"}) - ] - assert await store.load({"project_key": "B", "session_id": "s1"}) == [ - _e({"from": "B"}) - ] - if has_list_sessions: - assert len(await store.list_sessions("A")) == 1 - assert len(await store.list_sessions("B")) == 1 - - # --- Optional: list_sessions ------------------------------------------- - - if has_list_sessions: - # 7. list_sessions returns session_ids for project - store = await fresh() - await store.append({"project_key": "proj", "session_id": "a"}, [_e({"n": 1})]) - await store.append({"project_key": "proj", "session_id": "b"}, [_e({"n": 1})]) - await store.append({"project_key": "other", "session_id": "c"}, [_e({"n": 1})]) - sessions = await store.list_sessions("proj") - assert sorted(s["session_id"] for s in sessions) == ["a", "b"] - # mtime must be epoch-ms; >1e12 rules out epoch-seconds (≈2001 in ms). - assert all(math.isfinite(s["mtime"]) and s["mtime"] > 1e12 for s in sessions) - assert await store.list_sessions("never-appended-project") == [] - - # 8. list_sessions excludes subagent subpaths - store = await fresh() - await store.append( - {"project_key": "proj", "session_id": "main"}, [_e({"n": 1})] - ) - await store.append( - { - "project_key": "proj", - "session_id": "main", - "subpath": "subagents/agent-1", - }, - [_e({"n": 1})], - ) - sessions = await store.list_sessions("proj") - assert [s["session_id"] for s in sessions] == ["main"] - - # --- Optional: list_session_summaries ---------------------------------- - - if has_list_summaries: - # 14. list_session_summaries returns persisted fold output that - # round-trips through fold_session_summary again. Stores must NOT - # interpret ``data`` — only persist it verbatim. - from .._internal.session_summary import fold_session_summary - - store = await fresh() - key: SessionKey = {"project_key": "proj", "session_id": "summ-sess"} - await store.append( - key, - [ - _e({"timestamp": "2024-01-01T00:00:00.000Z", "customTitle": "first"}), - _e({"timestamp": "2024-01-01T00:00:01.000Z"}), - ], - ) - await store.append( - key, - [_e({"timestamp": "2024-01-01T00:00:02.000Z", "customTitle": "second"})], - ) - await store.append( - {"project_key": "other", "session_id": "elsewhere"}, - [_e({"timestamp": "2024-01-01T00:00:00.000Z"})], - ) - summaries = await store.list_session_summaries("proj") - by_id = {s["session_id"]: s for s in summaries} - assert set(by_id) == {"summ-sess"} - summ = by_id["summ-sess"] - # mtime must be epoch-ms; >1e12 rules out epoch-seconds. - assert math.isfinite(summ["mtime"]) and summ["mtime"] > 1e12 - # Clock alignment: sidecar mtime is storage write time (adapter- - # stamped at persist), and must share a clock with - # list_sessions().mtime for the same session. Adapters that derive - # sidecar mtime from entry ISO timestamps would report a strictly - # older value than list_sessions()'s storage-time mtime and make - # every sidecar look stale to the fast-path freshness check in - # list_sessions_from_store(); this assertion catches that. - if has_list_sessions: - ls_by_id = { - e["session_id"]: e["mtime"] for e in await store.list_sessions("proj") - } - assert summ["mtime"] >= ls_by_id["summ-sess"] - # data is opaque; the contract is that it round-trips into the fold. - assert isinstance(summ["data"], dict) - refolded = fold_session_summary( - summ, key, [_e({"timestamp": "2024-01-01T00:00:03.000Z"})] - ) - assert refolded["session_id"] == "summ-sess" - # The fold preserves prev["mtime"] verbatim — mtime is stamped by - # the adapter after persisting, not by the fold. - assert refolded["mtime"] == summ["mtime"] - # Subagent appends must NOT affect the main session's summary. - await store.append( - {**key, "subpath": "subagents/agent-1"}, - [_e({"timestamp": "2024-01-01T00:00:09.000Z", "customTitle": "subagent"})], - ) - after_sub = { - s["session_id"]: s for s in await store.list_session_summaries("proj") - } - assert after_sub["summ-sess"]["data"] == summ["data"] - assert await store.list_session_summaries("never-appended-project") == [] - if has_delete: - await store.delete(key) - assert await store.list_session_summaries("proj") == [] - - # --- Optional: delete -------------------------------------------------- - - if has_delete: - # 9. delete main then load returns None - store = await fresh() - await store.delete({"project_key": "proj", "session_id": "never-written"}) - await store.append(_KEY, [_e({"n": 1})]) - await store.delete(_KEY) - assert await store.load(_KEY) is None - - # 10. delete main cascades to subkeys - store = await fresh() - sub1: SessionKey = {**_KEY, "subpath": "subagents/agent-1"} - sub2: SessionKey = {**_KEY, "subpath": "subagents/agent-2"} - other: SessionKey = {"project_key": "proj", "session_id": "sess2"} - other_proj: SessionKey = { - "project_key": "other-proj", - "session_id": _KEY["session_id"], - } - await store.append(_KEY, [_e({"n": 1})]) - await store.append(sub1, [_e({"n": 1})]) - await store.append(sub2, [_e({"n": 1})]) - await store.append(other, [_e({"n": 1})]) - await store.append(other_proj, [_e({"n": 1})]) - - await store.delete(_KEY) - - assert await store.load(_KEY) is None - assert await store.load(sub1) is None - assert await store.load(sub2) is None - loaded_other = await store.load(other) - assert loaded_other is not None and len(loaded_other) == 1 - loaded_other_proj = await store.load(other_proj) - assert loaded_other_proj is not None and len(loaded_other_proj) == 1 - if has_list_subkeys: - assert await store.list_subkeys(_KEY) == [] - if has_list_sessions: - listed = await store.list_sessions(_KEY["project_key"]) - assert _KEY["session_id"] not in [s["session_id"] for s in listed] - - # 11. delete with subpath removes only that subkey - store = await fresh() - await store.append(_KEY, [_e({"n": 1})]) - await store.append(sub1, [_e({"n": 1})]) - await store.append(sub2, [_e({"n": 1})]) - - await store.delete(sub1) - - assert await store.load(sub1) is None - loaded_sub2 = await store.load(sub2) - assert loaded_sub2 is not None and len(loaded_sub2) == 1 - loaded_main = await store.load(_KEY) - assert loaded_main is not None and len(loaded_main) == 1 - if has_list_subkeys: - assert await store.list_subkeys(_KEY) == ["subagents/agent-2"] - - # --- Optional: list_subkeys -------------------------------------------- - - if has_list_subkeys: - # 12. list_subkeys returns subpaths - store = await fresh() - await store.append(_KEY, [_e({"n": 1})]) - await store.append({**_KEY, "subpath": "subagents/agent-1"}, [_e({"n": 1})]) - await store.append({**_KEY, "subpath": "subagents/agent-2"}, [_e({"n": 1})]) - await store.append( - { - "project_key": _KEY["project_key"], - "session_id": "other-sess", - "subpath": "subagents/agent-x", - }, - [_e({"n": 1})], - ) - subkeys = await store.list_subkeys(_KEY) - assert sorted(subkeys) == ["subagents/agent-1", "subagents/agent-2"] - assert "subagents/agent-x" not in subkeys - - # 13. list_subkeys excludes main transcript - store = await fresh() - await store.append(_KEY, [_e({"n": 1})]) - assert await store.list_subkeys(_KEY) == [] - assert ( - await store.list_subkeys( - {"project_key": "proj", "session_id": "never-appended"} - ) - == [] - ) - - -def _e(d: dict[str, Any]) -> Any: - """Build a test entry satisfying ``SessionStoreEntry`` (``type`` is required). - - Adapters must treat entries as opaque pass-through blobs; the value of - ``type`` is irrelevant to the contracts under test. - """ - return {"type": "x", **d} diff --git a/.venv/lib/python3.12/site-packages/claude_agent_sdk/types.py b/.venv/lib/python3.12/site-packages/claude_agent_sdk/types.py deleted file mode 100644 index b4860270..00000000 --- a/.venv/lib/python3.12/site-packages/claude_agent_sdk/types.py +++ /dev/null @@ -1,1870 +0,0 @@ -"""Type definitions for Claude SDK.""" - -import sys -from collections.abc import Awaitable, Callable -from dataclasses import dataclass, field -from pathlib import Path -from typing import TYPE_CHECKING, Any, Literal, Protocol - -if sys.version_info >= (3, 11): - from typing import NotRequired, Required, TypedDict -else: - # PEP 655: stdlib TypedDict on 3.10 doesn't process NotRequired/Required, - # so __required_keys__ would be wrong. typing_extensions backports the - # correct behavior. - from typing_extensions import NotRequired, Required, TypedDict - -if TYPE_CHECKING: - from mcp.server import Server as McpServer -else: - # Runtime placeholder for forward reference resolution in Pydantic 2.12+ - McpServer = Any - -# Permission modes -PermissionMode = Literal[ - "default", "acceptEdits", "plan", "bypassPermissions", "dontAsk", "auto" -] - -# SDK Beta features - see https://docs.anthropic.com/en/api/beta-headers -SdkBeta = Literal["context-1m-2025-08-07"] - -# Agent definitions -SettingSource = Literal["user", "project", "local"] - - -class SystemPromptPreset(TypedDict): - """System prompt preset configuration.""" - - type: Literal["preset"] - preset: Literal["claude_code"] - append: NotRequired[str] - exclude_dynamic_sections: NotRequired[bool] - """Strip per-user dynamic sections (working directory, auto-memory, git - status) from the system prompt so it stays static and cacheable across - users. The stripped content is re-injected into the first user message - so the model still has access to it. - - Use this when many users share the same preset system prompt and you - want the prompt-caching prefix to hit cross-user. - - Requires a Claude Code CLI version that supports this option; older - CLIs silently ignore it. - """ - - -class SystemPromptFile(TypedDict): - """System prompt file configuration.""" - - type: Literal["file"] - path: str - - -class TaskBudget(TypedDict): - """API-side task budget in tokens. - - When set, the model is made aware of its remaining token budget so it can - pace tool use and wrap up before the limit. Sent as - ``output_config.task_budget`` with the ``task-budgets-2026-03-13`` beta - header. - """ - - total: int - - -class ToolsPreset(TypedDict): - """Tools preset configuration.""" - - type: Literal["preset"] - preset: Literal["claude_code"] - - -@dataclass -class AgentDefinition: - """Agent definition configuration.""" - - description: str - prompt: str - tools: list[str] | None = None - disallowedTools: list[str] | None = None # noqa: N815 - # Model alias ("sonnet", "opus", "haiku", "inherit") or a full model ID. - model: str | None = None - skills: list[str] | None = None - memory: Literal["user", "project", "local"] | None = None - # Each entry is a server name (str) or an inline {name: config} dict. - mcpServers: list[str | dict[str, Any]] | None = None # noqa: N815 - initialPrompt: str | None = None # noqa: N815 - maxTurns: int | None = None # noqa: N815 - background: bool | None = None - effort: Literal["low", "medium", "high", "max"] | int | None = None - permissionMode: PermissionMode | None = None # noqa: N815 - - -# Permission Update types (matching TypeScript SDK) -PermissionUpdateDestination = Literal[ - "userSettings", "projectSettings", "localSettings", "session" -] - -PermissionBehavior = Literal["allow", "deny", "ask"] - - -@dataclass -class PermissionRuleValue: - """Permission rule value.""" - - tool_name: str - rule_content: str | None = None - - -@dataclass -class PermissionUpdate: - """Permission update configuration.""" - - type: Literal[ - "addRules", - "replaceRules", - "removeRules", - "setMode", - "addDirectories", - "removeDirectories", - ] - rules: list[PermissionRuleValue] | None = None - behavior: PermissionBehavior | None = None - mode: PermissionMode | None = None - directories: list[str] | None = None - destination: PermissionUpdateDestination | None = None - - def to_dict(self) -> dict[str, Any]: - """Convert PermissionUpdate to dictionary format matching TypeScript control protocol.""" - result: dict[str, Any] = { - "type": self.type, - } - - # Add destination for all variants - if self.destination is not None: - result["destination"] = self.destination - - # Handle different type variants - if self.type in ["addRules", "replaceRules", "removeRules"]: - # Rules-based variants require rules and behavior - if self.rules is not None: - result["rules"] = [ - { - "toolName": rule.tool_name, - "ruleContent": rule.rule_content, - } - for rule in self.rules - ] - if self.behavior is not None: - result["behavior"] = self.behavior - - elif self.type == "setMode": - # Mode variant requires mode - if self.mode is not None: - result["mode"] = self.mode - - elif self.type in ["addDirectories", "removeDirectories"]: - # Directory variants require directories - if self.directories is not None: - result["directories"] = self.directories - - return result - - -# Tool callback types -@dataclass -class ToolPermissionContext: - """Context information for tool permission callbacks.""" - - signal: Any | None = None # Future: abort signal support - suggestions: list[PermissionUpdate] = field( - default_factory=list - ) # Permission suggestions from CLI - tool_use_id: str | None = None - """Unique identifier for this specific tool call within the assistant message. - Multiple tool calls in the same assistant message will have different tool_use_ids.""" - agent_id: str | None = None - """If running within the context of a sub-agent, the sub-agent's ID.""" - - -# Match TypeScript's PermissionResult structure -@dataclass -class PermissionResultAllow: - """Allow permission result.""" - - behavior: Literal["allow"] = "allow" - updated_input: dict[str, Any] | None = None - updated_permissions: list[PermissionUpdate] | None = None - - -@dataclass -class PermissionResultDeny: - """Deny permission result.""" - - behavior: Literal["deny"] = "deny" - message: str = "" - interrupt: bool = False - - -PermissionResult = PermissionResultAllow | PermissionResultDeny - -CanUseTool = Callable[ - [str, dict[str, Any], ToolPermissionContext], Awaitable[PermissionResult] -] - - -##### Hook types -HookEvent = ( - Literal["PreToolUse"] - | Literal["PostToolUse"] - | Literal["PostToolUseFailure"] - | Literal["UserPromptSubmit"] - | Literal["Stop"] - | Literal["SubagentStop"] - | Literal["PreCompact"] - | Literal["Notification"] - | Literal["SubagentStart"] - | Literal["PermissionRequest"] -) - - -# Hook input types - strongly typed for each hook event -class BaseHookInput(TypedDict): - """Base hook input fields present across many hook events.""" - - session_id: str - transcript_path: str - cwd: str - permission_mode: NotRequired[str] - - -# agent_id/agent_type are present on BaseHookInput in the CLI's schema but are -# declared per-hook here because SubagentStartHookInput/SubagentStopHookInput -# need them as *required*, and PEP 655 forbids narrowing NotRequired->Required -# in a TypedDict subclass. The four tool-lifecycle types below are the only -# ones the CLI actually populates (the other BaseHookInput consumers don't -# have a toolUseContext in scope at their build site). -class _SubagentContextMixin(TypedDict, total=False): - """Optional sub-agent attribution fields for tool-lifecycle hooks. - - agent_id: Sub-agent identifier. Present only when the hook fires from - inside a Task-spawned sub-agent; absent on the main thread. Matches the - agent_id emitted by that sub-agent's SubagentStart/SubagentStop hooks. - When multiple sub-agents run in parallel their tool-lifecycle hooks - interleave over the same control channel — this is the only reliable - way to attribute each one to the correct sub-agent. - - agent_type: Agent type name (e.g. "general-purpose", "code-reviewer"). - Present inside a sub-agent (alongside agent_id), or on the main thread - of a session started with --agent (without agent_id). - """ - - agent_id: str - agent_type: str - - -class PreToolUseHookInput(BaseHookInput, _SubagentContextMixin): - """Input data for PreToolUse hook events.""" - - hook_event_name: Literal["PreToolUse"] - tool_name: str - tool_input: dict[str, Any] - tool_use_id: str - - -class PostToolUseHookInput(BaseHookInput, _SubagentContextMixin): - """Input data for PostToolUse hook events.""" - - hook_event_name: Literal["PostToolUse"] - tool_name: str - tool_input: dict[str, Any] - tool_response: Any - tool_use_id: str - - -class PostToolUseFailureHookInput(BaseHookInput, _SubagentContextMixin): - """Input data for PostToolUseFailure hook events.""" - - hook_event_name: Literal["PostToolUseFailure"] - tool_name: str - tool_input: dict[str, Any] - tool_use_id: str - error: str - is_interrupt: NotRequired[bool] - - -class UserPromptSubmitHookInput(BaseHookInput): - """Input data for UserPromptSubmit hook events.""" - - hook_event_name: Literal["UserPromptSubmit"] - prompt: str - - -class StopHookInput(BaseHookInput): - """Input data for Stop hook events.""" - - hook_event_name: Literal["Stop"] - stop_hook_active: bool - - -class SubagentStopHookInput(BaseHookInput): - """Input data for SubagentStop hook events.""" - - hook_event_name: Literal["SubagentStop"] - stop_hook_active: bool - agent_id: str - agent_transcript_path: str - agent_type: str - - -class PreCompactHookInput(BaseHookInput): - """Input data for PreCompact hook events.""" - - hook_event_name: Literal["PreCompact"] - trigger: Literal["manual", "auto"] - custom_instructions: str | None - - -class NotificationHookInput(BaseHookInput): - """Input data for Notification hook events.""" - - hook_event_name: Literal["Notification"] - message: str - title: NotRequired[str] - notification_type: str - - -class SubagentStartHookInput(BaseHookInput): - """Input data for SubagentStart hook events.""" - - hook_event_name: Literal["SubagentStart"] - agent_id: str - agent_type: str - - -class PermissionRequestHookInput(BaseHookInput, _SubagentContextMixin): - """Input data for PermissionRequest hook events.""" - - hook_event_name: Literal["PermissionRequest"] - tool_name: str - tool_input: dict[str, Any] - permission_suggestions: NotRequired[list[Any]] - - -# Union type for all hook inputs -HookInput = ( - PreToolUseHookInput - | PostToolUseHookInput - | PostToolUseFailureHookInput - | UserPromptSubmitHookInput - | StopHookInput - | SubagentStopHookInput - | PreCompactHookInput - | NotificationHookInput - | SubagentStartHookInput - | PermissionRequestHookInput -) - - -# Hook-specific output types -class PreToolUseHookSpecificOutput(TypedDict): - """Hook-specific output for PreToolUse events.""" - - hookEventName: Literal["PreToolUse"] - permissionDecision: NotRequired[Literal["allow", "deny", "ask"]] - permissionDecisionReason: NotRequired[str] - updatedInput: NotRequired[dict[str, Any]] - additionalContext: NotRequired[str] - - -class PostToolUseHookSpecificOutput(TypedDict): - """Hook-specific output for PostToolUse events.""" - - hookEventName: Literal["PostToolUse"] - additionalContext: NotRequired[str] - updatedMCPToolOutput: NotRequired[Any] - - -class PostToolUseFailureHookSpecificOutput(TypedDict): - """Hook-specific output for PostToolUseFailure events.""" - - hookEventName: Literal["PostToolUseFailure"] - additionalContext: NotRequired[str] - - -class UserPromptSubmitHookSpecificOutput(TypedDict): - """Hook-specific output for UserPromptSubmit events.""" - - hookEventName: Literal["UserPromptSubmit"] - additionalContext: NotRequired[str] - - -class SessionStartHookSpecificOutput(TypedDict): - """Hook-specific output for SessionStart events.""" - - hookEventName: Literal["SessionStart"] - additionalContext: NotRequired[str] - - -class NotificationHookSpecificOutput(TypedDict): - """Hook-specific output for Notification events.""" - - hookEventName: Literal["Notification"] - additionalContext: NotRequired[str] - - -class SubagentStartHookSpecificOutput(TypedDict): - """Hook-specific output for SubagentStart events.""" - - hookEventName: Literal["SubagentStart"] - additionalContext: NotRequired[str] - - -class PermissionRequestHookSpecificOutput(TypedDict): - """Hook-specific output for PermissionRequest events.""" - - hookEventName: Literal["PermissionRequest"] - decision: dict[str, Any] - - -HookSpecificOutput = ( - PreToolUseHookSpecificOutput - | PostToolUseHookSpecificOutput - | PostToolUseFailureHookSpecificOutput - | UserPromptSubmitHookSpecificOutput - | SessionStartHookSpecificOutput - | NotificationHookSpecificOutput - | SubagentStartHookSpecificOutput - | PermissionRequestHookSpecificOutput -) - - -# See https://docs.anthropic.com/en/docs/claude-code/hooks#advanced%3A-json-output -# for documentation of the output types. -# -# IMPORTANT: The Python SDK uses `async_` and `continue_` (with underscores) to avoid -# Python keyword conflicts. These fields are automatically converted to `async` and -# `continue` when sent to the CLI. You should use the underscore versions in your -# Python code. -class AsyncHookJSONOutput(TypedDict): - """Async hook output that defers hook execution. - - Fields: - async_: Set to True to defer hook execution. Note: This is converted to - "async" when sent to the CLI - use "async_" in your Python code. - asyncTimeout: Optional timeout in milliseconds for the async operation. - """ - - async_: Literal[ - True - ] # Using async_ to avoid Python keyword (converted to "async" for CLI) - asyncTimeout: NotRequired[int] - - -class SyncHookJSONOutput(TypedDict): - """Synchronous hook output with control and decision fields. - - This defines the structure for hook callbacks to control execution and provide - feedback to Claude. - - Common Control Fields: - continue_: Whether Claude should proceed after hook execution (default: True). - Note: This is converted to "continue" when sent to the CLI. - suppressOutput: Hide stdout from transcript mode (default: False). - stopReason: Message shown when continue is False. - - Decision Fields: - decision: Set to "block" to indicate blocking behavior. - systemMessage: Warning message displayed to the user. - reason: Feedback message for Claude about the decision. - - Hook-Specific Output: - hookSpecificOutput: Event-specific controls (e.g., permissionDecision for - PreToolUse, additionalContext for PostToolUse). - - Note: The CLI documentation shows field names without underscores ("async", "continue"), - but Python code should use the underscore versions ("async_", "continue_") as they - are automatically converted. - """ - - # Common control fields - continue_: NotRequired[ - bool - ] # Using continue_ to avoid Python keyword (converted to "continue" for CLI) - suppressOutput: NotRequired[bool] - stopReason: NotRequired[str] - - # Decision fields - # Note: "approve" is deprecated for PreToolUse (use permissionDecision instead) - # For other hooks, only "block" is meaningful - decision: NotRequired[Literal["block"]] - systemMessage: NotRequired[str] - reason: NotRequired[str] - - # Hook-specific outputs - hookSpecificOutput: NotRequired[HookSpecificOutput] - - -HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutput - - -class HookContext(TypedDict): - """Context information for hook callbacks. - - Fields: - signal: Reserved for future abort signal support. Currently always None. - """ - - signal: Any | None # Future: abort signal support - - -HookCallback = Callable[ - # HookCallback input parameters: - # - input: Strongly-typed hook input with discriminated unions based on hook_event_name - # - tool_use_id: Optional tool use identifier - # - context: Hook context with abort signal support (currently placeholder) - [HookInput, str | None, HookContext], - Awaitable[HookJSONOutput], -] - - -# Hook matcher configuration -@dataclass -class HookMatcher: - """Hook matcher configuration.""" - - # See https://docs.anthropic.com/en/docs/claude-code/hooks#structure for the - # expected string value. For example, for PreToolUse, the matcher can be - # a tool name like "Bash" or a combination of tool names like - # "Write|MultiEdit|Edit". - matcher: str | None = None - - # A list of Python functions with function signature HookCallback - hooks: list[HookCallback] = field(default_factory=list) - - # Timeout in seconds for all hooks in this matcher (default: 60) - timeout: float | None = None - - -# MCP Server config -class McpStdioServerConfig(TypedDict): - """MCP stdio server configuration.""" - - type: NotRequired[Literal["stdio"]] # Optional for backwards compatibility - command: str - args: NotRequired[list[str]] - env: NotRequired[dict[str, str]] - - -class McpSSEServerConfig(TypedDict): - """MCP SSE server configuration.""" - - type: Literal["sse"] - url: str - headers: NotRequired[dict[str, str]] - - -class McpHttpServerConfig(TypedDict): - """MCP HTTP server configuration.""" - - type: Literal["http"] - url: str - headers: NotRequired[dict[str, str]] - - -class McpSdkServerConfig(TypedDict): - """SDK MCP server configuration.""" - - type: Literal["sdk"] - name: str - instance: "McpServer" - - -McpServerConfig = ( - McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig | McpSdkServerConfig -) - - -# MCP Server Status types (returned by get_mcp_status) -# These mirror the TypeScript SDK's McpServerStatus type and use wire-format -# field names (camelCase where applicable) since they come directly from CLI -# JSON output. - - -class McpSdkServerConfigStatus(TypedDict): - """SDK MCP server config as returned in status responses. - - Unlike McpSdkServerConfig (which includes the in-process `instance`), - this output-only type only has serializable fields. - """ - - type: Literal["sdk"] - name: str - - -class McpClaudeAIProxyServerConfig(TypedDict): - """Claude.ai proxy MCP server config. - - Output-only type that appears in status responses for servers proxied - through Claude.ai. - """ - - type: Literal["claudeai-proxy"] - url: str - id: str - - -# Broader config type for status responses (includes claudeai-proxy which is -# output-only) -McpServerStatusConfig = ( - McpStdioServerConfig - | McpSSEServerConfig - | McpHttpServerConfig - | McpSdkServerConfigStatus - | McpClaudeAIProxyServerConfig -) - - -class McpToolAnnotations(TypedDict, total=False): - """Tool annotations as returned in MCP server status. - - Wire format uses camelCase field names (from CLI JSON output). - """ - - readOnly: bool - destructive: bool - openWorld: bool - - -class McpToolInfo(TypedDict): - """Information about a tool provided by an MCP server.""" - - name: str - description: NotRequired[str] - annotations: NotRequired[McpToolAnnotations] - - -class McpServerInfo(TypedDict): - """Server info from MCP initialize handshake (available when connected).""" - - name: str - version: str - - -# Connection status values for an MCP server -McpServerConnectionStatus = Literal[ - "connected", "failed", "needs-auth", "pending", "disabled" -] - - -class McpServerStatus(TypedDict): - """Status information for an MCP server connection. - - Returned by `ClaudeSDKClient.get_mcp_status()` in the `mcpServers` list. - """ - - name: str - """Server name as configured.""" - - status: McpServerConnectionStatus - """Current connection status.""" - - serverInfo: NotRequired[McpServerInfo] - """Server information from MCP handshake (available when connected).""" - - error: NotRequired[str] - """Error message (available when status is 'failed').""" - - config: NotRequired[McpServerStatusConfig] - """Server configuration (includes URL for HTTP/SSE servers).""" - - scope: NotRequired[str] - """Configuration scope (e.g., project, user, local, claudeai, managed).""" - - tools: NotRequired[list[McpToolInfo]] - """Tools provided by this server (available when connected).""" - - -class McpStatusResponse(TypedDict): - """Response from `ClaudeSDKClient.get_mcp_status()`. - - Wraps the list of server statuses under the `mcpServers` key, matching - the wire-format response shape. - """ - - mcpServers: list[McpServerStatus] - - -class ContextUsageCategory(TypedDict): - """A single context usage category (system prompt, tools, messages, etc.).""" - - name: str - tokens: int - color: str - isDeferred: NotRequired[bool] - - -class ContextUsageResponse(TypedDict): - """Response from `ClaudeSDKClient.get_context_usage()`. - - Provides a breakdown of current context window usage by category, - matching the data shown by the `/context` command in the CLI. - """ - - categories: list[ContextUsageCategory] - """Token usage broken down by category (system prompt, tools, messages, etc.).""" - - totalTokens: int - """Total tokens currently in the context window.""" - - maxTokens: int - """Effective maximum tokens (may be reduced by autocompact buffer).""" - - rawMaxTokens: int - """Raw model context window size.""" - - percentage: float - """Percentage of context window used (0-100).""" - - model: str - """Model name the context usage is calculated for.""" - - isAutoCompactEnabled: bool - """Whether autocompact is enabled for this session.""" - - memoryFiles: list[dict[str, Any]] - """CLAUDE.md and memory files loaded, with path, type, and token counts.""" - - mcpTools: list[dict[str, Any]] - """MCP tools with name, serverName, tokens, and isLoaded status.""" - - agents: list[dict[str, Any]] - """Agent definitions with agentType, source, and token counts.""" - - gridRows: list[list[dict[str, Any]]] - """Visual grid representation used by the CLI context display.""" - - autoCompactThreshold: NotRequired[int] - """Token threshold at which autocompact triggers.""" - - deferredBuiltinTools: NotRequired[list[dict[str, Any]]] - """Built-in tools deferred from the initial tool list.""" - - systemTools: NotRequired[list[dict[str, Any]]] - """System (built-in) tools with name and token counts.""" - - systemPromptSections: NotRequired[list[dict[str, Any]]] - """System prompt sections with name and token counts.""" - - slashCommands: NotRequired[dict[str, Any]] - """Slash command usage summary.""" - - skills: NotRequired[dict[str, Any]] - """Skill usage summary with frontmatter breakdown.""" - - messageBreakdown: NotRequired[dict[str, Any]] - """Detailed breakdown of message tokens by type (tool calls, results, etc.).""" - - apiUsage: NotRequired[dict[str, Any] | None] - """Cumulative API usage for the session.""" - - -class SdkPluginConfig(TypedDict): - """SDK plugin configuration. - - Currently only local plugins are supported via the 'local' type. - """ - - type: Literal["local"] - path: str - - -# Sandbox configuration types -class SandboxNetworkConfig(TypedDict, total=False): - """Network configuration for sandbox. - - Attributes: - allowUnixSockets: Unix socket paths accessible in sandbox (e.g., SSH agents). - allowAllUnixSockets: Allow all Unix sockets (less secure). - allowLocalBinding: Allow binding to localhost ports (macOS only). - httpProxyPort: HTTP proxy port if bringing your own proxy. - socksProxyPort: SOCKS5 proxy port if bringing your own proxy. - """ - - allowUnixSockets: list[str] - allowAllUnixSockets: bool - allowLocalBinding: bool - httpProxyPort: int - socksProxyPort: int - - -class SandboxIgnoreViolations(TypedDict, total=False): - """Violations to ignore in sandbox. - - Attributes: - file: File paths for which violations should be ignored. - network: Network hosts for which violations should be ignored. - """ - - file: list[str] - network: list[str] - - -class SandboxSettings(TypedDict, total=False): - """Sandbox settings configuration. - - This controls how Claude Code sandboxes bash commands for filesystem - and network isolation. - - **Important:** Filesystem and network restrictions are configured via permission - rules, not via these sandbox settings: - - Filesystem read restrictions: Use Read deny rules - - Filesystem write restrictions: Use Edit allow/deny rules - - Network restrictions: Use WebFetch allow/deny rules - - Attributes: - enabled: Enable bash sandboxing (macOS/Linux only). Default: False - autoAllowBashIfSandboxed: Auto-approve bash commands when sandboxed. Default: True - excludedCommands: Commands that should run outside the sandbox (e.g., ["git", "docker"]) - allowUnsandboxedCommands: Allow commands to bypass sandbox via dangerouslyDisableSandbox. - When False, all commands must run sandboxed (or be in excludedCommands). Default: True - network: Network configuration for sandbox. - ignoreViolations: Violations to ignore. - enableWeakerNestedSandbox: Enable weaker sandbox for unprivileged Docker environments - (Linux only). Reduces security. Default: False - - Example: - ```python - sandbox_settings: SandboxSettings = { - "enabled": True, - "autoAllowBashIfSandboxed": True, - "excludedCommands": ["docker"], - "network": { - "allowUnixSockets": ["/var/run/docker.sock"], - "allowLocalBinding": True - } - } - ``` - """ - - enabled: bool - autoAllowBashIfSandboxed: bool - excludedCommands: list[str] - allowUnsandboxedCommands: bool - network: SandboxNetworkConfig - ignoreViolations: SandboxIgnoreViolations - enableWeakerNestedSandbox: bool - - -# Content block types -@dataclass -class TextBlock: - """Text content block.""" - - text: str - - -@dataclass -class ThinkingBlock: - """Thinking content block.""" - - thinking: str - signature: str - - -@dataclass -class ToolUseBlock: - """Tool use content block.""" - - id: str - name: str - input: dict[str, Any] - - -@dataclass -class ToolResultBlock: - """Tool result content block.""" - - tool_use_id: str - content: str | list[dict[str, Any]] | None = None - is_error: bool | None = None - - -ServerToolName = Literal[ - "advisor", - "web_search", - "web_fetch", - "code_execution", - "bash_code_execution", - "text_editor_code_execution", - "tool_search_tool_regex", - "tool_search_tool_bm25", -] - - -@dataclass -class ServerToolUseBlock: - """Server-side tool use block (e.g. advisor, web_search, web_fetch). - - These are tools the API executes server-side on the model's behalf, so they - appear in the message stream alongside regular `tool_use` blocks but the - caller never needs to return a result. `name` is a discriminator — branch - on it to know which server tool was invoked. - """ - - id: str - name: ServerToolName - input: dict[str, Any] - - -@dataclass -class ServerToolResultBlock: - """Result block returned for a server-side tool call. - - Mirrors `ToolResultBlock`'s shape. `content` is the raw dict from the - API, opaque to this layer — callers that care about a specific server - tool's result schema can inspect `content["type"]`. - """ - - tool_use_id: str - content: dict[str, Any] - - -ContentBlock = ( - TextBlock - | ThinkingBlock - | ToolUseBlock - | ToolResultBlock - | ServerToolUseBlock - | ServerToolResultBlock -) - - -# Message types -AssistantMessageError = Literal[ - "authentication_failed", - "billing_error", - "rate_limit", - "invalid_request", - "server_error", - "unknown", -] - - -@dataclass -class UserMessage: - """User message.""" - - content: str | list[ContentBlock] - uuid: str | None = None - parent_tool_use_id: str | None = None - tool_use_result: dict[str, Any] | None = None - - -@dataclass -class AssistantMessage: - """Assistant message with content blocks.""" - - content: list[ContentBlock] - model: str - parent_tool_use_id: str | None = None - error: AssistantMessageError | None = None - usage: dict[str, Any] | None = None - message_id: str | None = None - stop_reason: str | None = None - session_id: str | None = None - uuid: str | None = None - - -@dataclass -class SystemMessage: - """System message with metadata.""" - - subtype: str - data: dict[str, Any] - - -class TaskUsage(TypedDict): - """Usage statistics reported in task_progress and task_notification messages.""" - - total_tokens: int - tool_uses: int - duration_ms: int - - -# Possible status values for a task_notification message. -TaskNotificationStatus = Literal["completed", "failed", "stopped"] - - -@dataclass -class TaskStartedMessage(SystemMessage): - """System message emitted when a task starts. - - Subclass of SystemMessage: existing ``isinstance(msg, SystemMessage)`` and - ``case SystemMessage()`` checks continue to match. The base ``subtype`` - and ``data`` fields remain populated with the raw payload. - """ - - task_id: str - description: str - uuid: str - session_id: str - tool_use_id: str | None = None - task_type: str | None = None - - -@dataclass -class TaskProgressMessage(SystemMessage): - """System message emitted while a task is in progress. - - Subclass of SystemMessage: existing ``isinstance(msg, SystemMessage)`` and - ``case SystemMessage()`` checks continue to match. The base ``subtype`` - and ``data`` fields remain populated with the raw payload. - """ - - task_id: str - description: str - usage: TaskUsage - uuid: str - session_id: str - tool_use_id: str | None = None - last_tool_name: str | None = None - - -@dataclass -class TaskNotificationMessage(SystemMessage): - """System message emitted when a task completes, fails, or is stopped. - - Subclass of SystemMessage: existing ``isinstance(msg, SystemMessage)`` and - ``case SystemMessage()`` checks continue to match. The base ``subtype`` - and ``data`` fields remain populated with the raw payload. - """ - - task_id: str - status: TaskNotificationStatus - output_file: str - summary: str - uuid: str - session_id: str - tool_use_id: str | None = None - usage: TaskUsage | None = None - - -@dataclass -class MirrorErrorMessage(SystemMessage): - """System message emitted when a :meth:`SessionStore.append` call fails. - - Non-fatal — the local-disk transcript is already durable, so the session - continues unaffected. The mirrored copy in the external store will be - missing the failed batch. - - Subclass of SystemMessage: existing ``isinstance(msg, SystemMessage)`` and - ``case SystemMessage()`` checks continue to match. The base ``subtype`` - field is ``"mirror_error"`` and ``data`` carries the raw payload. - """ - - key: "SessionKey | None" = None - error: str = "" - - -@dataclass -class ResultMessage: - """Result message with cost and usage information.""" - - subtype: str - duration_ms: int - duration_api_ms: int - is_error: bool - num_turns: int - session_id: str - stop_reason: str | None = None - total_cost_usd: float | None = None - usage: dict[str, Any] | None = None - result: str | None = None - structured_output: Any = None - model_usage: dict[str, Any] | None = None - permission_denials: list[Any] | None = None - errors: list[str] | None = None - uuid: str | None = None - - -@dataclass -class StreamEvent: - """Stream event for partial message updates during streaming.""" - - uuid: str - session_id: str - event: dict[str, Any] # The raw Anthropic API stream event - parent_tool_use_id: str | None = None - - -# Rate limit types — see https://docs.claude.com/en/docs/claude-code/rate-limits -RateLimitStatus = Literal["allowed", "allowed_warning", "rejected"] -RateLimitType = Literal[ - "five_hour", "seven_day", "seven_day_opus", "seven_day_sonnet", "overage" -] - - -@dataclass -class RateLimitInfo: - """Rate limit status emitted by the CLI when rate limit state changes. - - Attributes: - status: Current rate limit status. ``allowed_warning`` means approaching - the limit; ``rejected`` means the limit has been hit. - resets_at: Unix timestamp when the rate limit window resets. - rate_limit_type: Which rate limit window applies. - utilization: Fraction of the rate limit consumed (0.0 - 1.0). - overage_status: Status of overage/pay-as-you-go usage if applicable. - overage_resets_at: Unix timestamp when overage window resets. - overage_disabled_reason: Why overage is unavailable if status is rejected. - raw: Full raw dict from the CLI, including any fields not modeled above. - """ - - status: RateLimitStatus - resets_at: int | None = None - rate_limit_type: RateLimitType | None = None - utilization: float | None = None - overage_status: RateLimitStatus | None = None - overage_resets_at: int | None = None - overage_disabled_reason: str | None = None - raw: dict[str, Any] = field(default_factory=dict) - - -@dataclass -class RateLimitEvent: - """Rate limit event emitted when rate limit info changes. - - The CLI emits this whenever the rate limit status transitions (e.g. from - ``allowed`` to ``allowed_warning``). Use this to warn users before they - hit a hard limit, or to gracefully back off when ``status == "rejected"``. - """ - - rate_limit_info: RateLimitInfo - uuid: str - session_id: str - - -Message = ( - UserMessage - | AssistantMessage - | SystemMessage - | ResultMessage - | StreamEvent - | RateLimitEvent -) - - -# --------------------------------------------------------------------------- -# Session Store Types -# --------------------------------------------------------------------------- - - -class SessionKey(TypedDict): - """Identifies a session transcript or subagent transcript in a store. - - Main transcripts have no ``subpath``; subagent transcripts include a - ``subpath`` like ``"subagents/agent-{id}"`` that mirrors the on-disk - directory structure. - """ - - project_key: str - """Caller-defined scope. Default: sanitized cwd. Multi-tenant deployments - should set this to a tenant ID or project name. Paths longer than 200 - characters are truncated and suffixed with a portable djb2 hash so the - same path yields the same key across runtimes.""" - - session_id: str - - subpath: NotRequired[str] - """Omit for the main transcript; set for subagent files. Empty string is - invalid — omit the field for the main transcript. Opaque to the adapter — - just use it as a storage key suffix.""" - - -class SessionStoreEntry(TypedDict, total=False): - """One JSONL transcript line as observed by a :class:`SessionStore` adapter. - - The concrete shape is the CLI's on-disk transcript format (a large - discriminated union). That union is internal, so this is a minimal - structural supertype — adapters should treat entries as pass-through - blobs; round-tripping ``json.dumps``/``json.loads`` is the only - required invariant. - """ - - type: Required[str] - uuid: str - timestamp: str - # Additional fields are opaque JSON — adapters must pass them through. - - -class SessionStoreListEntry(TypedDict): - """Entry returned by :meth:`SessionStore.list_sessions`.""" - - session_id: str - mtime: int - """Last-modified time in Unix epoch milliseconds. Adapters without native - modification time (e.g. Redis) must maintain their own index.""" - - -class SessionSummaryEntry(TypedDict): - """Incrementally-maintained session summary. - - Stores obtain this from :func:`fold_session_summary` inside - :meth:`SessionStore.append` and persist it verbatim; they return the - full set from :meth:`SessionStore.list_session_summaries`. The ``data`` - field is opaque SDK-owned state — stores MUST NOT interpret it. - """ - - session_id: str - mtime: int - """Storage write time of the sidecar, in Unix epoch milliseconds. Must use - the same clock source as the ``mtime`` returned by - :meth:`SessionStore.list_sessions` for this session — typically file - mtime, S3 ``LastModified``, Postgres ``updated_at``, or whatever native - timestamp the adapter surfaces. Do NOT derive this from entry ISO - timestamps: adapters that write in batches with any persist latency - (every real backend) would report storage times strictly later than the - last entry's timestamp, making every sidecar appear stale and defeating - the fast-path staleness check in ``list_sessions_from_store``. - :func:`fold_session_summary` preserves whatever ``mtime`` the caller - passes in via ``prev`` and does not set it itself; stamp it after - persisting.""" - data: dict[str, Any] - """Opaque SDK-owned summary state. Persist verbatim; do not interpret.""" - - -class SessionListSubkeysKey(TypedDict): - """Key argument to :meth:`SessionStore.list_subkeys` (no ``subpath``).""" - - project_key: str - session_id: str - - -class SessionStore(Protocol): - """Adapter for mirroring session transcripts to external storage. - - The subprocess still writes to local disk (set ``CLAUDE_CONFIG_DIR=/tmp`` - for an ephemeral local copy); the adapter receives a secondary copy. - - The SDK never deletes from your store unless you call - ``delete_session_via_store()`` with :meth:`delete` implemented. Retention is - the adapter's responsibility — - implement TTL, object-storage lifecycle policies, or scheduled cleanup - according to your compliance requirements (e.g. ZDR/HIPAA retention - windows). Local-disk transcripts under ``CLAUDE_CONFIG_DIR`` are swept by - the existing ``cleanupPeriodDays`` setting independently of this adapter. - - Only :meth:`append` and :meth:`load` are required. The remaining methods - are optional: implementers may omit them, and call sites probe for their - presence at runtime before invoking (the SDK never uses ``isinstance`` for - this — a duck-typed adapter need not subclass ``SessionStore``). The - default implementations on this Protocol raise :class:`NotImplementedError` - so subclasses can inherit them as "absent" markers. - """ - - async def append(self, key: SessionKey, entries: list[SessionStoreEntry]) -> None: - """Mirror a batch of transcript entries. - - Called AFTER the subprocess's local write succeeds — durability is - already guaranteed locally. - - Batches arrive at ~100ms cadence during active turns. Entries are - JSON-safe plain objects — one per line in the local JSONL file. - - Within a single process, persist entries in append-call order; across - concurrent processes, order is by storage commit time, not call time. - - Most entries carry a stable ``uuid`` that adapters should treat as an - idempotency key (upsert / ignore-duplicate). Entries without a - ``uuid`` (e.g. titles, tags, mode markers) should be appended without - dedup. Exceptions are logged and the subprocess continues unaffected - — failed batches are retried (3 attempts total) with short backoff - before being dropped and surfaced as a ``MirrorErrorMessage``; - timeouts are not retried since the in-flight call may still land. - """ - ... - - async def load(self, key: SessionKey) -> list[SessionStoreEntry] | None: - """Load a full session for resume. - - Called once, in the SDK parent, before subprocess spawn. The result is - materialized to a temporary JSONL file; the subprocess resumes from - that file using its existing resume code. - - Return ``None`` for a key that was never written; adapters that cannot - distinguish "never written" from "emptied" (e.g. Redis ``LRANGE``) may - return ``None`` for both. Returned entries must be deep-equal to what - was appended — byte-equal serialization is NOT required (e.g. Postgres - ``JSONB`` may reorder object keys); the SDK never hashes or - byte-compares entries. - """ - ... - - async def list_sessions(self, project_key: str) -> list[SessionStoreListEntry]: - """List sessions for a ``project_key``. Returns IDs + modification times. - - ``mtime`` is Unix epoch milliseconds; adapters without native - modification time (e.g. Redis) must maintain their own index. Result - order is unspecified — the SDK sorts by ``mtime`` descending. - - Optional — if unimplemented, ``list_sessions()`` with a session store - raises. - """ - raise NotImplementedError - - async def list_session_summaries( - self, project_key: str - ) -> list[SessionSummaryEntry]: - """Return incrementally-maintained summaries for all sessions in one call. - - Stores should maintain these via :func:`fold_session_summary` inside - :meth:`append`. Skip the fold for keys with a ``subpath`` — subagent - transcripts must not contribute to the main session's summary. - - Like :meth:`list_sessions`, results are scoped to a single - ``project_key`` and exclude ``subpath`` entries. - - Optional — if unimplemented, ``list_sessions_from_store()`` falls back - to ``list_sessions()`` + per-session ``load()``. - - .. note:: - Stores that maintain summaries inside ``append()`` MUST serialize - sidecar writes if ``append()`` calls can race for the same session - — e.g., wrap the read-fold-write in a transaction/CAS, or hold a - per-session lock. The SDK's :func:`fold_session_summary` is pure; - concurrency control is the store's responsibility. - """ - raise NotImplementedError - - async def delete(self, key: SessionKey) -> None: - """Delete a session. - - Deleting a main-transcript key (no ``subpath``) must cascade to all - subkeys under that session so subagent transcripts aren't orphaned. A - targeted delete with an explicit ``subpath`` removes only that one - entry. - - Optional — if unimplemented, deletion is a no-op (appropriate for - WORM/append-only backends like object storage). - """ - raise NotImplementedError - - async def list_subkeys(self, key: SessionListSubkeysKey) -> list[str]: - """List all subpath keys under a session (e.g. subagent transcripts). - - Used during resume to discover and materialize all subagent data. - - Optional — if unimplemented, resume only materializes the main - transcript. - """ - raise NotImplementedError - - -# --------------------------------------------------------------------------- -# Session Listing Types -# --------------------------------------------------------------------------- - - -@dataclass -class SDKSessionInfo: - """Session metadata returned by ``list_sessions()``. - - Contains only data extractable from stat + head/tail reads — no full - JSONL parsing required. - - Attributes: - session_id: Unique session identifier (UUID). - summary: Display title for the session — custom title, auto-generated - summary, or first prompt. - last_modified: Last modified time in milliseconds since epoch. - file_size: Session file size in bytes. Only populated for local - JSONL storage; may be ``None`` for remote storage backends. - custom_title: Session title — user-set custom title or AI-generated title. - first_prompt: First meaningful user prompt in the session. - git_branch: Git branch at the end of the session. - cwd: Working directory for the session. - tag: User-set session tag. - created_at: Creation time in milliseconds since epoch, extracted - from the first entry's ISO timestamp field. More reliable - than stat().birthtime which is unsupported on some filesystems. - """ - - session_id: str - summary: str - last_modified: int - file_size: int | None = None - custom_title: str | None = None - first_prompt: str | None = None - git_branch: str | None = None - cwd: str | None = None - tag: str | None = None - created_at: int | None = None - - -@dataclass -class SessionMessage: - """A user or assistant message from a session transcript. - - Returned by ``get_session_messages()`` for reading historical session - data. Fields match the SDK wire protocol types (SDKUserMessage / - SDKAssistantMessage). - - Attributes: - type: Message type — ``"user"`` or ``"assistant"``. - uuid: Unique message identifier. - session_id: ID of the session this message belongs to. - message: Raw Anthropic API message dict (role, content, etc.). - parent_tool_use_id: Always ``None`` for top-level conversation - messages (tool-use sidechain messages are filtered out). - """ - - type: Literal["user", "assistant"] - uuid: str - session_id: str - message: Any - parent_tool_use_id: None = None - - -# Controls whether thinking text is returned summarized or omitted. Opus 4.7+ -# defaults to "omitted" (signature-only); pass "summarized" to receive text. -ThinkingDisplay = Literal["summarized", "omitted"] - - -class ThinkingConfigAdaptive(TypedDict): - type: Literal["adaptive"] - display: NotRequired[ThinkingDisplay] - - -class ThinkingConfigEnabled(TypedDict): - type: Literal["enabled"] - budget_tokens: int - display: NotRequired[ThinkingDisplay] - - -class ThinkingConfigDisabled(TypedDict): - type: Literal["disabled"] - - -ThinkingConfig = ThinkingConfigAdaptive | ThinkingConfigEnabled | ThinkingConfigDisabled - - -@dataclass -class ClaudeAgentOptions: - """Query options for Claude SDK.""" - - tools: list[str] | ToolsPreset | None = None - """Specify the base set of available built-in tools. - - - ``list[str]`` — Specific tool names (e.g. ``["Bash", "Read", "Edit"]``). - - ``[]`` (empty list) — Disable all built-in tools. - - ``{"type": "preset", "preset": "claude_code"}`` — Use all default Claude Code tools. - - To restrict which tools the model may call without being prompted, use - ``allowed_tools`` instead. - """ - - allowed_tools: list[str] = field(default_factory=list) - """Tool names that are auto-allowed without prompting for permission. - - These tools execute automatically without asking the user for approval. - To restrict which tools are available at all, use ``tools``. - """ - - system_prompt: str | SystemPromptPreset | SystemPromptFile | None = None - """System prompt configuration. - - - ``str`` — Use a custom system prompt. - - ``{"type": "preset", "preset": "claude_code"}`` — Use Claude Code's default - system prompt. - - ``{"type": "preset", "preset": "claude_code", "append": "..."}`` — Default - prompt with appended instructions. - """ - - mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict) - """MCP (Model Context Protocol) server configurations. - - Keys are server names, values are server configurations. May also be a path - to an MCP config JSON file. - """ - - permission_mode: PermissionMode | None = None - """Permission mode for the session. - - - ``"default"`` — Standard permission behavior; prompts for dangerous operations. - - ``"acceptEdits"`` — Auto-accept file edit operations. - - ``"bypassPermissions"`` — Bypass all permission checks. - - ``"plan"`` — Planning mode, no execution of tools. - - ``"dontAsk"`` — Don't prompt for permissions; deny if not pre-approved. - """ - - continue_conversation: bool = False - """Continue the most recent conversation in the current directory instead of - starting a new one. Mutually exclusive with ``resume``.""" - - resume: str | None = None - """Session ID to resume. Loads the conversation history from the specified session.""" - - session_id: str | None = None - """Use a specific session ID for the conversation instead of an auto-generated one. - - Must be a valid UUID. Cannot be used with ``continue_conversation`` or - ``resume`` unless ``fork_session`` is also set. - """ - - max_turns: int | None = None - """Maximum number of conversation turns before the query stops. - - A turn consists of a user message and assistant response. - """ - - max_budget_usd: float | None = None - """Maximum budget in USD for the query. - - The query will stop if this budget is exceeded, returning an - ``error_max_budget_usd`` result. - """ - - disallowed_tools: list[str] = field(default_factory=list) - """Tool names that are disallowed. - - These tools are removed from the model's context and cannot be used, even - if they would otherwise be allowed. - """ - - model: str | None = None - """Claude model to use. Defaults to the CLI default model. - - Examples: ``"claude-sonnet-4-5"``, ``"claude-opus-4-5"``. - """ - - fallback_model: str | None = None - """Fallback model to use if the primary model fails or is unavailable.""" - - betas: list[SdkBeta] = field(default_factory=list) - """Enable beta features. - - Currently supported: - - - ``"context-1m-2025-08-07"`` — Enable 1M token context window (Sonnet 4/4.5 only). - - See https://docs.anthropic.com/en/api/beta-headers. - """ - - permission_prompt_tool_name: str | None = None - """MCP tool name to use for permission prompts. - - When set, permission requests are routed through this MCP tool instead of - the default handler. - """ - - cwd: str | Path | None = None - """Current working directory for the session. Defaults to the process cwd.""" - - cli_path: str | Path | None = None - """Path to the Claude Code CLI executable. - - Uses the bundled executable if not specified. - """ - - settings: str | None = None - """Path to an additional settings JSON file to load. - - These are loaded into the "flag settings" layer, which has the highest - priority among user-controlled settings. Equivalent to the ``--settings`` - CLI flag. - """ - - add_dirs: list[str | Path] = field(default_factory=list) - """Additional directories Claude can access beyond the current working directory. - - Paths should be absolute. - """ - - env: dict[str, str] = field(default_factory=dict) - """Environment variables to pass to the Claude Code subprocess. - - SDK consumers can identify their app/library in the User-Agent header by - setting ``CLAUDE_AGENT_SDK_CLIENT_APP`` (e.g. ``"my-app/1.0.0"``). - """ - - extra_args: dict[str, str | None] = field(default_factory=dict) - """Additional CLI arguments to pass to Claude Code. - - Keys are argument names (without ``--``), values are argument values. Use - ``None`` for boolean flags. - """ - - max_buffer_size: int | None = None - """Maximum bytes to buffer when reading the CLI subprocess stdout.""" - - debug_stderr: Any = sys.stderr - """Deprecated and no longer read by the transport. Use the ``stderr`` callback.""" - - stderr: Callable[[str], None] | None = None - """Callback for stderr output from the Claude Code subprocess. - - Useful for debugging and logging. - """ - - can_use_tool: CanUseTool | None = None - """Custom permission handler for controlling tool usage. - - Called before each tool execution to determine if it should be allowed, - denied, or prompt the user. - """ - - hooks: dict[HookEvent, list[HookMatcher]] | None = None - """Hook callbacks for responding to various events during execution. - - Hooks can modify behavior, add context, or implement custom logic. See - https://docs.anthropic.com/en/docs/claude-code/hooks. - """ - - user: str | None = None - """Optional user identifier associated with the session.""" - - include_partial_messages: bool = False - """Include partial/streaming message events in the output. - - When true, ``SDKPartialAssistantMessage`` events are emitted during streaming. - """ - - fork_session: bool = False - """When true, resumed sessions fork to a new session ID rather than - continuing the previous session. Use with ``resume``.""" - - agents: dict[str, AgentDefinition] | None = None - """Programmatically define custom subagents invokable via the Agent tool. - - Keys are agent names, values are agent definitions. - """ - - setting_sources: list[SettingSource] | None = None - """Control which filesystem settings to load. - - - ``"user"`` — Global user settings (``~/.claude/settings.json``). - - ``"project"`` — Project settings (``.claude/settings.json``). - - ``"local"`` — Local settings (``.claude/settings.local.json``). - - When ``None``, all sources are loaded (matches CLI defaults). Pass ``[]`` - to disable filesystem settings (SDK isolation mode). Must include - ``"project"`` to load CLAUDE.md files. - """ - - skills: list[str] | Literal["all"] | None = None - """Skills to enable for the main session. - - This is the single place to turn skills on; you do not need to add - ``"Skill"`` to ``allowed_tools`` or set ``setting_sources`` yourself — the - SDK does both when this is set. - - - ``None`` (default): no SDK auto-configuration. The CLI's own defaults - still apply, so this is **not** "skills off" — to suppress every skill - from the listing, use ``[]``. - - ``"all"``: enable every discovered skill. - - ``list[str]``: enable only the listed skills. Names match the SKILL.md - ``name`` / directory name, or ``plugin:skill`` for plugin-qualified skills. - - This is a **context filter**, not a sandbox: unlisted skills are hidden - from the model's listing and rejected by the Skill tool, but their files - remain on disk and are reachable via Read/Bash. Do not store secrets in - skill files. - """ - - sandbox: SandboxSettings | None = None - """Sandbox settings for command execution isolation. - - When enabled, commands execute in a sandboxed environment that restricts - filesystem and network access. Filesystem and network restrictions are - configured via permission rules (Read/Edit for filesystem, WebFetch for - network), not via these sandbox settings — sandbox settings control - sandbox behavior (enabled, auto-allow, etc.). - - See https://docs.anthropic.com/en/docs/claude-code/settings#sandbox-settings. - """ - - plugins: list[SdkPluginConfig] = field(default_factory=list) - """Load plugins for this session. - - Plugins provide custom commands, agents, skills, and hooks that extend - Claude Code's capabilities. Currently only local plugins are supported. - """ - - max_thinking_tokens: int | None = None - """Maximum tokens the model may use for its thinking/reasoning process. - - .. deprecated:: - Use ``thinking`` instead. On newer models, this is treated as on/off - (0 = disabled, any other value = adaptive). For explicit control, use - ``thinking={"type": "adaptive"}`` or - ``thinking={"type": "enabled", "budget_tokens": N}``. - """ - - thinking: ThinkingConfig | None = None - """Controls Claude's thinking/reasoning behavior. - - - ``{"type": "adaptive"}`` — Claude decides when and how much to think - (Opus 4.6+). Default for models that support it. - - ``{"type": "enabled", "budget_tokens": N}`` — Fixed thinking token budget - (older models). - - ``{"type": "disabled"}`` — No extended thinking. - - When set, takes precedence over the deprecated ``max_thinking_tokens``. - See https://docs.anthropic.com/en/docs/build-with-claude/adaptive-thinking. - """ - - effort: Literal["low", "medium", "high", "max"] | None = None - """Controls how much effort Claude puts into its response. - - Works with adaptive thinking to guide thinking depth. - - - ``"low"`` — Minimal thinking, fastest responses. - - ``"medium"`` — Moderate thinking. - - ``"high"`` — Deep reasoning (default). - - ``"max"`` — Maximum effort. - - See https://docs.anthropic.com/en/docs/build-with-claude/effort. - """ - - output_format: dict[str, Any] | None = None - """Output format configuration for structured responses. - - When specified, the agent returns structured data matching the schema. - Matches the Messages API structure, e.g. - ``{"type": "json_schema", "schema": {"type": "object", "properties": {...}}}``. - """ - - enable_file_checkpointing: bool = False - """Enable file checkpointing to track file changes during the session. - - When enabled, files can be rewound to their state at any user message - using ``ClaudeSDKClient.rewind_files()``. File checkpointing creates - backups of files before they are modified so they can be restored later. - """ - - session_store: SessionStore | None = None - """Mirror session transcripts to an external store. - - When set, every transcript line written locally is also passed to - ``session_store.append()``, and ``resume`` can materialize from the store - when the local file is absent. - """ - - load_timeout_ms: int = 60_000 - """Timeout for each ``session_store.load()`` / ``list_subkeys()`` call during - resume materialization, in milliseconds. - - If the adapter doesn't settle within this window the query fails with a - clear error instead of hanging the iterator forever. A value of 0 means - immediate timeout; use a large value to effectively disable. - """ - - task_budget: TaskBudget | None = None - """API-side task budget in tokens. - - When set, the model is made aware of its remaining token budget so it can - pace tool use and wrap up before the limit. Sent as - ``output_config.task_budget`` with the ``task-budgets-2026-03-13`` beta - header. - """ - - -# SDK Control Protocol -class SDKControlInterruptRequest(TypedDict): - subtype: Literal["interrupt"] - - -class SDKControlPermissionRequest(TypedDict): - subtype: Literal["can_use_tool"] - tool_name: str - input: dict[str, Any] - # TODO: Add PermissionUpdate type here - permission_suggestions: list[Any] | None - blocked_path: str | None - tool_use_id: str - agent_id: NotRequired[str] - - -class SDKControlInitializeRequest(TypedDict): - subtype: Literal["initialize"] - hooks: dict[HookEvent, Any] | None - agents: NotRequired[dict[str, dict[str, Any]]] - - -class SDKControlSetPermissionModeRequest(TypedDict): - subtype: Literal["set_permission_mode"] - mode: PermissionMode - - -class SDKHookCallbackRequest(TypedDict): - subtype: Literal["hook_callback"] - callback_id: str - input: Any - tool_use_id: str | None - - -class SDKControlMcpMessageRequest(TypedDict): - subtype: Literal["mcp_message"] - server_name: str - message: Any - - -class SDKControlRewindFilesRequest(TypedDict): - subtype: Literal["rewind_files"] - user_message_id: str - - -class SDKControlMcpReconnectRequest(TypedDict): - """Reconnects a disconnected or failed MCP server.""" - - subtype: Literal["mcp_reconnect"] - # Note: wire protocol uses camelCase for this field - serverName: str - - -class SDKControlMcpToggleRequest(TypedDict): - """Enables or disables an MCP server.""" - - subtype: Literal["mcp_toggle"] - # Note: wire protocol uses camelCase for this field - serverName: str - enabled: bool - - -class SDKControlStopTaskRequest(TypedDict): - subtype: Literal["stop_task"] - task_id: str - - -class SDKControlRequest(TypedDict): - type: Literal["control_request"] - request_id: str - request: ( - SDKControlInterruptRequest - | SDKControlPermissionRequest - | SDKControlInitializeRequest - | SDKControlSetPermissionModeRequest - | SDKHookCallbackRequest - | SDKControlMcpMessageRequest - | SDKControlRewindFilesRequest - | SDKControlMcpReconnectRequest - | SDKControlMcpToggleRequest - | SDKControlStopTaskRequest - ) - - -class ControlResponse(TypedDict): - subtype: Literal["success"] - request_id: str - response: dict[str, Any] | None - - -class ControlErrorResponse(TypedDict): - subtype: Literal["error"] - request_id: str - error: str - - -class SDKControlResponse(TypedDict): - type: Literal["control_response"] - response: ControlResponse | ControlErrorResponse diff --git a/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/METADATA b/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/METADATA deleted file mode 100644 index 1fb06f04..00000000 --- a/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/METADATA +++ /dev/null @@ -1,84 +0,0 @@ -Metadata-Version: 2.4 -Name: click -Version: 8.4.2 -Summary: Composable command line interface toolkit -Maintainer-email: Pallets -Requires-Python: >=3.10 -Description-Content-Type: text/markdown -License-Expression: BSD-3-Clause -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Typing :: Typed -License-File: LICENSE.txt -Requires-Dist: colorama; platform_system == 'Windows' -Project-URL: Changes, https://click.palletsprojects.com/page/changes/ -Project-URL: Chat, https://discord.gg/pallets -Project-URL: Documentation, https://click.palletsprojects.com/ -Project-URL: Donate, https://palletsprojects.com/donate -Project-URL: Source, https://github.com/pallets/click/ - -
- -# Click - -Click is a Python package for creating beautiful command line interfaces -in a composable way with as little code as necessary. It's the "Command -Line Interface Creation Kit". It's highly configurable but comes with -sensible defaults out of the box. - -It aims to make the process of writing command line tools quick and fun -while also preventing any frustration caused by the inability to -implement an intended CLI API. - -Click in three points: - -- Arbitrary nesting of commands -- Automatic help page generation -- Supports lazy loading of subcommands at runtime - - -## A Simple Example - -```python -import click - -@click.command() -@click.option("--count", default=1, help="Number of greetings.") -@click.option("--name", prompt="Your name", help="The person to greet.") -def hello(count, name): - """Simple program that greets NAME for a total of COUNT times.""" - for _ in range(count): - click.echo(f"Hello, {name}!") - -if __name__ == '__main__': - hello() -``` - -``` -$ python hello.py --count=3 -Your name: Click -Hello, Click! -Hello, Click! -Hello, Click! -``` - - -## Donate - -The Pallets organization develops and supports Click and other popular -packages. In order to grow the community of contributors and users, and -allow the maintainers to devote more time to the projects, [please -donate today][]. - -[please donate today]: https://palletsprojects.com/donate - -## Contributing - -See our [detailed contributing documentation][contrib] for many ways to -contribute, including reporting issues, requesting features, asking or answering -questions, and making PRs. - -[contrib]: https://palletsprojects.com/contributing/ - diff --git a/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/RECORD b/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/RECORD deleted file mode 100644 index da6cf2ba..00000000 --- a/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/RECORD +++ /dev/null @@ -1,40 +0,0 @@ -click-8.4.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -click-8.4.2.dist-info/METADATA,sha256=GUyd2B1Wf5CB8CbH5AEGD7r6e8FHyOClizZotApkwDE,2621 -click-8.4.2.dist-info/RECORD,, -click-8.4.2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 -click-8.4.2.dist-info/licenses/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 -click/__init__.py,sha256=FId2fXCSJB3yeWD-e2uON-mBhFa2Yc9MvXGmHu8OXG0,4634 -click/__pycache__/__init__.cpython-312.pyc,, -click/__pycache__/_compat.cpython-312.pyc,, -click/__pycache__/_termui_impl.cpython-312.pyc,, -click/__pycache__/_textwrap.cpython-312.pyc,, -click/__pycache__/_utils.cpython-312.pyc,, -click/__pycache__/_winconsole.cpython-312.pyc,, -click/__pycache__/core.cpython-312.pyc,, -click/__pycache__/decorators.cpython-312.pyc,, -click/__pycache__/exceptions.cpython-312.pyc,, -click/__pycache__/formatting.cpython-312.pyc,, -click/__pycache__/globals.cpython-312.pyc,, -click/__pycache__/parser.cpython-312.pyc,, -click/__pycache__/shell_completion.cpython-312.pyc,, -click/__pycache__/termui.cpython-312.pyc,, -click/__pycache__/testing.cpython-312.pyc,, -click/__pycache__/types.cpython-312.pyc,, -click/__pycache__/utils.cpython-312.pyc,, -click/_compat.py,sha256=gPNtXQ9q-G6Qil2b-MC5CsHsGGcQ4u6YSWy9_tlmuhc,18879 -click/_termui_impl.py,sha256=CGdg24AeXijeGSzbu0Z7x3c4aaahVFjVBpEbbjhQ5K4,31730 -click/_textwrap.py,sha256=7Z0N7Vmn-66TNSTUwp6OXJbcUXRmYET9h9c2ucD8oQQ,6270 -click/_utils.py,sha256=eCZCtwJtsYD5QYkkNWJ8MY_8ABIjy8MczgMMyVY32rQ,996 -click/_winconsole.py,sha256=KSxfNbMlYRa6GOJuCLgsg2Pb3dVkgJNPqLJPae-Pa10,8543 -click/core.py,sha256=rZz76ihNTFV4Y2sxp3H-m93GxL2acD5Pqs0IobEvmuk,140616 -click/decorators.py,sha256=9e1Ndu4jhGAcP6RGdNPAwAWtuP9hEs4ETp1u3lKmH1o,19709 -click/exceptions.py,sha256=HvSY34G4auj_bYRR8-T8CU8Jwq_1-OcsRU4ezfozeEk,11862 -click/formatting.py,sha256=8SW2KGkvjfz9Q1NbeojMHuZBN0cfnQJDs4mqDP6oXms,10444 -click/globals.py,sha256=gM-Nh6A4M0HB_SgkaF5M4ncGGMDHc_flHXu9_oh4GEU,1923 -click/parser.py,sha256=oJ-fU_3mvxugIuNtHaCATZ56lgEmHRggjJiSqEgYrjA,19052 -click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -click/shell_completion.py,sha256=5tGGY5pV3mAZ17xT23OnuKrWqzEyyLVtrJ30npUxjkU,22618 -click/termui.py,sha256=Vn9ehmrQl92z2_6R4bVZOsHUI6j8LrT8u0RzNZUpCvY,33213 -click/testing.py,sha256=S9I-pspAlJH3RvZJWDQoJXb-M0nrAEJzXcUzrVXsT34,26458 -click/types.py,sha256=9G4DB-nBj-omA_XWsYwbQ3H9BkpH82wJj-kxIPScKmA,44788 -click/utils.py,sha256=XwrDxOzU__rnHn-rvJmJcD7ecbypUKMeDJQRjN2F-OA,20942 diff --git a/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/WHEEL deleted file mode 100644 index d8b9936d..00000000 --- a/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.12.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/licenses/LICENSE.txt b/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/licenses/LICENSE.txt deleted file mode 100644 index d12a8491..00000000 --- a/.venv/lib/python3.12/site-packages/click-8.4.2.dist-info/licenses/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2014 Pallets - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/lib/python3.12/site-packages/click/__init__.py b/.venv/lib/python3.12/site-packages/click/__init__.py deleted file mode 100644 index 64be7e0c..00000000 --- a/.venv/lib/python3.12/site-packages/click/__init__.py +++ /dev/null @@ -1,126 +0,0 @@ -""" -Click is a simple Python module inspired by the stdlib optparse to make -writing command line scripts fun. Unlike other modules, it's based -around a simple API that does not come with too much magic and is -composable. -""" - -from __future__ import annotations - -from .core import Argument as Argument -from .core import Command as Command -from .core import CommandCollection as CommandCollection -from .core import Context as Context -from .core import Group as Group -from .core import Option as Option -from .core import Parameter as Parameter -from .core import ParameterSource as ParameterSource -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 NoSuchCommand as NoSuchCommand -from .exceptions import NoSuchOption as NoSuchOption -from .exceptions import UsageError as UsageError -from .formatting import HelpFormatter as HelpFormatter -from .formatting import wrap_text as wrap_text -from .globals import get_current_context as get_current_context -from .termui import clear as clear -from .termui import confirm as confirm -from .termui import echo_via_pager as echo_via_pager -from .termui import edit as edit -from .termui import get_pager_file as get_pager_file -from .termui import getchar as getchar -from .termui import launch as launch -from .termui import pause as pause -from .termui import progressbar as progressbar -from .termui import prompt as prompt -from .termui import secho as secho -from .termui import style as style -from .termui import unstyle as unstyle -from .types import BOOL as BOOL -from .types import Choice as Choice -from .types import DateTime as DateTime -from .types import File as File -from .types import FLOAT as FLOAT -from .types import FloatRange as FloatRange -from .types import INT as INT -from .types import IntRange as IntRange -from .types import ParamType as ParamType -from .types import Path as Path -from .types import STRING as STRING -from .types import Tuple as Tuple -from .types import UNPROCESSED as UNPROCESSED -from .types import UUID as UUID -from .utils import echo as echo -from .utils import format_filename as format_filename -from .utils import get_app_dir as get_app_dir -from .utils import get_binary_stream as get_binary_stream -from .utils import get_text_stream as get_text_stream -from .utils import open_file as open_file - - -def __getattr__(name: str) -> object: - import warnings - - if name == "BaseCommand": - from .core import _BaseCommand - - warnings.warn( - "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" - " 'Command' instead.", - DeprecationWarning, - stacklevel=2, - ) - return _BaseCommand - - if name == "MultiCommand": - from .core import _MultiCommand - - warnings.warn( - "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" - " 'Group' instead.", - DeprecationWarning, - stacklevel=2, - ) - return _MultiCommand - - if name == "OptionParser": - from .parser import _OptionParser - - warnings.warn( - "'OptionParser' is deprecated and will be removed in Click 9.0. The" - " old parser is available in 'optparse'.", - DeprecationWarning, - stacklevel=2, - ) - return _OptionParser - - if name == "__version__": - import importlib.metadata - import warnings - - warnings.warn( - "The '__version__' attribute is deprecated and will be removed in" - " Click 9.1. Use feature detection or" - " 'importlib.metadata.version(\"click\")' instead.", - DeprecationWarning, - stacklevel=2, - ) - return importlib.metadata.version("click") - - raise AttributeError(name) diff --git a/.venv/lib/python3.12/site-packages/click/_compat.py b/.venv/lib/python3.12/site-packages/click/_compat.py deleted file mode 100644 index 134c4f38..00000000 --- a/.venv/lib/python3.12/site-packages/click/_compat.py +++ /dev/null @@ -1,626 +0,0 @@ -from __future__ import annotations - -import codecs -import collections.abc as cabc -import io -import os -import re -import sys -import typing as t -from types import TracebackType -from weakref import WeakKeyDictionary - -CYGWIN = sys.platform.startswith("cygwin") -WIN = sys.platform.startswith("win") -auto_wrap_for_ansi: t.Callable[[t.TextIO], t.TextIO] | None = None -_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") - - -def _make_text_stream( - stream: t.BinaryIO, - encoding: str | None, - errors: str | None, - force_readable: bool = False, - force_writable: bool = False, -) -> t.TextIO: - if encoding is None: - encoding = get_best_encoding(stream) - if errors is None: - errors = "replace" - return _NonClosingTextIOWrapper( - stream, - encoding, - errors, - line_buffering=True, - force_readable=force_readable, - force_writable=force_writable, - ) - - -def is_ascii_encoding(encoding: str) -> bool: - """Checks if a given encoding is ascii.""" - try: - return codecs.lookup(encoding).name == "ascii" - except LookupError: - return False - - -def get_best_encoding(stream: t.IO[t.Any]) -> str: - """Returns the default stream encoding if not found.""" - rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() - if is_ascii_encoding(rv): - return "utf-8" - return rv - - -class _NonClosingTextIOWrapper(io.TextIOWrapper): - def __init__( - self, - stream: t.BinaryIO, - encoding: str | None, - errors: str | None, - force_readable: bool = False, - force_writable: bool = False, - **extra: t.Any, - ) -> None: - self._stream = stream = t.cast( - t.BinaryIO, _FixupStream(stream, force_readable, force_writable) - ) - super().__init__(stream, encoding, errors, **extra) - - def __del__(self) -> None: - try: - self.detach() - except Exception: - pass - - def isatty(self) -> bool: - # https://bitbucket.org/pypy/pypy/issue/1803 - return self._stream.isatty() - - -class _FixupStream: - """The new io interface needs more from streams than streams - traditionally implement. As such, this fix-up code is necessary in - some circumstances. - - The forcing of readable and writable flags are there because some tools - put badly patched objects on sys (one such offender are certain version - of jupyter notebook). - """ - - def __init__( - self, - stream: t.BinaryIO, - force_readable: bool = False, - force_writable: bool = False, - ): - self._stream = stream - self._force_readable = force_readable - self._force_writable = force_writable - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._stream, name) - - def read1(self, size: int) -> bytes: - f = getattr(self._stream, "read1", None) - - if f is not None: - return t.cast(bytes, f(size)) - - return self._stream.read(size) - - def readable(self) -> bool: - if self._force_readable: - return True - x = getattr(self._stream, "readable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.read(0) - except Exception: - return False - return True - - def writable(self) -> bool: - if self._force_writable: - return True - x = getattr(self._stream, "writable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.write(b"") - except Exception: - try: - self._stream.write(b"") - except Exception: - return False - return True - - def seekable(self) -> bool: - x = getattr(self._stream, "seekable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.seek(self._stream.tell()) - except Exception: - return False - return True - - -def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: - try: - return isinstance(stream.read(0), bytes) - except Exception: - return default - # This happens in some cases where the stream was already - # closed. In this case, we assume the default. - - -def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: - try: - stream.write(b"") - except Exception: - try: - stream.write("") - return False - except Exception: - pass - return default - return True - - -def _find_binary_reader(stream: t.IO[t.Any]) -> t.BinaryIO | None: - # We need to figure out if the given stream is already binary. - # This can happen because the official docs recommend detaching - # the streams to get binary streams. Some code might do this, so - # we need to deal with this case explicitly. - if _is_binary_reader(stream, False): - return t.cast(t.BinaryIO, stream) - - buf = getattr(stream, "buffer", None) - - # Same situation here; this time we assume that the buffer is - # actually binary in case it's closed. - if buf is not None and _is_binary_reader(buf, True): - return t.cast(t.BinaryIO, buf) - - return None - - -def _find_binary_writer(stream: t.IO[t.Any]) -> t.BinaryIO | None: - # We need to figure out if the given stream is already binary. - # This can happen because the official docs recommend detaching - # the streams to get binary streams. Some code might do this, so - # we need to deal with this case explicitly. - if _is_binary_writer(stream, False): - return t.cast(t.BinaryIO, stream) - - buf = getattr(stream, "buffer", None) - - # Same situation here; this time we assume that the buffer is - # actually binary in case it's closed. - if buf is not None and _is_binary_writer(buf, True): - return t.cast(t.BinaryIO, buf) - - return None - - -def _stream_is_misconfigured(stream: t.TextIO) -> bool: - """A stream is misconfigured if its encoding is ASCII.""" - # If the stream does not have an encoding set, we assume it's set - # to ASCII. This appears to happen in certain unittest - # environments. It's not quite clear what the correct behavior is - # but this at least will force Click to recover somehow. - return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") - - -def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: str | None) -> bool: - """A stream attribute is compatible if it is equal to the - desired value or the desired value is unset and the attribute - has a value. - """ - stream_value = getattr(stream, attr, None) - return stream_value == value or (value is None and stream_value is not None) - - -def _is_compatible_text_stream( - stream: t.TextIO, encoding: str | None, errors: str | None -) -> bool: - """Check if a stream's encoding and errors attributes are - compatible with the desired values. - """ - return _is_compat_stream_attr( - stream, "encoding", encoding - ) and _is_compat_stream_attr(stream, "errors", errors) - - -def _force_correct_text_stream( - text_stream: t.IO[t.Any], - encoding: str | None, - errors: str | None, - is_binary: t.Callable[[t.IO[t.Any], bool], bool], - find_binary: t.Callable[[t.IO[t.Any]], t.BinaryIO | None], - force_readable: bool = False, - force_writable: bool = False, -) -> t.TextIO: - if is_binary(text_stream, False): - binary_reader = t.cast(t.BinaryIO, text_stream) - else: - text_stream = t.cast(t.TextIO, text_stream) - # If the stream looks compatible, and won't default to a - # misconfigured ascii encoding, return it as-is. - if _is_compatible_text_stream(text_stream, encoding, errors) and not ( - encoding is None and _stream_is_misconfigured(text_stream) - ): - return text_stream - - # Otherwise, get the underlying binary reader. - possible_binary_reader = find_binary(text_stream) - - # If that's not possible, silently use the original reader - # and get mojibake instead of exceptions. - if possible_binary_reader is None: - return text_stream - - binary_reader = possible_binary_reader - - # Default errors to replace instead of strict in order to get - # something that works. - if errors is None: - errors = "replace" - - # Wrap the binary stream in a text stream with the correct - # encoding parameters. - return _make_text_stream( - binary_reader, - encoding, - errors, - force_readable=force_readable, - force_writable=force_writable, - ) - - -def _force_correct_text_reader( - text_reader: t.IO[t.Any], - encoding: str | None, - errors: str | None, - force_readable: bool = False, -) -> t.TextIO: - return _force_correct_text_stream( - text_reader, - encoding, - errors, - _is_binary_reader, - _find_binary_reader, - force_readable=force_readable, - ) - - -def _force_correct_text_writer( - text_writer: t.IO[t.Any], - encoding: str | None, - errors: str | None, - force_writable: bool = False, -) -> t.TextIO: - return _force_correct_text_stream( - text_writer, - encoding, - errors, - _is_binary_writer, - _find_binary_writer, - force_writable=force_writable, - ) - - -def get_binary_stdin() -> t.BinaryIO: - reader = _find_binary_reader(sys.stdin) - if reader is None: - raise RuntimeError("Was not able to determine binary stream for sys.stdin.") - return reader - - -def get_binary_stdout() -> t.BinaryIO: - writer = _find_binary_writer(sys.stdout) - if writer is None: - raise RuntimeError("Was not able to determine binary stream for sys.stdout.") - return writer - - -def get_binary_stderr() -> t.BinaryIO: - writer = _find_binary_writer(sys.stderr) - if writer is None: - raise RuntimeError("Was not able to determine binary stream for sys.stderr.") - return writer - - -def get_text_stdin(encoding: str | None = None, errors: str | None = None) -> t.TextIO: - rv = _get_windows_console_stream(sys.stdin, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) - - -def get_text_stdout(encoding: str | None = None, errors: str | None = None) -> t.TextIO: - rv = _get_windows_console_stream(sys.stdout, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) - - -def get_text_stderr(encoding: str | None = None, errors: str | None = None) -> t.TextIO: - rv = _get_windows_console_stream(sys.stderr, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) - - -def _wrap_io_open( - file: str | os.PathLike[str] | int, - mode: str, - encoding: str | None, - errors: str | None, -) -> t.IO[t.Any]: - """Handles not passing ``encoding`` and ``errors`` in binary mode.""" - if "b" in mode: - return open(file, mode) - - return open(file, mode, encoding=encoding, errors=errors) - - -def open_stream( - filename: str | os.PathLike[str], - mode: str = "r", - encoding: str | None = None, - errors: str | None = "strict", - atomic: bool = False, -) -> tuple[t.IO[t.Any], bool]: - binary = "b" in mode - filename = os.fspath(filename) - - # Standard streams first. These are simple because they ignore the - # atomic flag. Use fsdecode to handle Path("-"). - if os.fsdecode(filename) == "-": - if any(m in mode for m in ["w", "a", "x"]): - if binary: - return get_binary_stdout(), False - return get_text_stdout(encoding=encoding, errors=errors), False - if binary: - return get_binary_stdin(), False - return get_text_stdin(encoding=encoding, errors=errors), False - - # Non-atomic writes directly go out through the regular open functions. - if not atomic: - return _wrap_io_open(filename, mode, encoding, errors), True - - # Some usability stuff for atomic writes - if "a" in mode: - raise ValueError( - "Appending to an existing file is not supported, because that" - " would involve an expensive `copy`-operation to a temporary" - " file. Open the file in normal `w`-mode and copy explicitly" - " if that's what you're after." - ) - if "x" in mode: - raise ValueError("Use the `overwrite`-parameter instead.") - if "w" not in mode: - raise ValueError("Atomic writes only make sense with `w`-mode.") - - # Atomic writes are more complicated. They work by opening a file - # as a proxy in the same folder and then using the fdopen - # functionality to wrap it in a Python file. Then we wrap it in an - # atomic file that moves the file over on close. - import errno - import random - - try: - perm: int | None = os.stat(filename).st_mode - except OSError: - perm = None - - flags = os.O_RDWR | os.O_CREAT | os.O_EXCL - - if binary: - flags |= getattr(os, "O_BINARY", 0) - - while True: - tmp_filename = os.path.join( - os.path.dirname(filename), - f".__atomic-write{random.randrange(1 << 32):08x}", - ) - try: - fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) - break - except OSError as e: - if e.errno == errno.EEXIST or ( - os.name == "nt" - and e.errno == errno.EACCES - and os.path.isdir(e.filename) - and os.access(e.filename, os.W_OK) - ): - continue - raise - - if perm is not None: - os.chmod(tmp_filename, perm) # in case perm includes bits in umask - - f = _wrap_io_open(fd, mode, encoding, errors) - af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) - return t.cast(t.IO[t.Any], af), True - - -class _AtomicFile: - def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: - self._f = f - self._tmp_filename = tmp_filename - self._real_filename = real_filename - self.closed = False - - @property - def name(self) -> str: - return self._real_filename - - def close(self, delete: bool = False) -> None: - if self.closed: - return - self._f.close() - os.replace(self._tmp_filename, self._real_filename) - self.closed = True - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._f, name) - - def __enter__(self) -> _AtomicFile: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - tb: TracebackType | None, - ) -> None: - self.close(delete=exc_type is not None) - - def __repr__(self) -> str: - return repr(self._f) - - -def strip_ansi(value: str) -> str: - return _ansi_re.sub("", value) - - -def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: - while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): - stream = stream._stream - - return stream.__class__.__module__.startswith("ipykernel.") - - -def should_strip_ansi( - stream: t.IO[t.Any] | None = None, color: bool | None = None -) -> bool: - if color is None: - if stream is None: - stream = sys.stdin - elif hasattr(stream, "color"): - # ._termui_impl.MaybeStripAnsi handles stripping ansi itself, - # so we don't need to strip it here - return False - return not isatty(stream) and not _is_jupyter_kernel_output(stream) - return not color - - -# On Windows, wrap the output streams with colorama to support ANSI -# color codes. -# NOTE: double check is needed so mypy does not analyze this on Linux -if sys.platform.startswith("win") and WIN: - from ._winconsole import _get_windows_console_stream - - def _get_argv_encoding() -> str: - import locale - - return locale.getpreferredencoding() - - _ansi_stream_wrappers: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() - - def auto_wrap_for_ansi(stream: t.TextIO, color: bool | None = None) -> t.TextIO: - """Support ANSI color and style codes on Windows by wrapping a - stream with colorama. - """ - try: - cached = _ansi_stream_wrappers.get(stream) - except Exception: - cached = None - - if cached is not None: - return cached - - import colorama - - strip = should_strip_ansi(stream, color) - ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) - rv = t.cast(t.TextIO, ansi_wrapper.stream) - _write = rv.write - - def _safe_write(s: str) -> int: - try: - return _write(s) - except BaseException: - ansi_wrapper.reset_all() - raise - - rv.write = _safe_write # type: ignore[method-assign] - - try: - _ansi_stream_wrappers[stream] = rv - except Exception: - pass - - return rv - -else: - - def _get_argv_encoding() -> str: - return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() - - def _get_windows_console_stream( - f: t.TextIO, encoding: str | None, errors: str | None - ) -> t.TextIO | None: - return None - - -def term_len(x: str) -> int: - return len(strip_ansi(x)) - - -def isatty(stream: t.IO[t.Any]) -> bool: - try: - return stream.isatty() - except Exception: - return False - - -def _make_cached_stream_func( - src_func: t.Callable[[], t.TextIO | None], - wrapper_func: t.Callable[[], t.TextIO], -) -> t.Callable[[], t.TextIO | None]: - cache: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() - - def func() -> t.TextIO | None: - stream = src_func() - - if stream is None: - return None - - try: - rv = cache.get(stream) - except Exception: - rv = None - if rv is not None: - return rv - rv = wrapper_func() - try: - cache[stream] = rv - except Exception: - pass - return rv - - return func - - -_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) -_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) -_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) - - -binary_streams: cabc.Mapping[str, t.Callable[[], t.BinaryIO]] = { - "stdin": get_binary_stdin, - "stdout": get_binary_stdout, - "stderr": get_binary_stderr, -} - -text_streams: cabc.Mapping[str, t.Callable[[str | None, str | None], t.TextIO]] = { - "stdin": get_text_stdin, - "stdout": get_text_stdout, - "stderr": get_text_stderr, -} diff --git a/.venv/lib/python3.12/site-packages/click/_termui_impl.py b/.venv/lib/python3.12/site-packages/click/_termui_impl.py deleted file mode 100644 index fadae940..00000000 --- a/.venv/lib/python3.12/site-packages/click/_termui_impl.py +++ /dev/null @@ -1,945 +0,0 @@ -""" -This module contains implementations for the termui module. To keep the -import time of Click down, some infrequently used functionality is -placed in this module and only imported as needed. -""" - -from __future__ import annotations - -import collections.abc as cabc -import contextlib -import io -import math -import os -import shlex -import sys -import time -import typing as t -from gettext import gettext as _ -from io import StringIO -from pathlib import Path -from types import TracebackType - -from ._compat import _default_text_stdout -from ._compat import CYGWIN -from ._compat import get_best_encoding -from ._compat import isatty -from ._compat import strip_ansi -from ._compat import term_len -from ._compat import WIN -from .exceptions import ClickException -from .utils import echo -from .utils import KeepOpenFile - -V = t.TypeVar("V") - - -class _BufferedTextPagerStream(t.Protocol): - buffer: t.BinaryIO - - -def _has_binary_buffer( - stream: t.BinaryIO | t.TextIO, -) -> t.TypeGuard[_BufferedTextPagerStream]: - # TextIO is wider than TextIOWrapper; text-only streams such as StringIO - # are valid TextIO values but do not expose a binary buffer to wrap. - return getattr(stream, "buffer", None) is not None - - -if os.name == "nt": - BEFORE_BAR = "\r" - AFTER_BAR = "\n" -else: - BEFORE_BAR = "\r\033[?25l" - AFTER_BAR = "\033[?25h\n" - - -class ProgressBar(t.Generic[V]): - def __init__( - self, - iterable: cabc.Iterable[V] | None, - length: int | None = None, - fill_char: str = "#", - empty_char: str = " ", - bar_template: str = "%(bar)s", - info_sep: str = " ", - hidden: bool = False, - show_eta: bool = True, - show_percent: bool | None = None, - show_pos: bool = False, - item_show_func: t.Callable[[V | None], str | None] | None = None, - label: str | None = None, - file: t.TextIO | None = None, - color: bool | None = None, - update_min_steps: int = 1, - width: int = 30, - ) -> None: - self.fill_char = fill_char - self.empty_char = empty_char - self.bar_template = bar_template - self.info_sep = info_sep - self.hidden = hidden - self.show_eta = show_eta - self.show_percent = show_percent - self.show_pos = show_pos - self.item_show_func = item_show_func - self.label: str = label or "" - - if file is None: - file = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if file is None: - file = StringIO() - - self.file = file - self.color = color - self.update_min_steps = update_min_steps - self._completed_intervals = 0 - self.width: int = width - self.autowidth: bool = width == 0 - - if length is None: - from operator import length_hint - - length = length_hint(iterable, -1) - - if length == -1: - length = None - if iterable is None: - if length is None: - raise TypeError("iterable or length is required") - iterable = t.cast("cabc.Iterable[V]", range(length)) - self.iter: cabc.Iterable[V] = iter(iterable) - self.length = length - self.pos: int = 0 - self.avg: list[float] = [] - self.last_eta: float - self.start: float - self.start = self.last_eta = time.time() - self.eta_known: bool = False - self.finished: bool = False - self.max_width: int | None = None - self.entered: bool = False - self.current_item: V | None = None - self._is_atty = isatty(self.file) - self._last_line: str | None = None - - def __enter__(self) -> ProgressBar[V]: - self.entered = True - self.render_progress() - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - tb: TracebackType | None, - ) -> None: - self.render_finish() - - def __iter__(self) -> cabc.Iterator[V]: - if not self.entered: - raise RuntimeError("You need to use progress bars in a with block.") - self.render_progress() - return self.generator() - - def __next__(self) -> V: - # Iteration is defined in terms of a generator function, - # returned by iter(self); use that to define next(). This works - # because `self.iter` is an iterable consumed by that generator, - # so it is re-entry safe. Calling `next(self.generator())` - # twice works and does "what you want". - return next(iter(self)) - - def render_finish(self) -> None: - if self.hidden or not self._is_atty: - return - self.file.write(AFTER_BAR) - self.file.flush() - - @property - def pct(self) -> float: - if self.finished: - return 1.0 - return min(self.pos / (float(self.length or 1) or 1), 1.0) - - @property - def time_per_iteration(self) -> float: - if not self.avg: - return 0.0 - return sum(self.avg) / float(len(self.avg)) - - @property - def eta(self) -> float: - if self.length is not None and not self.finished: - return self.time_per_iteration * (self.length - self.pos) - return 0.0 - - def format_eta(self) -> str: - if self.eta_known: - t = int(self.eta) - seconds = t % 60 - t //= 60 - minutes = t % 60 - t //= 60 - hours = t % 24 - t //= 24 - if t > 0: - return "{d}{day_label} {h:02}:{m:02}:{s:02}".format( - d=t, - day_label=_("d"), - h=hours, - m=minutes, - s=seconds, - ) - 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: - if self.hidden: - return - - if not self._is_atty: - # Only output the label once if the output is not a TTY. - if self._last_line != self.label: - self._last_line = self.label - echo(self.label, file=self.file, color=self.color) - return - - buf = [] - # Update width in case the terminal has been resized - if self.autowidth: - import shutil - - old_width = self.width - self.width = 0 - clutter_length = term_len(self.format_progress_line()) - new_width = max(0, shutil.get_terminal_size().columns - clutter_length) - if new_width < old_width and self.max_width is not None: - buf.append(BEFORE_BAR) - buf.append(" " * self.max_width) - self.max_width = new_width - self.width = new_width - - clear_width = self.width - if self.max_width is not None: - clear_width = self.max_width - - buf.append(BEFORE_BAR) - line = self.format_progress_line() - line_len = term_len(line) - if self.max_width is None or self.max_width < line_len: - self.max_width = line_len - - buf.append(line) - buf.append(" " * (clear_width - line_len)) - line = "".join(buf) - # Render the line only if it changed. - - if line != self._last_line: - self._last_line = line - echo(line, file=self.file, color=self.color, nl=False) - self.file.flush() - - def make_step(self, n_steps: int) -> None: - self.pos += n_steps - if self.length is not None and self.pos >= self.length: - self.finished = True - - if (time.time() - self.last_eta) < 1.0: - return - - self.last_eta = time.time() - - # self.avg is a rolling list of length <= 7 of steps where steps are - # defined as time elapsed divided by the total progress through - # self.length. - if self.pos: - step = (time.time() - self.start) / self.pos - else: - step = time.time() - self.start - - self.avg = self.avg[-6:] + [step] - - self.eta_known = self.length is not None - - def update(self, n_steps: int, current_item: V | None = None) -> None: - """Update the progress bar by advancing a specified number of - steps, and optionally set the ``current_item`` for this new - position. - - :param n_steps: Number of steps to advance. - :param current_item: Optional item to set as ``current_item`` - for the updated position. - - .. versionchanged:: 8.0 - Added the ``current_item`` optional parameter. - - .. versionchanged:: 8.0 - Only render when the number of steps meets the - ``update_min_steps`` threshold. - """ - if current_item is not None: - self.current_item = current_item - - self._completed_intervals += n_steps - - if self._completed_intervals >= self.update_min_steps: - self.make_step(self._completed_intervals) - self.render_progress() - self._completed_intervals = 0 - - def finish(self) -> None: - self.eta_known = False - self.current_item = None - self.finished = True - - def generator(self) -> cabc.Iterator[V]: - """Return a generator which yields the items added to the bar - during construction, and updates the progress bar *after* the - yielded block returns. - """ - # WARNING: the iterator interface for `ProgressBar` relies on - # this and only works because this is a simple generator which - # doesn't create or manage additional state. If this function - # changes, the impact should be evaluated both against - # `iter(bar)` and `next(bar)`. `next()` in particular may call - # `self.generator()` repeatedly, and this must remain safe in - # order for that interface to work. - if not self.entered: - raise RuntimeError("You need to use progress bars in a with block.") - - if not self._is_atty: - yield from self.iter - else: - for rv in self.iter: - self.current_item = rv - - # This allows show_item_func to be updated before the - # item is processed. Only trigger at the beginning of - # the update interval. - if self._completed_intervals == 0: - self.render_progress() - - yield rv - self.update(1) - - self.finish() - self.render_progress() - - -class MaybeStripAnsi(io.TextIOWrapper): - def __init__(self, stream: t.IO[bytes], *, color: bool, **kwargs: t.Any): - super().__init__(stream, **kwargs) - self.color = color - - def write(self, text: str) -> int: - if not self.color: - text = strip_ansi(text) - return super().write(text) - - -def _pager_contextmanager( - color: bool | None = None, -) -> t.ContextManager[tuple[t.BinaryIO | t.TextIO, str, bool]]: - """Decide what method to use for paging through text.""" - stdout = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if stdout is None: - stdout = StringIO() - - if not isatty(sys.stdin) or not isatty(stdout): - return _nullpager(stdout, color) - - # Split using POSIX mode (the default) so that quote characters are - # stripped from tokens and quoted Windows paths are preserved. - # Non-POSIX mode retains quotes in tokens, and wrapping tokens - # with shlex.quote re-introduces quoting issues on Windows. - pager_cmd_parts = shlex.split(os.environ.get("PAGER", "")) - if pager_cmd_parts: - if WIN: - return _tempfilepager(pager_cmd_parts, color) - return _pipepager(pager_cmd_parts, color) - - if os.environ.get("TERM") in ("dumb", "emacs"): - return _nullpager(stdout, color) - if WIN or sys.platform.startswith("os2"): - return _tempfilepager(["more"], color) - return _pipepager(["less"], color) - - -@contextlib.contextmanager -def get_pager_file(color: bool | None = None) -> t.Generator[t.TextIO, None, None]: - """Context manager. - - Yields a writable file-like object which can be used as an output pager. - - .. versionadded:: 8.4.0 - - :param color: controls if the pager supports ANSI colors or not. The - default is autodetection. - """ - with _pager_contextmanager(color=color) as (stream, encoding, color): - # Split streams by capabilities rather than the abstract TextIO / - # BinaryIO annotations: buffered text streams can be unwrapped to bytes, - # while other streams are yielded as-is. - wrapper: MaybeStripAnsi | None = None - if _has_binary_buffer(stream): - # Text stream backed by a binary buffer. - wrapper = MaybeStripAnsi(stream.buffer, color=color, encoding=encoding) - stream = wrapper - try: - # Narrow the BinaryIO | TextIO union that _pager_contextmanager - # yields; the caller writes text to the pager. - yield t.cast(t.TextIO, stream) - finally: - try: - stream.flush() - finally: - # Hand the binary buffer back to the pager that produced it - # rather than letting this TextIOWrapper close it on garbage - # collection. The pager owns the buffer's lifecycle: subprocess - # pipes and temp files are closed by their own helpers, while a - # borrowed stdout must stay open for the caller. detach() runs - # even if flush() raised, so the buffer is never closed here. - if wrapper is not None: - wrapper.detach() - - -@contextlib.contextmanager -def _pipepager( - cmd_parts: list[str], color: bool | None = None -) -> t.Iterator[tuple[t.BinaryIO | t.TextIO, str, bool]]: - """Page through text by feeding it to another program. - - Invokes the pager via :class:`subprocess.Popen` with an ``argv`` list - produced by :func:`shlex.split`. The command is resolved to an absolute - path with :func:`shutil.which` as recommended by the - :mod:`subprocess` docs for Windows compatibility. - - Invoking a pager through this might support colors: if piping to - ``less`` and the user hasn't decided on colors, ``LESS=-R`` is set - automatically. - """ - # Split the command into the invoked CLI and its parameters. - if not cmd_parts: - # No usable pager: fall back to stdout through _nullpager so it gets the - # same borrowed-stream handling and the caller's stream is not closed. - stdout = _default_text_stdout() or StringIO() - with _nullpager(stdout, color) as rv: - yield rv - return - - import shutil - - cmd = cmd_parts[0] - cmd_params = cmd_parts[1:] - - cmd_filepath = shutil.which(cmd) - if not cmd_filepath: - # No usable pager: fall back to stdout through _nullpager so it gets the - # same borrowed-stream handling and the caller's stream is not closed. - stdout = _default_text_stdout() or StringIO() - with _nullpager(stdout, color) as rv: - yield rv - return - - # Produces a normalized absolute path string. - # multi-call binaries such as busybox derive their identity from the symlink - # less -> busybox. resolve() causes them to misbehave. (eg. less becomes busybox) - cmd_path = Path(cmd_filepath).absolute() - cmd_name = cmd_path.name - - import subprocess - - # Make a local copy of the environment to not affect the global one. - env = dict(os.environ) - - # If we're piping to less and the user hasn't decided on colors, we enable - # them by default we find the -R flag in the command line arguments. - if color is None and cmd_name == "less": - less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_params)}" - if not less_flags: - env["LESS"] = "-R" - color = True - elif "r" in less_flags or "R" in less_flags: - color = True - - if color is None: - color = False - - c = subprocess.Popen( - [str(cmd_path)] + cmd_params, - shell=False, - stdin=subprocess.PIPE, - env=env, - errors="replace", - text=True, - ) - stdin = t.cast(t.BinaryIO, c.stdin) - encoding = get_best_encoding(stdin) - try: - yield stdin, encoding, color - except BrokenPipeError: - # In case the pager exited unexpectedly, ignore the broken pipe error. - pass - except Exception as e: - # In case there is an exception we want to close the pager immediately - # and let the caller handle it. - # Otherwise the pager will keep running, and the user may not notice - # the error message, or worse yet it may leave the terminal in a broken state. - c.terminate() - raise e - finally: - # We must close stdin and wait for the pager to exit before we continue - try: - stdin.close() - # Close implies flush, so it might throw a BrokenPipeError if the pager - # process exited already. - except BrokenPipeError: - pass - - # Less doesn't respect ^C, but catches it for its own UI purposes (aborting - # search or other commands inside less). - # - # That means when the user hits ^C, the parent process (click) terminates, - # but less is still alive, paging the output and messing up the terminal. - # - # If the user wants to make the pager exit on ^C, they should set - # `LESS='-K'`. It's not our decision to make. - while True: - try: - c.wait() - except KeyboardInterrupt: - pass - else: - break - - -@contextlib.contextmanager -def _tempfilepager( - cmd_parts: list[str], color: bool | None = None -) -> t.Iterator[tuple[t.BinaryIO | t.TextIO, str, bool]]: - """Page through text by invoking a program on a temporary file. - - Used as the primary pager strategy on Windows (where piping to - ``more`` adds spurious ``\\r\\n``), and as a fallback on other - platforms. The command is resolved to an absolute path with - :func:`shutil.which`. - """ - # Split the command into the invoked CLI and its parameters. - if not cmd_parts: - # No usable pager: fall back to stdout through _nullpager so it gets the - # same borrowed-stream handling and the caller's stream is not closed. - stdout = _default_text_stdout() or StringIO() - with _nullpager(stdout, color) as rv: - yield rv - return - - import shutil - import subprocess - - cmd = cmd_parts[0] - - cmd_filepath = shutil.which(cmd) - if not cmd_filepath: - # No usable pager: fall back to stdout through _nullpager so it gets the - # same borrowed-stream handling and the caller's stream is not closed. - stdout = _default_text_stdout() or StringIO() - with _nullpager(stdout, color) as rv: - yield rv - return - - # Produces a normalized absolute path string. - # multi-call binaries such as busybox derive their identity from the symlink - # less -> busybox. resolve() causes them to misbehave. (eg. less becomes busybox) - cmd_path = Path(cmd_filepath).absolute() - - import tempfile - - encoding = get_best_encoding(sys.stdout) - if color is None: - color = False - # On Windows, NamedTemporaryFile cannot be opened by another process - # while Python still has it open, so we use delete=False and clean up manually - # rather than using a contextmanager here. - f = tempfile.NamedTemporaryFile(mode="wb", delete=False) - try: - yield t.cast(t.BinaryIO, f), encoding, color - f.flush() - f.close() - subprocess.call([str(cmd_path), f.name]) - finally: - os.unlink(f.name) - - -@contextlib.contextmanager -def _nullpager( - stream: t.TextIO, color: bool | None = None -) -> t.Iterator[tuple[t.TextIO, str, bool]]: - """Simply print unformatted text. This is the ultimate fallback. Don't close the - output stream in this case, since it's coming from elsewhere rather than our - internal helpers. - - The stream is wrapped in :class:`~click.utils.KeepOpenFile` so that, as a - borrowed stream, it is not closed by a ``with`` block. The wrapper that - :func:`get_pager_file` builds around it is detached rather than closed. - """ - encoding = get_best_encoding(stream) - - if color is None: - color = False - - yield KeepOpenFile(stream), encoding, color # type: ignore[misc] - - -class Editor: - def __init__( - self, - editor: str | None = None, - env: cabc.Mapping[str, str] | None = None, - require_save: bool = True, - extension: str = ".txt", - ) -> None: - self.editor = editor - self.env = env - self.require_save = require_save - self.extension = extension - - def get_editor(self) -> str: - if self.editor is not None: - return self.editor - for key in "VISUAL", "EDITOR": - rv = os.environ.get(key) - if rv: - return rv - if WIN: - return "notepad" - - from shutil import which - - for editor in "sensible-editor", "vim", "nano": - if which(editor) is not None: - return editor - return "vi" - - def edit_files(self, filenames: cabc.Iterable[str]) -> None: - """Open files in the user's editor.""" - import shlex - import subprocess - - editor = self.get_editor() - environ: dict[str, str] | None = None - - if self.env: - environ = os.environ.copy() - environ.update(self.env) - - try: - # Split in POSIX mode (the default) for the same reasons as - # in pager(): strips quotes from tokens and preserves quoted - # Windows paths. - c = subprocess.Popen( - args=shlex.split(editor) + list(filenames), - env=environ, - ) - exit_code = c.wait() - if exit_code != 0: - raise ClickException( - _("{editor}: Editing failed").format(editor=editor) - ) - except OSError as e: - raise ClickException( - _("{editor}: Editing failed: {e}").format(editor=editor, e=e) - ) from e - - @t.overload - def edit(self, text: bytes | bytearray) -> bytes | None: ... - - # We cannot know whether or not the type expected is str or bytes when None - # is passed, so str is returned as that was what was done before. - @t.overload - def edit(self, text: str | None) -> str | None: ... - - def edit(self, text: str | bytes | bytearray | None) -> str | bytes | None: - import tempfile - - if text is None: - data: bytes | bytearray = b"" - elif isinstance(text, (bytes, bytearray)): - data = text - else: - if text and not text.endswith("\n"): - text += "\n" - - if WIN: - data = text.replace("\n", "\r\n").encode("utf-8-sig") - else: - data = text.encode("utf-8") - - fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) - f: t.BinaryIO - - try: - with os.fdopen(fd, "wb") as f: - f.write(data) - - # If the filesystem resolution is 1 second, like Mac OS - # 10.12 Extended, or 2 seconds, like FAT32, and the editor - # closes very fast, require_save can fail. Set the modified - # time to be 2 seconds in the past to work around this. - os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) - # Depending on the resolution, the exact value might not be - # recorded, so get the new recorded value. - timestamp = os.path.getmtime(name) - - self.edit_files((name,)) - - if self.require_save and os.path.getmtime(name) == timestamp: - return None - - with open(name, "rb") as f: - rv = f.read() - - if isinstance(text, (bytes, bytearray)): - return rv - - return rv.decode("utf-8-sig").replace("\r\n", "\n") - finally: - os.unlink(name) - - -def open_url(url: str, wait: bool = False, locate: bool = False) -> int: - import subprocess - - def _unquote_file(url: str) -> str: - from urllib.parse import unquote - - if url.startswith("file://"): - url = unquote(url[7:]) - - return url - - if sys.platform == "darwin": - args = ["open"] - if wait: - args.append("-W") - if locate: - args.append("-R") - args.append(_unquote_file(url)) - null = open("/dev/null", "w") - try: - return subprocess.Popen(args, stderr=null).wait() - finally: - null.close() - elif WIN: - if locate: - url = _unquote_file(url) - args = ["explorer", "/select,", url] - try: - return subprocess.call(args) - except OSError: - return 127 - else: - try: - os.startfile(url) # type: ignore[attr-defined] - except OSError: - return 127 - return 0 - elif CYGWIN: - if locate: - url = _unquote_file(url) - args = ["cygstart", os.path.dirname(url)] - else: - args = ["cygstart"] - if wait: - args.append("-w") - args.append(url) - try: - return subprocess.call(args) - except OSError: - # Command not found - return 127 - - try: - if locate: - url = os.path.dirname(_unquote_file(url)) or "." - else: - url = _unquote_file(url) - c = subprocess.Popen(["xdg-open", url]) - if wait: - return c.wait() - return 0 - except OSError: - if url.startswith(("http://", "https://")) and not locate and not wait: - import webbrowser - - webbrowser.open(url) - return 0 - return 1 - - -def _translate_ch_to_exc(ch: str) -> None: - if ch == "\x03": - raise KeyboardInterrupt() - - if ch == "\x04" and not WIN: # Unix-like, Ctrl+D - raise EOFError() - - if ch == "\x1a" and WIN: # Windows, Ctrl+Z - raise EOFError() - - -if sys.platform == "win32": - import msvcrt - - @contextlib.contextmanager - def raw_terminal() -> cabc.Iterator[int]: - yield -1 - - def getchar(echo: bool) -> str: - # The function `getch` will return a bytes object corresponding to - # the pressed character. Since Windows 10 build 1803, it will also - # return \x00 when called a second time after pressing a regular key. - # - # `getwch` does not share this probably-bugged behavior. Moreover, it - # returns a Unicode object by default, which is what we want. - # - # Either of these functions will return \x00 or \xe0 to indicate - # a special key, and you need to call the same function again to get - # the "rest" of the code. The fun part is that \u00e0 is - # "latin small letter a with grave", so if you type that on a French - # keyboard, you _also_ get a \xe0. - # E.g., consider the Up arrow. This returns \xe0 and then \x48. The - # resulting Unicode string reads as "a with grave" + "capital H". - # This is indistinguishable from when the user actually types - # "a with grave" and then "capital H". - # - # When \xe0 is returned, we assume it's part of a special-key sequence - # and call `getwch` again, but that means that when the user types - # the \u00e0 character, `getchar` doesn't return until a second - # character is typed. - # The alternative is returning immediately, but that would mess up - # cross-platform handling of arrow keys and others that start with - # \xe0. Another option is using `getch`, but then we can't reliably - # read non-ASCII characters, because return values of `getch` are - # limited to the current 8-bit codepage. - # - # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` - # is doing the right thing in more situations than with `getch`. - - if echo: - func = t.cast(t.Callable[[], str], msvcrt.getwche) - else: - func = t.cast(t.Callable[[], str], msvcrt.getwch) - - rv = func() - - if rv in ("\x00", "\xe0"): - # \x00 and \xe0 are control characters that indicate special key, - # see above. - rv += func() - - _translate_ch_to_exc(rv) - return rv - -else: - import termios - import tty - - @contextlib.contextmanager - def raw_terminal() -> cabc.Iterator[int]: - f: t.TextIO | None - fd: int - - if not isatty(sys.stdin): - f = open("/dev/tty") - fd = f.fileno() - else: - fd = sys.stdin.fileno() - f = None - - try: - old_settings = termios.tcgetattr(fd) - - try: - tty.setraw(fd) - yield fd - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - sys.stdout.flush() - - if f is not None: - f.close() - except termios.error: - pass - - def getchar(echo: bool) -> str: - with raw_terminal() as fd: - ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") - - if echo and isatty(sys.stdout): - sys.stdout.write(ch) - - _translate_ch_to_exc(ch) - return ch diff --git a/.venv/lib/python3.12/site-packages/click/_textwrap.py b/.venv/lib/python3.12/site-packages/click/_textwrap.py deleted file mode 100644 index 82840f2d..00000000 --- a/.venv/lib/python3.12/site-packages/click/_textwrap.py +++ /dev/null @@ -1,188 +0,0 @@ -from __future__ import annotations - -import collections.abc as cabc -import textwrap -from contextlib import contextmanager - -from ._compat import _ansi_re -from ._compat import term_len - - -def _truncate_visible(text: str, n: int) -> str: - """Return the longest prefix of ``text`` containing at most ``n`` visible - characters. - - ANSI escape sequences inside the prefix are kept intact and do not count - toward the visible width. A cut is never placed inside an escape sequence. - """ - if n <= 0: - return "" - - visible = 0 - i = 0 - cut = 0 - end = len(text) - while i < end: - m = _ansi_re.match(text, i) - if m is not None: - i = m.end() - continue - visible += 1 - i += 1 - cut = i - if visible >= n: - break - return text[:cut] - - -class TextWrapper(textwrap.TextWrapper): - """``textwrap.TextWrapper`` variant that measures widths by visible - character count. - - ANSI escape sequences embedded in chunks, indents, or the placeholder are - excluded from the width budget. Without this, styled help text (a styled - ``Usage:`` prefix, a colorized option name, ...) would be wrapped earlier - than its visible length warrants and tokens would split mid-word. - """ - - def _handle_long_word( - self, - reversed_chunks: list[str], - cur_line: list[str], - cur_len: int, - width: int, - ) -> None: - space_left = max(width - cur_len, 1) - - if self.break_long_words: - last = reversed_chunks[-1] - cut = _truncate_visible(last, space_left) - res = last[len(cut) :] - cur_line.append(cut) - reversed_chunks[-1] = res - elif not cur_line: - cur_line.append(reversed_chunks.pop()) - - def _wrap_chunks(self, chunks: list[str]) -> list[str]: - """Wrap chunks counting widths in visible characters. - - Mirrors the algorithm of :meth:`textwrap.TextWrapper._wrap_chunks` - with every width measurement routed through - :func:`click._compat.term_len` instead of :func:`len`, so ANSI escape - bytes in chunks, indents, or the placeholder do not inflate the count. - - .. seealso:: - :class:`textwrap.TextWrapper` in the Python standard library documentation: - https://docs.python.org/3/library/textwrap.html#textwrap.TextWrapper - - Reference implementation in CPython: - https://github.com/python/cpython/blob/main/Lib/textwrap.py - """ - lines: list[str] = [] - if self.width <= 0: - raise ValueError(f"invalid width {self.width!r} (must be > 0)") - if self.max_lines is not None: - if self.max_lines > 1: - indent = self.subsequent_indent - else: - indent = self.initial_indent - if term_len(indent) + term_len(self.placeholder.lstrip()) > self.width: - raise ValueError("placeholder too large for max width") - - chunks.reverse() - - while chunks: - cur_line: list[str] = [] - cur_len = 0 - - if lines: - indent = self.subsequent_indent - else: - indent = self.initial_indent - - width = self.width - term_len(indent) - - if self.drop_whitespace and chunks[-1].strip() == "" and lines: - del chunks[-1] - - while chunks: - n = term_len(chunks[-1]) - - if cur_len + n <= width: - cur_line.append(chunks.pop()) - cur_len += n - - else: - break - - if chunks and term_len(chunks[-1]) > width: - self._handle_long_word(chunks, cur_line, cur_len, width) - cur_len = sum(map(term_len, cur_line)) - - if self.drop_whitespace and cur_line and cur_line[-1].strip() == "": - cur_len -= term_len(cur_line[-1]) - del cur_line[-1] - - if cur_line: - if ( - self.max_lines is None - or len(lines) + 1 < self.max_lines - or ( - not chunks - or self.drop_whitespace - and len(chunks) == 1 - and not chunks[0].strip() - ) - and cur_len <= width - ): - lines.append(indent + "".join(cur_line)) - else: - while cur_line: - if ( - cur_line[-1].strip() - and cur_len + term_len(self.placeholder) <= width - ): - cur_line.append(self.placeholder) - lines.append(indent + "".join(cur_line)) - break - cur_len -= term_len(cur_line[-1]) - del cur_line[-1] - else: - if lines: - prev_line = lines[-1].rstrip() - if ( - term_len(prev_line) + term_len(self.placeholder) - <= self.width - ): - lines[-1] = prev_line + self.placeholder - break - lines.append(indent + self.placeholder.lstrip()) - break - - return lines - - @contextmanager - def extra_indent(self, indent: str) -> cabc.Iterator[None]: - old_initial_indent = self.initial_indent - old_subsequent_indent = self.subsequent_indent - self.initial_indent += indent - self.subsequent_indent += indent - - try: - yield - finally: - self.initial_indent = old_initial_indent - self.subsequent_indent = old_subsequent_indent - - def indent_only(self, text: str) -> str: - rv = [] - - for idx, line in enumerate(text.splitlines()): - indent = self.initial_indent - - if idx > 0: - indent = self.subsequent_indent - - rv.append(f"{indent}{line}") - - return "\n".join(rv) diff --git a/.venv/lib/python3.12/site-packages/click/_utils.py b/.venv/lib/python3.12/site-packages/click/_utils.py deleted file mode 100644 index 05ee2e99..00000000 --- a/.venv/lib/python3.12/site-packages/click/_utils.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations - -import enum -import typing as t - - -class Sentinel(enum.Enum): - """Enum used to define sentinel values. - - .. seealso:: - - `PEP 661 - Sentinel Values `_. - """ - - UNSET = object() - FLAG_NEEDS_VALUE = object() - - def __repr__(self) -> str: - return f"{self.__class__.__name__}.{self.name}" - - -UNSET: t.Literal[Sentinel.UNSET] = Sentinel.UNSET -"""Sentinel used to indicate that a value is not set.""" - -FLAG_NEEDS_VALUE: t.Literal[Sentinel.FLAG_NEEDS_VALUE] = Sentinel.FLAG_NEEDS_VALUE -"""Sentinel used to indicate 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``. -""" - -T_UNSET: t.TypeAlias = t.Literal[Sentinel.UNSET] -"""Type hint for the :data:`UNSET` sentinel value.""" - -T_FLAG_NEEDS_VALUE: t.TypeAlias = t.Literal[Sentinel.FLAG_NEEDS_VALUE] -"""Type hint for the :data:`FLAG_NEEDS_VALUE` sentinel value.""" diff --git a/.venv/lib/python3.12/site-packages/click/_winconsole.py b/.venv/lib/python3.12/site-packages/click/_winconsole.py deleted file mode 100644 index d25178d6..00000000 --- a/.venv/lib/python3.12/site-packages/click/_winconsole.py +++ /dev/null @@ -1,297 +0,0 @@ -# This module is based on the excellent work by Adam Bartoš who -# provided a lot of what went into the implementation here in -# the discussion to issue1602 in the Python bug tracker. -# -# There are some general differences in regards to how this works -# compared to the original patches as we do not need to patch -# the entire interpreter but just work in our little world of -# echo and prompt. -from __future__ import annotations - -import collections.abc as cabc -import io -import sys -import time -import typing as t -from ctypes import Array -from ctypes import byref -from ctypes import c_char -from ctypes import c_char_p -from ctypes import c_int -from ctypes import c_ssize_t -from ctypes import c_ulong -from ctypes import c_void_p -from ctypes import POINTER -from ctypes import py_object -from ctypes import Structure -from ctypes.wintypes import DWORD -from ctypes.wintypes import HANDLE -from ctypes.wintypes import LPCWSTR -from ctypes.wintypes import LPWSTR -from gettext import gettext as _ - -from ._compat import _NonClosingTextIOWrapper - -assert sys.platform == "win32" -import msvcrt # noqa: E402 -from ctypes import windll # noqa: E402 -from ctypes import WINFUNCTYPE # noqa: E402 - -c_ssize_p = POINTER(c_ssize_t) - -kernel32 = windll.kernel32 -GetStdHandle = kernel32.GetStdHandle -ReadConsoleW = kernel32.ReadConsoleW -WriteConsoleW = kernel32.WriteConsoleW -GetConsoleMode = kernel32.GetConsoleMode -GetLastError = kernel32.GetLastError -GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) -CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( - ("CommandLineToArgvW", windll.shell32) -) -LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) - -STDIN_HANDLE = GetStdHandle(-10) -STDOUT_HANDLE = GetStdHandle(-11) -STDERR_HANDLE = GetStdHandle(-12) - -PyBUF_SIMPLE = 0 -PyBUF_WRITABLE = 1 - -ERROR_SUCCESS = 0 -ERROR_NOT_ENOUGH_MEMORY = 8 -ERROR_OPERATION_ABORTED = 995 - -STDIN_FILENO = 0 -STDOUT_FILENO = 1 -STDERR_FILENO = 2 - -EOF = b"\x1a" -MAX_BYTES_WRITTEN = 32767 - -if t.TYPE_CHECKING: - try: - # Using `typing_extensions.Buffer` instead of `collections.abc` - # on Windows for some reason does not have `Sized` implemented. - from collections.abc import Buffer # type: ignore - except ImportError: - from typing_extensions import Buffer - -try: - from ctypes import pythonapi -except ImportError: - # On PyPy we cannot get buffers so our ability to operate here is - # severely limited. - get_buffer = None -else: - - class Py_buffer(Structure): - _fields_ = [ # noqa: RUF012 - ("buf", c_void_p), - ("obj", py_object), - ("len", c_ssize_t), - ("itemsize", c_ssize_t), - ("readonly", c_int), - ("ndim", c_int), - ("format", c_char_p), - ("shape", c_ssize_p), - ("strides", c_ssize_p), - ("suboffsets", c_ssize_p), - ("internal", c_void_p), - ] - - PyObject_GetBuffer = pythonapi.PyObject_GetBuffer - PyBuffer_Release = pythonapi.PyBuffer_Release - - def get_buffer(obj: Buffer, writable: bool = False) -> Array[c_char]: - buf = Py_buffer() - flags: int = PyBUF_WRITABLE if writable else PyBUF_SIMPLE - PyObject_GetBuffer(py_object(obj), byref(buf), flags) - - try: - buffer_type = c_char * buf.len - out: Array[c_char] = buffer_type.from_address(buf.buf) - return out - finally: - PyBuffer_Release(byref(buf)) - - -class _WindowsConsoleRawIOBase(io.RawIOBase): - def __init__(self, handle: int | None) -> None: - self.handle = handle - - def isatty(self) -> t.Literal[True]: - super().isatty() - return True - - -class _WindowsConsoleReader(_WindowsConsoleRawIOBase): - def readable(self) -> t.Literal[True]: - return True - - def readinto(self, b: Buffer) -> int: - bytes_to_be_read = len(b) - if not bytes_to_be_read: - return 0 - elif bytes_to_be_read % 2: - raise ValueError( - "cannot read odd number of bytes from UTF-16-LE encoded console" - ) - - buffer = get_buffer(b, writable=True) - code_units_to_be_read = bytes_to_be_read // 2 - code_units_read = c_ulong() - - rv = ReadConsoleW( - HANDLE(self.handle), - buffer, - code_units_to_be_read, - byref(code_units_read), - None, - ) - if GetLastError() == ERROR_OPERATION_ABORTED: - # wait for KeyboardInterrupt - time.sleep(0.1) - if not rv: - raise OSError(_("Windows error: {error}").format(error=GetLastError())) - - if buffer[0] == EOF: - return 0 - return 2 * code_units_read.value - - -class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): - def writable(self) -> t.Literal[True]: - return True - - @staticmethod - def _get_error_message(errno: int) -> str: - if errno == ERROR_SUCCESS: - return "ERROR_SUCCESS" - elif errno == ERROR_NOT_ENOUGH_MEMORY: - return "ERROR_NOT_ENOUGH_MEMORY" - return _("Windows error: {error}").format(error=errno) - - def write(self, b: Buffer) -> int: - bytes_to_be_written = len(b) - buf = get_buffer(b) - code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 - code_units_written = c_ulong() - - WriteConsoleW( - HANDLE(self.handle), - buf, - code_units_to_be_written, - byref(code_units_written), - None, - ) - bytes_written = 2 * code_units_written.value - - if bytes_written == 0 and bytes_to_be_written > 0: - raise OSError(self._get_error_message(GetLastError())) - return bytes_written - - -class ConsoleStream: - def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: - self._text_stream = text_stream - self.buffer = byte_stream - - @property - def name(self) -> str: - return self.buffer.name - - def write(self, x: t.AnyStr) -> int: - if isinstance(x, str): - return self._text_stream.write(x) - try: - self.flush() - except Exception: - pass - return self.buffer.write(x) - - def writelines(self, lines: cabc.Iterable[t.AnyStr]) -> None: - for line in lines: - self.write(line) - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._text_stream, name) - - def isatty(self) -> bool: - return self.buffer.isatty() - - def __repr__(self) -> str: - return f"" - - -def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -_stream_factories: cabc.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { - 0: _get_text_stdin, - 1: _get_text_stdout, - 2: _get_text_stderr, -} - - -def _is_console(f: t.TextIO) -> bool: - if not hasattr(f, "fileno"): - return False - - try: - fileno = f.fileno() - except (OSError, io.UnsupportedOperation): - return False - - handle = msvcrt.get_osfhandle(fileno) - return bool(GetConsoleMode(handle, byref(DWORD()))) - - -def _get_windows_console_stream( - f: t.TextIO, encoding: str | None, errors: str | None -) -> t.TextIO | None: - if ( - get_buffer is None - or encoding not in {"utf-16-le", None} - or errors not in {"strict", None} - or not _is_console(f) - ): - return None - - func = _stream_factories.get(f.fileno()) - if func is None: - return None - - b = getattr(f, "buffer", None) - - if b is None: - return None - - return func(b) diff --git a/.venv/lib/python3.12/site-packages/click/core.py b/.venv/lib/python3.12/site-packages/click/core.py deleted file mode 100644 index d7ecbefb..00000000 --- a/.venv/lib/python3.12/site-packages/click/core.py +++ /dev/null @@ -1,3639 +0,0 @@ -from __future__ import annotations - -import collections.abc as cabc -import enum -import errno -import inspect -import os -import sys -import typing as t -from abc import ABC -from abc import abstractmethod -from collections import abc -from collections import Counter -from contextlib import AbstractContextManager -from contextlib import contextmanager -from contextlib import ExitStack -from functools import update_wrapper -from gettext import gettext as _ -from gettext import ngettext -from itertools import repeat -from types import TracebackType - -from . import types -from ._utils import FLAG_NEEDS_VALUE -from ._utils import UNSET -from .exceptions import Abort -from .exceptions import BadParameter -from .exceptions import ClickException -from .exceptions import Exit -from .exceptions import MissingParameter -from .exceptions import NoArgsIsHelpError -from .exceptions import NoSuchCommand -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 _OptionParser -from .parser import _split_opt -from .termui import confirm -from .termui import prompt -from .termui import style -from .utils import _detect_program_name -from .utils import _expand_args -from .utils import echo -from .utils import make_default_short_help -from .utils import make_str -from .utils import PacifyFlushWrapper - -if t.TYPE_CHECKING: - from typing_extensions import Self - - from .shell_completion import CompletionItem - -F = t.TypeVar("F", bound="t.Callable[..., t.Any]") -V = t.TypeVar("V") - - -def _complete_visible_commands( - ctx: Context, incomplete: str -) -> cabc.Iterator[tuple[str, Command]]: - """List all the subcommands of a group that start with the - incomplete value and aren't hidden. - - :param ctx: Invocation context for the group. - :param incomplete: Value being completed. May be empty. - """ - multi = t.cast(Group, ctx.command) - - for name in multi.list_commands(ctx): - if name.startswith(incomplete): - command = multi.get_command(ctx, name) - - if command is not None and not command.hidden: - yield name, command - - -def _check_nested_chain( - base_command: Group, cmd_name: str, cmd: Command, register: bool = False -) -> None: - if not base_command.chain or not isinstance(cmd, Group): - return - - if register: - message = ( - f"It is not possible to add the group {cmd_name!r} to another" - f" group {base_command.name!r} that is in chain mode." - ) - else: - message = ( - f"Found the group {cmd_name!r} as subcommand to another group " - f" {base_command.name!r} that is in chain mode. This is not supported." - ) - - raise RuntimeError(message) - - -def _format_deprecated_label(deprecated: bool | str) -> str: - """Return the parenthesized deprecation label shown in help text.""" - label = _("deprecated").upper() - if isinstance(deprecated, str): - return f"({label}: {deprecated})" - return f"({label})" - - -def _format_deprecated_suffix(deprecated: bool | str) -> str: - """Return the trailing reason for a ``DeprecationWarning`` message, - prefixed with a space, or an empty string when no reason was given. - """ - if isinstance(deprecated, str): - return f" {deprecated}" - return "" - - -def batch(iterable: cabc.Iterable[V], batch_size: int) -> list[tuple[V, ...]]: - return list(zip(*repeat(iter(iterable), batch_size), strict=False)) - - -@contextmanager -def augment_usage_errors( - ctx: Context, param: Parameter | None = None -) -> cabc.Generator[None]: - """Context manager that attaches extra information to exceptions.""" - try: - yield - except BadParameter as e: - if e.ctx is None: - e.ctx = ctx - if param is not None and e.param is None: - e.param = param - raise - except UsageError as e: - if e.ctx is None: - e.ctx = ctx - raise - - -def iter_params_for_processing( - invocation_order: cabc.Sequence[Parameter], - declaration_order: cabc.Sequence[Parameter], -) -> list[Parameter]: - """Returns all declared parameters in the order they should be processed. - - The declared parameters are re-shuffled depending on the order in which - they were invoked, as well as the eagerness of each parameters. - - The invocation order takes precedence over the declaration order. I.e. the - order in which the user provided them to the CLI is respected. - - This behavior and its effect on callback evaluation is detailed at: - https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order - """ - - def sort_key(item: Parameter) -> tuple[bool, float]: - try: - idx: float = invocation_order.index(item) - except ValueError: - idx = float("inf") - - return not item.is_eager, idx - - return sorted(declaration_order, key=sort_key) - - -class ParameterSource(enum.IntEnum): - """This is an :class:`~enum.IntEnum` 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. - - Members are ordered from most explicit to least explicit source. - This allows comparison to check if a value was explicitly provided: - - .. code-block:: python - - source = ctx.get_parameter_source("port") - if source < click.ParameterSource.DEFAULT_MAP: - ... # value was explicitly set - - .. versionchanged:: 8.3.3 - Use :class:`~enum.IntEnum` and reorder members from most to - least explicit. Supports comparison operators. - - .. versionchanged:: 8.0 - Use :class:`~enum.Enum` and drop the ``validate`` method. - - .. versionchanged:: 8.0 - Added the ``PROMPT`` value. - """ - - PROMPT = enum.auto() - """Used a prompt to confirm a default or provide a 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_MAP = enum.auto() - """Used a default provided by :attr:`Context.default_map`.""" - DEFAULT = enum.auto() - """Used the default specified by the parameter.""" - - -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 that it's - the name of the script. - :param obj: an arbitrary object of user data. - :param auto_envvar_prefix: the prefix to use for automatic environment - variables. If this is `None` then reading - from environment variables is disabled. This - does not affect manually set environment - variables which are always read. - :param default_map: a dictionary (like object) with default values - for parameters. - :param terminal_width: the width of the terminal. The default is - inherit from parent context. If no context - defines the terminal width then auto - detection will be applied. - :param max_content_width: the maximum width for content rendered by - Click (this currently only affects help - pages). This defaults to 80 characters if - not overridden. In other words: even if the - terminal is larger than that, Click will not - format things wider than 80 characters by - default. In addition to that, formatters might - add some safety mapping on the right. - :param resilient_parsing: if this flag is enabled then Click will - parse without any interactivity or callback - invocation. Default values will also be - ignored. This is useful for implementing - things such as completion support. - :param allow_extra_args: if this is set to `True` then extra arguments - at the end will not raise an error and will be - kept on the context. The default is to inherit - from the command. - :param allow_interspersed_args: if this is set to `False` then options - and arguments cannot be mixed. The - default is to inherit from the command. - :param ignore_unknown_options: instructs click to ignore options it does - not know and keeps them for later - processing. - :param help_option_names: optionally a list of strings that define how - the default help parameter is named. The - default is ``['--help']``. - :param token_normalize_func: an optional function that is used to - normalize tokens (options, choices, - etc.). This for instance can be used to - implement case insensitive behavior. - :param color: controls if the terminal supports ANSI colors or not. The - default is autodetection. This is only needed if ANSI - codes are used in texts that Click prints which is by - default not the case. This for instance would affect - help output. - :param show_default: Show the default value for commands. If this - value is not set, it defaults to the value from the parent - context. ``Command.show_default`` overrides this default for the - specific command. - - .. versionchanged:: 8.2 - The ``protected_args`` attribute is deprecated and will be removed in - Click 9.0. ``args`` will contain remaining unparsed tokens. - - .. versionchanged:: 8.1 - The ``show_default`` parameter is overridden by - ``Command.show_default``, instead of the other way around. - - .. versionchanged:: 8.0 - The ``show_default`` parameter defaults to the value from the - parent context. - - .. versionchanged:: 7.1 - Added the ``show_default`` parameter. - - .. versionchanged:: 4.0 - Added the ``color``, ``ignore_unknown_options``, and - ``max_content_width`` parameters. - - .. versionchanged:: 3.0 - Added the ``allow_extra_args`` and ``allow_interspersed_args`` - parameters. - - .. versionchanged:: 2.0 - Added the ``resilient_parsing``, ``help_option_names``, and - ``token_normalize_func`` parameters. - """ - - #: The formatter class to create with :meth:`make_formatter`. - #: - #: .. versionadded:: 8.0 - formatter_class: type[HelpFormatter] = HelpFormatter - - parent: Context | None - command: Command - info_name: str | None - params: dict[str, t.Any] - args: list[str] - _protected_args: list[str] - _opt_prefixes: set[str] - obj: t.Any - _meta: dict[str, t.Any] - default_map: cabc.MutableMapping[str, t.Any] | None - invoked_subcommand: str | None - terminal_width: int | None - max_content_width: int | None - allow_extra_args: bool - allow_interspersed_args: bool - ignore_unknown_options: bool - help_option_names: list[str] - token_normalize_func: t.Callable[[str], str] | None - resilient_parsing: bool - auto_envvar_prefix: str | None - color: bool | None - show_default: bool | None - _close_callbacks: list[t.Callable[[], t.Any]] - _depth: int - _parameter_source: dict[str, ParameterSource] - _param_default_explicit: dict[str, bool] - _exit_stack: ExitStack - - def __init__( - self, - command: Command, - parent: Context | None = None, - info_name: str | None = None, - obj: t.Any | None = None, - auto_envvar_prefix: str | None = None, - default_map: cabc.MutableMapping[str, t.Any] | None = None, - terminal_width: int | None = None, - max_content_width: int | None = None, - resilient_parsing: bool = False, - allow_extra_args: bool | None = None, - allow_interspersed_args: bool | None = None, - ignore_unknown_options: bool | None = None, - help_option_names: list[str] | None = None, - token_normalize_func: t.Callable[[str], str] | None = None, - color: bool | None = None, - show_default: bool | None = None, - ) -> None: - #: the parent context or `None` if none exists. - self.parent = parent - #: the :class:`Command` for this context. - self.command = command - #: the descriptive information name - self.info_name = info_name - #: Map of parameter names to their parsed values. Parameters - #: with ``expose_value=False`` are not stored. - self.params = {} - #: the leftover arguments. - self.args = [] - #: 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 = [] - #: the collected prefixes of the command's options. - self._opt_prefixes = 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 = obj - self._meta = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = show_default - - self._close_callbacks = [] - self._depth = 0 - self._parameter_source = {} - # Tracks whether the option that currently owns each parameter slot in - # :attr:`params` had its ``default`` set explicitly by the user. Used - # to tie-break feature-switch groups where multiple options share a - # parameter name and both fall back to their default value. - # Refs: https://github.com/pallets/click/issues/3403 - self._param_default_explicit = {} - self._exit_stack = ExitStack() - - @property - def protected_args(self) -> list[str]: - import warnings - - warnings.warn( - "'protected_args' is deprecated and will be removed in Click 9.0." - " 'args' will contain remaining unparsed tokens.", - DeprecationWarning, - stacklevel=2, - ) - return self._protected_args - - def to_info_dict(self) -> dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. This traverses the entire CLI - structure. - - .. code-block:: python - - with Context(cli) as ctx: - info = ctx.to_info_dict() - - .. versionadded:: 8.0 - """ - return { - "command": self.command.to_info_dict(self), - "info_name": self.info_name, - "allow_extra_args": self.allow_extra_args, - "allow_interspersed_args": self.allow_interspersed_args, - "ignore_unknown_options": self.ignore_unknown_options, - "auto_envvar_prefix": self.auto_envvar_prefix, - } - - def __enter__(self) -> Self: - self._depth += 1 - push_context(self) - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - tb: TracebackType | None, - ) -> bool | None: - self._depth -= 1 - exit_result: bool | None = None - if self._depth == 0: - exit_result = self._close_with_exception_info(exc_type, exc_value, tb) - pop_context() - - return exit_result - - @contextmanager - def scope(self, cleanup: bool = True) -> cabc.Generator[Context]: - """This helper method can be used with the context object to promote - it to the current thread local (see :func:`get_current_context`). - The default behavior of this is to invoke the cleanup functions which - can be disabled by setting `cleanup` to `False`. The cleanup - functions are typically used for things such as closing file handles. - - If the cleanup is intended the context object can also be directly - used as a context manager. - - Example usage:: - - with ctx.scope(): - assert get_current_context() is ctx - - This is equivalent:: - - with ctx: - assert get_current_context() is ctx - - .. versionadded:: 5.0 - - :param cleanup: controls if the cleanup functions should be run or - not. The default is to run these functions. In - some situations the context only wants to be - temporarily pushed in which case this can be disabled. - Nested pushes automatically defer the cleanup. - """ - if not cleanup: - self._depth += 1 - try: - with self as rv: - yield rv - finally: - if not cleanup: - self._depth -= 1 - - @property - def meta(self) -> dict[str, t.Any]: - """This is a dictionary which is shared with all the contexts - that are nested. It exists so that click utilities can store some - state here if they need to. It is however the responsibility of - that code to manage this dictionary well. - - The keys are supposed to be unique dotted strings. For instance - module paths are a good choice for it. What is stored in there is - irrelevant for the operation of click. However what is important is - that code that places data here adheres to the general semantics of - the system. - - Example usage:: - - LANG_KEY = f'{__name__}.lang' - - def set_language(value): - ctx = get_current_context() - ctx.meta[LANG_KEY] = value - - def get_language(): - return get_current_context().meta.get(LANG_KEY, 'en_US') - - .. versionadded:: 5.0 - """ - return self._meta - - def make_formatter(self) -> HelpFormatter: - """Creates the :class:`~click.HelpFormatter` for the help and - usage output. - - To quickly customize the formatter class used without overriding - this method, set the :attr:`formatter_class` attribute. - - .. versionchanged:: 8.0 - Added the :attr:`formatter_class` attribute. - """ - return self.formatter_class( - width=self.terminal_width, max_width=self.max_content_width - ) - - def with_resource(self, context_manager: AbstractContextManager[V]) -> V: - """Register a resource as if it were used in a ``with`` - statement. The resource will be cleaned up when the context is - popped. - - Uses :meth:`contextlib.ExitStack.enter_context`. It calls the - resource's ``__enter__()`` method and returns the result. When - the context is popped, it closes the stack, which calls the - resource's ``__exit__()`` method. - - To register a cleanup function for something that isn't a - context manager, use :meth:`call_on_close`. Or use something - from :mod:`contextlib` to turn it into a context manager first. - - .. code-block:: python - - @click.group() - @click.option("--name") - @click.pass_context - def cli(ctx): - ctx.obj = ctx.with_resource(connect_db(name)) - - :param context_manager: The context manager to enter. - :return: Whatever ``context_manager.__enter__()`` returns. - - .. versionadded:: 8.0 - """ - return self._exit_stack.enter_context(context_manager) - - def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: - """Register a function to be called when the context tears down. - - This can be used to close resources opened during the script - execution. Resources that support Python's context manager - protocol which would be used in a ``with`` statement should be - registered with :meth:`with_resource` instead. - - :param f: The function to execute on teardown. - """ - return self._exit_stack.callback(f) - - def close(self) -> None: - """Invoke all close callbacks registered with - :meth:`call_on_close`, and exit all context managers entered - with :meth:`with_resource`. - """ - self._close_with_exception_info(None, None, None) - - def _close_with_exception_info( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - tb: TracebackType | None, - ) -> bool | None: - """Unwind the exit stack by calling its :meth:`__exit__` providing the exception - information to allow for exception handling by the various resources registered - using :meth;`with_resource` - - :return: Whatever ``exit_stack.__exit__()`` returns. - """ - exit_result = self._exit_stack.__exit__(exc_type, exc_value, tb) - # In case the context is reused, create a new exit stack. - self._exit_stack = ExitStack() - - return exit_result - - @property - def command_path(self) -> str: - """The computed command path. This is used for the ``usage`` - information on the help page. It's automatically created by - combining the info names of the chain of contexts to the root. - """ - rv = "" - if self.info_name is not None: - rv = self.info_name - if self.parent is not None: - parent_command_path = [self.parent.command_path] - - if isinstance(self.parent.command, Command): - for param in self.parent.command.get_params(self): - parent_command_path.extend(param.get_usage_pieces(self)) - - rv = f"{' '.join(parent_command_path)} {rv}" - return rv.lstrip() - - def find_root(self) -> Context: - """Finds the outermost context.""" - node = self - while node.parent is not None: - node = node.parent - return node - - def find_object(self, object_type: type[V]) -> V | None: - """Finds the closest object of a given type.""" - node: Context | None = self - - while node is not None: - if isinstance(node.obj, object_type): - return node.obj - - node = node.parent - - return None - - def ensure_object(self, object_type: type[V]) -> V: - """Like :meth:`find_object` but sets the innermost object to a - new instance of `object_type` if it does not exist. - """ - rv = self.find_object(object_type) - if rv is None: - self.obj = rv = object_type() - return rv - - def _default_map_has(self, name: str | None) -> bool: - """Check if :attr:`default_map` contains a real value for ``name``. - - Returns ``False`` when the key is absent, the map is ``None``, - ``name`` is ``None``, or the stored value is the internal - :data:`UNSET` sentinel. - """ - return ( - name is not None - and self.default_map is not None - and name in self.default_map - and self.default_map[name] is not UNSET - ) - - @t.overload - def lookup_default( - self, name: str, call: t.Literal[True] = True - ) -> t.Any | None: ... - - @t.overload - def lookup_default( - self, name: str, call: t.Literal[False] = ... - ) -> t.Any | t.Callable[[], t.Any] | None: ... - - def lookup_default(self, name: str, call: bool = True) -> t.Any | None: - """Get the default for a parameter from :attr:`default_map`. - - :param name: Name of the parameter. - :param call: If the default is a callable, call it. Disable to - return the callable instead. - - .. versionchanged:: 8.0 - Added the ``call`` parameter. - """ - if not self._default_map_has(name): - return None - - # Assert to make the type checker happy. - assert self.default_map is not None - value = self.default_map[name] - - if call and callable(value): - return value() - - return value - - def fail(self, message: str) -> t.NoReturn: - """Aborts the execution of the program with a specific error - message. - - :param message: the error message to fail with. - """ - raise UsageError(message, self) - - def abort(self) -> t.NoReturn: - """Aborts the script.""" - raise Abort() - - def exit(self, code: int = 0) -> t.NoReturn: - """Exits the application with a given exit code. - - .. versionchanged:: 8.2 - Callbacks and context managers registered with :meth:`call_on_close` - and :meth:`with_resource` are closed before exiting. - """ - self.close() - raise Exit(code) - - def get_usage(self) -> str: - """Helper method to get formatted usage string for the current - context and command. - """ - return self.command.get_usage(self) - - def get_help(self) -> str: - """Helper method to get formatted help page for the current - context and command. - """ - return self.command.get_help(self) - - def _make_sub_context(self, command: Command) -> Context: - """Create a new context of the same type as this context, but - for a new command. - - :meta private: - """ - return type(self)(command, info_name=command.name, parent=self) - - @t.overload - def invoke( - self, callback: t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any - ) -> V: ... - - @t.overload - def invoke(self, callback: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: ... - - def invoke( - self, callback: Command | t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any - ) -> t.Any | V: - """Invokes a command callback in exactly the way it expects. There - are two ways to invoke this method: - - 1. the first argument can be a callback and all other arguments and - keyword arguments are forwarded directly to the function. - 2. the first argument is a click command object. In that case all - arguments are forwarded as well but proper click parameters - (options and click arguments) must be keyword arguments and Click - will fill in defaults. - - .. versionchanged:: 8.0 - All ``kwargs`` are tracked in :attr:`params` so they will be - passed if :meth:`forward` is called at multiple levels. - - .. versionchanged:: 3.2 - A new context is created, and missing arguments use default values. - """ - if isinstance(callback, Command): - other_cmd = callback - - if other_cmd.callback is None: - raise TypeError( - "The given command does not have a callback that can be invoked." - ) - else: - callback = t.cast("t.Callable[..., V]", other_cmd.callback) - - ctx = self._make_sub_context(other_cmd) - - for param in other_cmd.params: - if param.name not in kwargs and param.expose_value: - default_value = param.get_default(ctx) - # We explicitly hide the :attr:`UNSET` value to the user, as we - # choose to make it an implementation detail. And because ``invoke`` - # has been designed as part of Click public API, we return ``None`` - # instead. Refs: - # https://github.com/pallets/click/issues/3066 - # https://github.com/pallets/click/issues/3065 - # https://github.com/pallets/click/pull/3068 - if default_value is UNSET: - default_value = None - kwargs[param.name] = param.type_cast_value(ctx, default_value) - - # Track all kwargs as params, so that forward() will pass - # them on in subsequent calls. - ctx.params.update(kwargs) - else: - ctx = self - - with augment_usage_errors(self): - with ctx: - return callback(*args, **kwargs) - - def forward(self, cmd: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: - """Similar to :meth:`invoke` but fills in default keyword - arguments from the current context if the other command expects - it. This cannot invoke callbacks directly, only other commands. - - .. versionchanged:: 8.0 - All ``kwargs`` are tracked in :attr:`params` so they will be - passed if ``forward`` is called at multiple levels. - """ - # Can only forward to other commands, not direct callbacks. - if not isinstance(cmd, Command): - raise TypeError("Callback is not a command.") - - for param in self.params: - if param not in kwargs: - kwargs[param] = self.params[param] - - return self.invoke(cmd, *args, **kwargs) - - def set_parameter_source(self, name: str, source: ParameterSource) -> None: - """Set the source of a parameter. This indicates the location - from which the value of the parameter was obtained. - - :param name: The name of the parameter. - :param source: A member of :class:`~click.core.ParameterSource`. - """ - self._parameter_source[name] = source - - def get_parameter_source(self, name: str) -> ParameterSource | None: - """Get the source of a parameter. This indicates the location - from which the value of the parameter was obtained. - - This can be useful for determining when a user specified a value - on the command line that is the same as the default value. It - will be :attr:`~click.core.ParameterSource.DEFAULT` only if the - value was actually taken from the default. - - :param name: The name of the parameter. - :rtype: ParameterSource - - .. versionchanged:: 8.0 - Returns ``None`` if the parameter was not provided from any - source. - """ - return self._parameter_source.get(name) - - -class Command: - """Commands are the basic building block of command line interfaces in - Click. A basic command handles command line parsing and might dispatch - more parsing to commands nested below it. - - :param name: the name of the command to use unless a group overrides it. - :param context_settings: an optional dictionary with defaults that are - passed to the context object. - :param callback: the callback to invoke. This is optional. - :param params: the parameters to register with this command. This can - be either :class:`Option` or :class:`Argument` objects. - :param help: the help string to use for this command. - :param epilog: like the help string but it's printed at the end of the - help page after everything else. - :param short_help: the short help to use for this command. This is - shown on the command listing of the parent command. - :param add_help_option: by default each command registers a ``--help`` - option. This can be disabled by this parameter. - :param no_args_is_help: this controls what happens if no arguments are - provided. This option is disabled by default. - If enabled this will add ``--help`` as argument - if no arguments are passed - :param hidden: hide this command from help outputs. - :param deprecated: If ``True`` or non-empty string, issues a message - indicating that the command is deprecated and highlights - its deprecation in --help. The message can be customized - by using a string as the value. - - .. versionchanged:: 8.2 - This is the base class for all commands, not ``BaseCommand``. - ``deprecated`` can be set to a string as well to customize the - deprecation message. - - .. versionchanged:: 8.1 - ``help``, ``epilog``, and ``short_help`` are stored unprocessed, - all formatting is done when outputting help text, not at init, - and is done even if not using the ``@command`` decorator. - - .. versionchanged:: 8.0 - Added a ``repr`` showing the command name. - - .. versionchanged:: 7.1 - Added the ``no_args_is_help`` parameter. - - .. versionchanged:: 2.0 - Added the ``context_settings`` parameter. - """ - - #: The context class to create with :meth:`make_context`. - #: - #: .. versionadded:: 8.0 - context_class: type[Context] = Context - - #: the default for the :attr:`Context.allow_extra_args` flag. - allow_extra_args = False - - #: the default for the :attr:`Context.allow_interspersed_args` flag. - allow_interspersed_args = True - - #: the default for the :attr:`Context.ignore_unknown_options` flag. - ignore_unknown_options = False - - name: str | None - context_settings: cabc.MutableMapping[str, t.Any] - callback: t.Callable[..., t.Any] | None - params: list[Parameter] - help: str | None - epilog: str | None - options_metavar: str | None - short_help: str | None - add_help_option: bool - _help_option: Option | None - no_args_is_help: bool - hidden: bool - deprecated: bool | str - - def __init__( - self, - name: str | None, - context_settings: cabc.MutableMapping[str, t.Any] | None = None, - callback: t.Callable[..., t.Any] | None = None, - params: list[Parameter] | None = None, - help: str | None = None, - epilog: str | None = None, - short_help: str | None = None, - options_metavar: str | None = "[OPTIONS]", - add_help_option: bool = True, - no_args_is_help: bool = False, - hidden: bool = False, - deprecated: bool | str = False, - ) -> None: - #: the name the command thinks it has. Upon registering a command - #: on a :class:`Group` the group will default the command name - #: with this information. You should instead use the - #: :class:`Context`\'s :attr:`~Context.info_name` attribute. - self.name = name - - if context_settings is None: - context_settings = {} - - #: an optional dictionary with defaults passed to the context. - self.context_settings = 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 = params or [] - self.help = help - self.epilog = epilog - self.options_metavar = options_metavar - self.short_help = short_help - self.add_help_option = add_help_option - self._help_option = None - self.no_args_is_help = no_args_is_help - self.hidden = hidden - self.deprecated = deprecated - - def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: - return { - "name": self.name, - "params": [param.to_info_dict() for param in self.get_params(ctx)], - "help": self.help, - "epilog": self.epilog, - "short_help": self.short_help, - "hidden": self.hidden, - "deprecated": self.deprecated, - } - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.name}>" - - def get_usage(self, ctx: Context) -> str: - """Formats the usage line into a string and returns it. - - Calls :meth:`format_usage` internally. - """ - formatter = ctx.make_formatter() - self.format_usage(ctx, formatter) - return formatter.getvalue().rstrip("\n") - - def get_params(self, ctx: Context) -> list[Parameter]: - params = self.params - help_option = self.get_help_option(ctx) - - if help_option is not None: - params = [*params, help_option] - - if __debug__: - import warnings - - opts = [opt for param in params for opt in param.opts] - opts_counter = Counter(opts) - duplicate_opts = (opt for opt, count in opts_counter.items() if count > 1) - - for duplicate_opt in duplicate_opts: - warnings.warn( - ( - f"The parameter {duplicate_opt} is used more than once. " - "Remove its duplicate as parameters should be unique." - ), - stacklevel=3, - ) - - return params - - def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the usage line into the formatter. - - This is a low-level method called by :meth:`get_usage`. - """ - pieces = self.collect_usage_pieces(ctx) - formatter.write_usage(ctx.command_path, " ".join(pieces)) - - def collect_usage_pieces(self, ctx: Context) -> list[str]: - """Returns all the pieces that go into the usage line and returns - it as a list of strings. - """ - rv = [self.options_metavar] if self.options_metavar else [] - - for param in self.get_params(ctx): - rv.extend(param.get_usage_pieces(ctx)) - - return rv - - def get_help_option_names(self, ctx: Context) -> list[str]: - """Returns the names for the help option.""" - all_names = set(ctx.help_option_names) - for param in self.params: - all_names.difference_update(param.opts) - all_names.difference_update(param.secondary_opts) - return list(all_names) - - def get_help_option(self, ctx: Context) -> Option | None: - """Returns the help option object. - - Skipped if :attr:`add_help_option` is ``False``. - - .. versionchanged:: 8.1.8 - The help option is now cached to avoid creating it multiple times. - """ - help_option_names = self.get_help_option_names(ctx) - - if not help_option_names or not self.add_help_option: - return None - - # Cache the help option object in private _help_option attribute to - # avoid creating it multiple times. Not doing this will break the - # callback ordering by iter_params_for_processing(), which relies on - # object comparison. - if self._help_option is None: - # Avoid circular import. - from .decorators import help_option - - # Apply help_option decorator and pop resulting option - help_option(*help_option_names)(self) - self._help_option = self.params.pop() # type: ignore[assignment] - - return self._help_option - - def make_parser(self, ctx: Context) -> _OptionParser: - """Creates the underlying option parser for this command.""" - parser = _OptionParser(ctx) - for param in self.get_params(ctx): - param.add_to_parser(parser, ctx) - return parser - - def get_help(self, ctx: Context) -> str: - """Formats the help into a string and returns it. - - Calls :meth:`format_help` internally. - """ - formatter = ctx.make_formatter() - self.format_help(ctx, formatter) - return formatter.getvalue().rstrip("\n") - - def get_short_help_str(self, limit: int = 45) -> str: - """Gets short help for the command or makes it by shortening the - long help string. - """ - if self.short_help: - text = inspect.cleandoc(self.short_help) - elif self.help: - text = make_default_short_help(self.help, limit) - else: - text = "" - - if self.deprecated: - text = f"{_(text)} {_format_deprecated_label(self.deprecated)}" - - return text.strip() - - def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the help into the formatter if it exists. - - This is a low-level method called by :meth:`get_help`. - - This calls the following methods: - - - :meth:`format_usage` - - :meth:`format_help_text` - - :meth:`format_options` - - :meth:`format_epilog` - """ - self.format_usage(ctx, formatter) - self.format_help_text(ctx, formatter) - self.format_options(ctx, formatter) - self.format_epilog(ctx, formatter) - - def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the help text to the formatter if it exists.""" - if self.help is not None: - # truncate the help text to the first form feed - text = inspect.cleandoc(self.help).partition("\f")[0] - else: - text = "" - - if self.deprecated: - label = _format_deprecated_label(self.deprecated) - text = f"{_(text)} {label}" if text else label - - if text: - formatter.write_paragraph() - - with formatter.indentation(): - formatter.write_text(text) - - def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes all the options into the formatter if they exist.""" - opts = [] - for param in self.get_params(ctx): - rv = param.get_help_record(ctx) - if rv is not None: - opts.append(rv) - - if opts: - with formatter.section(_("Options")): - formatter.write_dl(opts) - - def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the epilog into the formatter if it exists.""" - if self.epilog: - epilog = inspect.cleandoc(self.epilog) - formatter.write_paragraph() - - with formatter.indentation(): - formatter.write_text(epilog) - - def make_context( - self, - info_name: str | None, - args: list[str], - parent: Context | None = None, - **extra: t.Any, - ) -> Context: - """This function when given an info name and arguments will kick - off the parsing and create a new :class:`Context`. It does not - invoke the actual command callback though. - - To quickly customize the context class used without overriding - this method, set the :attr:`context_class` attribute. - - :param info_name: the info name for this invocation. Generally this - is the most descriptive name for the script or - command. For the toplevel script it's usually - the name of the script, for commands below it's - the name of the command. - :param args: the arguments to parse as list of strings. - :param parent: the parent context if available. - :param extra: extra keyword arguments forwarded to the context - constructor. - - .. versionchanged:: 8.0 - Added the :attr:`context_class` attribute. - """ - for key, value in self.context_settings.items(): - if key not in extra: - extra[key] = value - - ctx = self.context_class(self, info_name=info_name, parent=parent, **extra) - - with ctx.scope(cleanup=False): - self.parse_args(ctx, args) - return ctx - - def parse_args(self, ctx: Context, args: list[str]) -> list[str]: - if not args and self.no_args_is_help and not ctx.resilient_parsing: - raise NoArgsIsHelpError(ctx) - - parser = self.make_parser(ctx) - opts, args, param_order = parser.parse_args(args=args) - - for param in iter_params_for_processing(param_order, self.get_params(ctx)): - _, args = param.handle_parse_result(ctx, opts, args) - - # We now have all parameters' values into `ctx.params`, but the data may contain - # the `UNSET` sentinel. - # Convert `UNSET` to `None` to ensure that the user doesn't see `UNSET`. - # - # Waiting until after the initial parse to convert allows us to treat `UNSET` - # more like a missing value when multiple params use the same name. - # Refs: - # https://github.com/pallets/click/issues/3071 - # https://github.com/pallets/click/pull/3079 - for name, value in ctx.params.items(): - if value is UNSET: - ctx.params[name] = None - - 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.{extra_message}" - ).format( - name=self.name, - extra_message=_format_deprecated_suffix(self.deprecated), - ) - echo(style(message, fg="red"), err=True) - - if self.callback is not None: - return ctx.invoke(self.callback, **ctx.params) - - def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: - """Return a list of completions for the incomplete value. Looks - at the names of options and chained multi-commands. - - Any command could be part of a chained multi-command, so sibling - commands are valid at any point during command completion. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results: list[CompletionItem] = [] - - if incomplete and not incomplete[0].isalnum(): - for param in self.get_params(ctx): - if ( - not isinstance(param, Option) - or param.hidden - or ( - not param.multiple - and ctx.get_parameter_source(param.name) - is ParameterSource.COMMANDLINE - ) - ): - continue - - results.extend( - CompletionItem(name, help=param.help) - for name in [*param.opts, *param.secondary_opts] - if name.startswith(incomplete) - ) - - while ctx.parent is not None: - ctx = ctx.parent - - if isinstance(ctx.command, Group) and ctx.command.chain: - results.extend( - CompletionItem(name, help=command.get_short_help_str()) - for name, command in _complete_visible_commands(ctx, incomplete) - if name not in ctx._protected_args - ) - - return results - - @t.overload - def main( - self, - args: cabc.Sequence[str] | None = None, - prog_name: str | None = None, - complete_var: str | None = None, - standalone_mode: t.Literal[True] = True, - **extra: t.Any, - ) -> t.NoReturn: ... - - @t.overload - def main( - self, - args: cabc.Sequence[str] | None = None, - prog_name: str | None = None, - complete_var: str | None = None, - standalone_mode: bool = ..., - **extra: t.Any, - ) -> t.Any: ... - - def main( - self, - args: cabc.Sequence[str] | None = None, - prog_name: str | None = None, - complete_var: str | None = None, - standalone_mode: bool = True, - windows_expand_args: bool = True, - **extra: t.Any, - ) -> t.Any: - """This is the way to invoke a script with all the bells and - whistles as a command line application. This will always terminate - the application after a call. If this is not wanted, ``SystemExit`` - needs to be caught. - - This method is also available by directly calling the instance of - a :class:`Command`. - - :param args: the arguments that should be used for parsing. If not - provided, ``sys.argv[1:]`` is used. - :param prog_name: the program name that should be used. By default - the program name is constructed by taking the file - name from ``sys.argv[0]``. - :param complete_var: the environment variable that controls the - bash completion support. The default is - ``"__COMPLETE"`` with prog_name in - uppercase. - :param standalone_mode: the default behavior is to invoke the script - in standalone mode. Click will then - handle exceptions and convert them into - error messages and the function will never - return but shut down the interpreter. If - this is set to `False` they will be - propagated to the caller and the return - value of this function is the return value - of :meth:`invoke`. - :param windows_expand_args: Expand glob patterns, user dir, and - env vars in command line args on Windows. - :param extra: extra keyword arguments are forwarded to the context - constructor. See :class:`Context` for more information. - - .. versionchanged:: 8.0.1 - Added the ``windows_expand_args`` parameter to allow - disabling command line arg expansion on Windows. - - .. versionchanged:: 8.0 - When taking arguments from ``sys.argv`` on Windows, glob - patterns, user dir, and env vars are expanded. - - .. versionchanged:: 3.0 - Added the ``standalone_mode`` parameter. - """ - if args is None: - args = sys.argv[1:] - - if os.name == "nt" and windows_expand_args: - args = _expand_args(args) - else: - args = list(args) - - if prog_name is None: - prog_name = _detect_program_name() - - # Process shell completion requests and exit early. - self._main_shell_completion(extra, prog_name, complete_var) - - try: - try: - with self.make_context(prog_name, args, **extra) as ctx: - rv = self.invoke(ctx) - if not standalone_mode: - return rv - # it's not safe to `ctx.exit(rv)` here! - # note that `rv` may actually contain data like "1" which - # has obvious effects - # more subtle case: `rv=[None, None]` can come out of - # chained commands which all returned `None` -- so it's not - # even always obvious that `rv` indicates success/failure - # by its truthiness/falsiness - ctx.exit() - except (EOFError, KeyboardInterrupt) as e: - echo(file=sys.stderr) - raise Abort() from e - except ClickException as e: - if not standalone_mode: - raise - e.show() - sys.exit(e.exit_code) - except OSError as e: - if e.errno == errno.EPIPE: - sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) - sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) - sys.exit(1) - else: - raise - except Exit as e: - if standalone_mode: - sys.exit(e.exit_code) - else: - # in non-standalone mode, return the exit code - # note that this is only reached if `self.invoke` above raises - # an Exit explicitly -- thus bypassing the check there which - # would return its result - # the results of non-standalone execution may therefore be - # somewhat ambiguous: if there are codepaths which lead to - # `ctx.exit(1)` and to `return 1`, the caller won't be able to - # tell the difference between the two - return e.exit_code - except Abort: - if not standalone_mode: - raise - echo(_("Aborted!"), file=sys.stderr) - sys.exit(1) - - def _main_shell_completion( - self, - ctx_args: cabc.MutableMapping[str, t.Any], - prog_name: str, - complete_var: str | None = None, - ) -> None: - """Check if the shell is asking for tab completion, process - that, then exit early. Called from :meth:`main` before the - program is invoked. - - :param prog_name: Name of the executable in the shell. - :param complete_var: Name of the environment variable that holds - the completion instruction. Defaults to - ``_{PROG_NAME}_COMPLETE``. - - .. versionchanged:: 8.2.0 - Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). - """ - if complete_var is None: - complete_name = prog_name.replace("-", "_").replace(".", "_") - complete_var = f"_{complete_name}_COMPLETE".upper() - - instruction = os.environ.get(complete_var) - - if not instruction: - return - - from .shell_completion import shell_complete - - rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) - sys.exit(rv) - - def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: - """Alias for :meth:`main`.""" - return self.main(*args, **kwargs) - - -class _FakeSubclassCheck(type): - def __subclasscheck__(cls, subclass: type) -> bool: - return issubclass(subclass, cls.__bases__[0]) - - def __instancecheck__(cls, instance: t.Any) -> bool: - return isinstance(instance, cls.__bases__[0]) - - -class _BaseCommand(Command, metaclass=_FakeSubclassCheck): - """ - .. deprecated:: 8.2 - Will be removed in Click 9.0. Use ``Command`` instead. - """ - - -class Group(Command): - """A group is a command that nests other commands (or more groups). - - :param name: The name of the group command. - :param commands: Map names to :class:`Command` objects. Can be a list, which - will use :attr:`Command.name` as the keys. - :param invoke_without_command: Invoke the group's callback even if a - subcommand is not given. - :param no_args_is_help: If no arguments are given, show the group's help and - exit. Defaults to the opposite of ``invoke_without_command``. - :param subcommand_metavar: How to represent the subcommand argument in help. - The default will represent whether ``chain`` is set or not. - :param chain: Allow passing more than one subcommand argument. After parsing - a command's arguments, if any arguments remain another command will be - matched, and so on. - :param result_callback: A function to call after the group's and - subcommand's callbacks. The value returned by the subcommand is passed. - If ``chain`` is enabled, the value will be a list of values returned by - all the commands. If ``invoke_without_command`` is enabled, the value - will be the value returned by the group's callback, or an empty list if - ``chain`` is enabled. - :param kwargs: Other arguments passed to :class:`Command`. - - .. versionchanged:: 8.0 - The ``commands`` argument can be a list of command objects. - - .. versionchanged:: 8.2 - Merged with and replaces the ``MultiCommand`` base class. - """ - - allow_extra_args = True - allow_interspersed_args = False - - #: If set, this is used by the group's :meth:`command` decorator - #: as the default :class:`Command` class. This is useful to make all - #: subcommands use a custom command class. - #: - #: .. versionadded:: 8.0 - command_class: type[Command] | None = None - - #: If set, this is used by the group's :meth:`group` decorator - #: as the default :class:`Group` class. This is useful to make all - #: subgroups use a custom group class. - #: - #: If set to the special value :class:`type` (literally - #: ``group_class = type``), this group's class will be used as the - #: default class. This makes a custom group class continue to make - #: custom groups. - #: - #: .. versionadded:: 8.0 - group_class: type[Group] | type[type] | None = None - # Literal[type] isn't valid, so use Type[type] - - commands: cabc.MutableMapping[str, Command] - invoke_without_command: bool - subcommand_metavar: str - chain: bool - _result_callback: t.Callable[..., t.Any] | None - - def __init__( - self, - name: str | None = None, - commands: cabc.MutableMapping[str, Command] - | cabc.Sequence[Command] - | None = None, - invoke_without_command: bool = False, - no_args_is_help: bool | None = None, - subcommand_metavar: str | None = None, - chain: bool = False, - result_callback: t.Callable[..., t.Any] | None = None, - **kwargs: t.Any, - ) -> None: - super().__init__(name, **kwargs) - - if commands is None: - commands = {} - elif isinstance(commands, abc.Sequence): - commands = {c.name: c for c in commands if c.name is not None} - - #: The registered subcommands by their exported names. - self.commands = commands - - if no_args_is_help is None: - no_args_is_help = not invoke_without_command - - self.no_args_is_help = no_args_is_help - self.invoke_without_command = invoke_without_command - - if subcommand_metavar is None: - # When the group can run without a subcommand, the leading command - # token is optional, so wrap it in brackets to reflect that. - if chain: - if invoke_without_command: - subcommand_metavar = "[COMMAND1] [ARGS]... [COMMAND2 [ARGS]...]..." - else: - subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." - elif invoke_without_command: - subcommand_metavar = "[COMMAND] [ARGS]..." - else: - subcommand_metavar = "COMMAND [ARGS]..." - - self.subcommand_metavar = subcommand_metavar - self.chain = chain - # The result callback that is stored. This can be set or - # overridden with the :func:`result_callback` decorator. - self._result_callback = result_callback - - if self.chain: - for param in self.params: - if isinstance(param, Argument) and not param.required: - raise RuntimeError( - "A group in chain mode cannot have optional arguments." - ) - - def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: - info_dict = super().to_info_dict(ctx) - commands = {} - - for name in self.list_commands(ctx): - command = self.get_command(ctx, name) - - if command is None: - continue - - sub_ctx = ctx._make_sub_context(command) - - with sub_ctx.scope(cleanup=False): - commands[name] = command.to_info_dict(sub_ctx) - - info_dict.update(commands=commands, chain=self.chain) - return info_dict - - def add_command(self, cmd: Command, name: str | None = None) -> None: - """Registers another :class:`Command` with this group. If the name - is not provided, the name of the command is used. - """ - name = name or cmd.name - if name is None: - raise TypeError("Command has no name.") - _check_nested_chain(self, name, cmd, register=True) - self.commands[name] = cmd - - @t.overload - def command(self, __func: t.Callable[..., t.Any]) -> Command: ... - - @t.overload - def command( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... - - def command( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], Command] | Command: - """A shortcut decorator for declaring and attaching a command to - the group. This takes the same arguments as :func:`command` and - immediately registers the created command with this group by - calling :meth:`add_command`. - - To customize the command class used, set the - :attr:`command_class` attribute. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.0 - Added the :attr:`command_class` attribute. - """ - from .decorators import command - - func: t.Callable[..., t.Any] | None = None - - if args and callable(args[0]): - assert len(args) == 1 and not kwargs, ( - "Use 'command(**kwargs)(callable)' to provide arguments." - ) - (func,) = args - args = () - - if self.command_class and kwargs.get("cls") is None: - kwargs["cls"] = self.command_class - - def decorator(f: t.Callable[..., t.Any]) -> Command: - cmd: Command = command(*args, **kwargs)(f) - self.add_command(cmd) - return cmd - - if func is not None: - return decorator(func) - - return decorator - - @t.overload - def group(self, __func: t.Callable[..., t.Any]) -> Group: ... - - @t.overload - def group( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], Group]: ... - - def group( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], Group] | Group: - """A shortcut decorator for declaring and attaching a group to - the group. This takes the same arguments as :func:`group` and - immediately registers the created group with this group by - calling :meth:`add_command`. - - To customize the group class used, set the :attr:`group_class` - attribute. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.0 - Added the :attr:`group_class` attribute. - """ - from .decorators import group - - func: t.Callable[..., t.Any] | None = None - - if args and callable(args[0]): - assert len(args) == 1 and not kwargs, ( - "Use 'group(**kwargs)(callable)' to provide arguments." - ) - (func,) = args - args = () - - if self.group_class is not None and kwargs.get("cls") is None: - if self.group_class is type: - kwargs["cls"] = type(self) - else: - kwargs["cls"] = self.group_class - - def decorator(f: t.Callable[..., t.Any]) -> Group: - cmd: Group = group(*args, **kwargs)(f) - self.add_command(cmd) - return cmd - - if func is not None: - return decorator(func) - - return decorator - - def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: - """Adds a result callback to the command. By default if a - result callback is already registered this will chain them but - this can be disabled with the `replace` parameter. The result - callback is invoked with the return value of the subcommand - (or the list of return values from all subcommands if chaining - is enabled) as well as the parameters as they would be passed - to the main callback. - - Example:: - - @click.group() - @click.option('-i', '--input', default=23) - def cli(input): - return 42 - - @cli.result_callback() - def process_result(result, input): - return result + input - - :param replace: if set to `True` an already existing result - callback will be removed. - - .. versionchanged:: 8.0 - Renamed from ``resultcallback``. - - .. versionadded:: 3.0 - """ - - def decorator(f: F) -> F: - old_callback = self._result_callback - - if old_callback is None or replace: - self._result_callback = f - return f - - def function(value: t.Any, /, *args: t.Any, **kwargs: t.Any) -> t.Any: - inner = old_callback(value, *args, **kwargs) - return f(inner, *args, **kwargs) - - self._result_callback = rv = update_wrapper(t.cast(F, function), f) - return rv # type: ignore[return-value] - - return decorator - - def get_command(self, ctx: Context, cmd_name: str) -> Command | None: - """Given a context and a command name, this returns a :class:`Command` - object if it exists or returns ``None``. - """ - return self.commands.get(cmd_name) - - def list_commands(self, ctx: Context) -> list[str]: - """Returns a list of subcommand names in the order they should appear.""" - return sorted(self.commands) - - def collect_usage_pieces(self, ctx: Context) -> list[str]: - rv = super().collect_usage_pieces(ctx) - rv.append(self.subcommand_metavar) - return rv - - def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: - super().format_options(ctx, formatter) - self.format_commands(ctx, formatter) - - def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: - """Extra format methods for multi methods that adds all the commands - after the options. - """ - commands = [] - for subcommand in self.list_commands(ctx): - cmd = self.get_command(ctx, subcommand) - # What is this, the tool lied about a command. Ignore it - if cmd is None: - continue - if cmd.hidden: - continue - - commands.append((subcommand, cmd)) - - # allow for 3 times the default spacing - if len(commands): - limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) - - rows = [] - for subcommand, cmd in commands: - help = cmd.get_short_help_str(limit) - rows.append((subcommand, help)) - - if rows: - with formatter.section(_("Commands")): - formatter.write_dl(rows) - - def parse_args(self, ctx: Context, args: list[str]) -> list[str]: - if not args and self.no_args_is_help and not ctx.resilient_parsing: - raise NoArgsIsHelpError(ctx) - - rest = super().parse_args(ctx, args) - - if self.chain: - ctx._protected_args = rest - ctx.args = [] - elif rest: - ctx._protected_args, ctx.args = rest[:1], rest[1:] - - return ctx.args - - def invoke(self, ctx: Context) -> t.Any: - def _process_result(value: t.Any) -> t.Any: - if self._result_callback is not None: - value = ctx.invoke(self._result_callback, value, **ctx.params) - return value - - if not ctx._protected_args: - if self.invoke_without_command: - # No subcommand was invoked, so the result callback is - # invoked with the group return value for regular - # groups, or an empty list for chained groups. - with ctx: - rv = super().invoke(ctx) - return _process_result([] if self.chain else rv) - ctx.fail(_("Missing command.")) - - # Fetch args back out - args = [*ctx._protected_args, *ctx.args] - ctx.args = [] - ctx._protected_args = [] - - # If we're not in chain mode, we only allow the invocation of a - # single command but we also inform the current context about the - # name of the command to invoke. - if not self.chain: - # Make sure the context is entered so we do not clean up - # resources until the result processor has worked. - with ctx: - cmd_name, cmd, args = self.resolve_command(ctx, args) - assert cmd is not None - ctx.invoked_subcommand = cmd_name - super().invoke(ctx) - sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) - with sub_ctx: - return _process_result(sub_ctx.command.invoke(sub_ctx)) - - # In chain mode we create the contexts step by step, but after the - # base command has been invoked. Because at that point we do not - # know the subcommands yet, the invoked subcommand attribute is - # set to ``*`` to inform the command that subcommands are executed - # but nothing else. - with ctx: - ctx.invoked_subcommand = "*" if args else None - super().invoke(ctx) - - # Otherwise we make every single context and invoke them in a - # chain. In that case the return value to the result processor - # is the list of all invoked subcommand's results. - contexts = [] - while args: - cmd_name, cmd, args = self.resolve_command(ctx, args) - assert cmd is not None - sub_ctx = cmd.make_context( - cmd_name, - args, - parent=ctx, - allow_extra_args=True, - allow_interspersed_args=False, - ) - contexts.append(sub_ctx) - args, sub_ctx.args = sub_ctx.args, [] - - rv = [] - for sub_ctx in contexts: - with sub_ctx: - rv.append(sub_ctx.command.invoke(sub_ctx)) - return _process_result(rv) - - def resolve_command( - self, ctx: Context, args: list[str] - ) -> tuple[str | None, Command | None, list[str]]: - cmd_name = make_str(args[0]) - - # Get the command - cmd = self.get_command(ctx, cmd_name) - - # If we can't find the command but there is a normalization - # function available, we try with that one. - if cmd is None and ctx.token_normalize_func is not None: - cmd_name = ctx.token_normalize_func(cmd_name) - cmd = self.get_command(ctx, cmd_name) - - # If we don't find the command we want to show an error message - # to the user that it was not provided. However, there is - # something else we should do: if the first argument looks like - # an option we want to kick off parsing again for arguments to - # resolve things like --help which now should go to the main - # place. - if cmd is None and not ctx.resilient_parsing: - if _split_opt(cmd_name)[0]: - self.parse_args(ctx, args) - raise NoSuchCommand(cmd_name, possibilities=self.commands, ctx=ctx) - return cmd_name if cmd else None, cmd, args[1:] - - def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: - """Return a list of completions for the incomplete value. Looks - at the names of options, subcommands, and chained - multi-commands. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results = [ - CompletionItem(name, help=command.get_short_help_str()) - for name, command in _complete_visible_commands(ctx, incomplete) - ] - results.extend(super().shell_complete(ctx, incomplete)) - return results - - -class _MultiCommand(Group, metaclass=_FakeSubclassCheck): - """ - .. deprecated:: 8.2 - Will be removed in Click 9.0. Use ``Group`` instead. - """ - - -class CommandCollection(Group): - """A :class:`Group` that looks up subcommands on other groups. If a command - is not found on this group, each registered source is checked in order. - Parameters on a source are not added to this group, and a source's callback - is not invoked when invoking its commands. In other words, this "flattens" - commands in many groups into this one group. - - :param name: The name of the group command. - :param sources: A list of :class:`Group` objects to look up commands from. - :param kwargs: Other arguments passed to :class:`Group`. - - .. versionchanged:: 8.2 - This is a subclass of ``Group``. Commands are looked up first on this - group, then each of its sources. - """ - - sources: list[Group] - - def __init__( - self, - name: str | None = None, - sources: list[Group] | None = None, - **kwargs: t.Any, - ) -> None: - super().__init__(name, **kwargs) - #: The list of registered groups. - self.sources = sources or [] - - def add_source(self, group: Group) -> None: - """Add a group as a source of commands.""" - self.sources.append(group) - - def get_command(self, ctx: Context, cmd_name: str) -> Command | None: - rv = super().get_command(ctx, cmd_name) - - if rv is not None: - return rv - - for source in self.sources: - rv = source.get_command(ctx, cmd_name) - - if rv is not None: - if self.chain: - _check_nested_chain(self, cmd_name, rv) - - return rv - - return None - - def list_commands(self, ctx: Context) -> list[str]: - rv: set[str] = set(super().list_commands(ctx)) - - for source in self.sources: - rv.update(source.list_commands(ctx)) - - return sorted(rv) - - -def _check_iter(value: cabc.Iterable[V]) -> cabc.Iterator[V]: - """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(ABC): - r"""A parameter to a command comes in two versions: they are either - :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently - not supported by design as some of the internals for parsing are - intentionally not finalized. - - Some settings are supported by both options and arguments. - - :param param_decls: the parameter declarations for this option or - argument. This is a list of flags or argument - names. - :param type: the type that should be used. Either a :class:`ParamType` - or a Python type. The latter is converted into the former - automatically if supported. - :param required: controls if this is optional or not. - :param default: the default value if omitted. This can also be a callable, - in which case it's invoked when the default is needed - without any arguments. - :param callback: A function to further process or validate the value - after type conversion. It is called as ``f(ctx, param, value)`` - and must return the value. It is called for all sources, - including prompts. - :param nargs: the number of arguments to match. If not ``1`` the return - value is a tuple instead of single value. The default for - nargs is ``1`` (except if the type is a tuple, then it's - the arity of the tuple). If ``nargs=-1``, all remaining - parameters are collected. - :param metavar: how the value is represented in the help page. - :param expose_value: if this is `True` then the value is passed onwards - to the command callback and stored on the context, - otherwise it's skipped. - :param is_eager: eager values are processed before non eager ones. This - should not be set for arguments or it will inverse the - order of processing. - :param envvar: environment variable(s) that are used to provide a default value for - this parameter. This can be a string or a sequence of strings. If a sequence is - given, only the first non-empty environment variable is used for the parameter. - :param shell_complete: A function that returns custom shell - completions. Used instead of the param's type completion if - given. Takes ``ctx, param, incomplete`` and must return a list - of :class:`~click.shell_completion.CompletionItem` or a list of - strings. - :param deprecated: If ``True`` or non-empty string, issues a message - indicating that the argument is deprecated and highlights - its deprecation in --help. The message can be customized - by using a string as the value. A deprecated parameter - cannot be required, a ValueError will be raised otherwise. - - .. versionchanged:: 8.2.0 - Introduction of ``deprecated``. - - .. versionchanged:: 8.2 - Adding duplicate parameter names to a :class:`~click.core.Command` will - result in a ``UserWarning`` being shown. - - .. versionchanged:: 8.2 - Adding duplicate parameter names to a :class:`~click.core.Command` will - result in a ``UserWarning`` being shown. - - .. versionchanged:: 8.0 - ``process_value`` validates required parameters and bounded - ``nargs``, and invokes the parameter callback before returning - the value. This allows the callback to validate prompts. - ``full_process_value`` is removed. - - .. versionchanged:: 8.0 - ``autocompletion`` is renamed to ``shell_complete`` and has new - semantics described above. The old name is deprecated and will - be removed in 8.1, until then it will be wrapped to match the - new requirements. - - .. versionchanged:: 8.0 - For ``multiple=True, nargs>1``, the default must be a list of - tuples. - - .. versionchanged:: 8.0 - Setting a default is no longer required for ``nargs>1``, it will - default to ``None``. ``multiple=True`` or ``nargs=-1`` will - default to ``()``. - - .. versionchanged:: 7.1 - Empty environment variables are ignored rather than taking the - empty string value. This makes it possible for scripts to clear - variables if they can't unset them. - - .. versionchanged:: 2.0 - Changed signature for parameter callback to also be passed the - parameter. The old callback format will still work, but it will - raise a warning to give you a chance to migrate the code easier. - """ - - param_type_name = "parameter" - - name: str - opts: list[str] - secondary_opts: list[str] - # `Parameter.type` is annotated in `__init__` to avoid confusing mypy - required: bool - callback: t.Callable[[Context, Parameter, t.Any], t.Any] | None - nargs: int - multiple: bool - expose_value: bool - default: t.Any | t.Callable[[], t.Any] | None - _default_explicit: bool - is_eager: bool - metavar: str | None - envvar: str | cabc.Sequence[str] | None - _custom_shell_complete: ( - t.Callable[[Context, Parameter, str], list[CompletionItem] | list[str]] | None - ) - deprecated: bool | str - - def __init__( - self, - param_decls: cabc.Sequence[str] | None = None, - type: types.ParamType[t.Any] | t.Any | None = None, - required: bool = False, - # XXX The default historically embed two concepts: - # - the declaration of a Parameter object carrying the default (handy to - # arbitrage the default value of coupled Parameters sharing the same - # self.name, like flag options), - # - and the actual value of the default. - # It is confusing and is the source of many issues discussed in: - # https://github.com/pallets/click/pull/3030 - # In the future, we might think of splitting it in two, not unlike - # Option.is_flag and Option.flag_value: we could have something like - # Parameter.is_default and Parameter.default_value. - default: t.Any | t.Callable[[], t.Any] | None = UNSET, - callback: t.Callable[[Context, Parameter, t.Any], t.Any] | None = None, - nargs: int | None = None, - multiple: bool = False, - metavar: str | None = None, - expose_value: bool = True, - is_eager: bool = False, - envvar: str | cabc.Sequence[str] | None = None, - shell_complete: t.Callable[ - [Context, Parameter, str], list[CompletionItem] | list[str] - ] - | None = None, - deprecated: bool | str = False, - ) -> None: - self.name, self.opts, self.secondary_opts = self._parse_decls( - param_decls or (), expose_value - ) - self.type: types.ParamType[t.Any] = 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 - # Whether the user passed ``default`` explicitly to the constructor. - # Captured before any auto-derived default (like ``False`` for boolean - # flags in :class:`Option`) replaces the :data:`UNSET` sentinel, so it - # remains ``False`` when the default was inferred rather than chosen. - # Refs: https://github.com/pallets/click/issues/3403 - self._default_explicit = default is not UNSET - self.is_eager = is_eager - self.metavar = metavar - self.envvar = envvar - self._custom_shell_complete = shell_complete - self.deprecated = deprecated - - if __debug__: - if self.type.is_composite and nargs != self.type.arity: - raise ValueError( - f"'nargs' must be {self.type.arity} (or None) for" - f" type {self.type!r}, but it was {nargs}." - ) - - if required and deprecated: - raise ValueError( - f"The {self.param_type_name} '{self.human_readable_name}' " - "is deprecated and still required. A deprecated " - f"{self.param_type_name} cannot be required." - ) - - def to_info_dict(self) -> dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - .. versionchanged:: 8.3.0 - Returns ``None`` for the :attr:`default` if it was not set. - - .. 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, - # We explicitly hide the :attr:`UNSET` value to the user, as we choose to - # make it an implementation detail. And because ``to_info_dict`` has been - # designed for documentation purposes, we return ``None`` instead. - "default": self.default if self.default is not UNSET else None, - "envvar": self.envvar, - } - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.name}>" - - @abstractmethod - def _parse_decls( - self, decls: cabc.Sequence[str], expose_value: bool - ) -> tuple[str, list[str], list[str]]: ... - - @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 - - def make_metavar(self, ctx: Context) -> str: - if self.metavar is not None: - return self.metavar - - metavar = self.type.get_metavar(param=self, ctx=ctx) - - if metavar is None: - metavar = self.type.name.upper() - - if self.nargs != 1: - metavar += "..." - - return metavar - - @t.overload - def get_default( - self, ctx: Context, call: t.Literal[True] = True - ) -> t.Any | None: ... - - @t.overload - def get_default( - self, ctx: Context, call: bool = ... - ) -> t.Any | t.Callable[[], t.Any] | None: ... - - def get_default( - self, ctx: Context, call: bool = True - ) -> t.Any | t.Callable[[], t.Any] | None: - """Get the default for the parameter. Tries - :meth:`Context.lookup_default` first, then the local default. - - :param ctx: Current context. - :param call: If the default is a callable, call it. Disable to - return the callable instead. - - .. versionchanged:: 8.0.2 - Type casting is no longer performed when getting a default. - - .. versionchanged:: 8.0.1 - Type casting can fail in resilient parsing mode. Invalid - defaults will not prevent showing help text. - - .. versionchanged:: 8.0 - Looks at ``ctx.default_map`` first. - - .. versionchanged:: 8.0 - Added the ``call`` parameter. - """ - value = ctx.lookup_default(self.name, call=False) - - if value is None and not ctx._default_map_has(self.name): - value = self.default - - if call and callable(value): - value = value() - - return value - - @abstractmethod - def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: ... - - def consume_value( - self, ctx: Context, opts: cabc.Mapping[str, t.Any] - ) -> tuple[t.Any, ParameterSource]: - """Returns the parameter value produced by the parser. - - If the parser did not produce a value from user input, the value is either - sourced from the environment variable, the default map, or the parameter's - default value. In that order of precedence. - - If no value is found, an internal sentinel value is returned. - - :meta private: - """ - # Collect from the parse the value passed by the user to the CLI. - value = opts.get(self.name, UNSET) - # If the value is set, it means it was sourced from the command line by the - # parser, otherwise it left unset by default. - source = ( - ParameterSource.COMMANDLINE - if value is not UNSET - else ParameterSource.DEFAULT - ) - - if value is UNSET: - envvar_value = self.value_from_envvar(ctx) - if envvar_value is not None: - value = envvar_value - source = ParameterSource.ENVIRONMENT - - if value is UNSET: - default_map_value = ctx.lookup_default(self.name) - if default_map_value is not None or ctx._default_map_has(self.name): - value = default_map_value - source = ParameterSource.DEFAULT_MAP - - # A string from default_map must be split for multi-value - # parameters, matching value_from_envvar behavior. - if isinstance(value, str) and self.nargs != 1: - value = self.type.split_envvar_value(value) - - if value is UNSET: - default_value = self.get_default(ctx) - if default_value is not UNSET: - value = default_value - 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 parameter's - :attr:`type`, :attr:`multiple`, and :attr:`nargs`. - """ - if value is None: - if self.multiple or self.nargs == -1: - return () - else: - return value - - def check_iter(value: t.Any) -> cabc.Iterator[t.Any]: - try: - return _check_iter(value) - except TypeError: - # This should only happen when passing in args manually, - # the parser should construct an iterable when parsing - # the command line. - raise BadParameter( - _("Value must be an iterable."), ctx=ctx, param=self - ) from None - - # Define the conversion function based on nargs and type. - - if self.nargs == 1 or self.type.is_composite: - - def convert(value: t.Any) -> t.Any: - return self.type(value, param=self, ctx=ctx) - - elif self.nargs == -1: - - def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] - return tuple(self.type(x, self, ctx) for x in check_iter(value)) - - else: # nargs > 1 - - def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] - value = tuple(check_iter(value)) - - if len(value) != self.nargs: - raise BadParameter( - ngettext( - "Takes {nargs} values but 1 was given.", - "Takes {nargs} values but {len} were given.", - len(value), - ).format(nargs=self.nargs, len=len(value)), - ctx=ctx, - param=self, - ) - - return tuple(self.type(x, self, ctx) for x in value) - - if self.multiple: - return tuple(convert(x) for x in check_iter(value)) - - return convert(value) - - def value_is_missing(self, value: t.Any) -> bool: - """A value is considered missing if: - - - it is :attr:`UNSET`, - - or if it is an empty sequence while the parameter is suppose to have - non-single value (i.e. :attr:`nargs` is not ``1`` or :attr:`multiple` is - set). - - :meta private: - """ - if value is UNSET: - 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: - """Process the value of this parameter: - - 1. Type cast the value using :meth:`type_cast_value`. - 2. Check if the value is missing (see: :meth:`value_is_missing`), and raise - :exc:`MissingParameter` if it is required. - 3. If a :attr:`callback` is set, call it to have the value replaced by the - result of the callback. If the value was not set, the callback receive - ``None``. This keep the legacy behavior as it was before the introduction of - the :attr:`UNSET` sentinel. - - :meta private: - """ - # shelter `type_cast_value` from ever seeing an `UNSET` value by handling the - # cases in which `UNSET` gets special treatment explicitly at this layer - # - # Refs: - # https://github.com/pallets/click/issues/3069 - if value is UNSET: - if self.multiple or self.nargs == -1: - value = () - else: - 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: - # Legacy case: UNSET is not exposed directly to the callback, but converted - # to None. - if value is UNSET: - value = None - - # Search for parameters with UNSET values in the context. - unset_keys = {k: None for k, v in ctx.params.items() if v is UNSET} - # No UNSET values, call the callback as usual. - if not unset_keys: - value = self.callback(ctx, self, value) - - # Legacy case: provide a temporarily manipulated context to the callback - # to hide UNSET values as None. - # - # Refs: - # https://github.com/pallets/click/issues/3136 - # https://github.com/pallets/click/pull/3137 - else: - # Add another layer to the context stack to clearly hint that the - # context is temporarily modified. - with ctx: - # Update the context parameters to replace UNSET with None. - ctx.params.update(unset_keys) - # Feed these fake context parameters to the callback. - value = self.callback(ctx, self, value) - # Restore the UNSET values in the context parameters. - ctx.params.update( - { - k: UNSET - for k in unset_keys - # Only restore keys that are present and still None, in case - # the callback modified other parameters. - if k in ctx.params and ctx.params[k] is None - } - ) - - return value - - def resolve_envvar_value(self, ctx: Context) -> str | None: - """Returns the value found in the environment variable(s) attached to this - parameter. - - Environment variables values are `always returned as strings - `_. - - This method returns ``None`` if: - - - the :attr:`envvar` property is not set on the :class:`Parameter`, - - the environment variable is not found in the environment, - - the variable is found in the environment but its value is empty (i.e. the - environment variable is present but has an empty string). - - If :attr:`envvar` is setup with multiple environment variables, - then only the first non-empty value is returned. - - .. caution:: - - The raw value extracted from the environment is not normalized and is - returned as-is. Any normalization or reconciliation is performed later by - the :class:`Parameter`'s :attr:`type`. - - :meta private: - """ - if not self.envvar: - 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) - - # Return the first non-empty value of the list of environment variables. - if rv: - return rv - # Else, absence of value is interpreted as an environment variable that - # is not set, so proceed to the next one. - - return None - - def value_from_envvar(self, ctx: Context) -> str | cabc.Sequence[str] | None: - """Process the raw environment variable string for this parameter. - - Returns the string as-is or splits it into a sequence of strings if the - parameter is expecting multiple values (i.e. its :attr:`nargs` property is set - to a value other than ``1``). - - :meta private: - """ - rv = self.resolve_envvar_value(ctx) - - if rv is not None and self.nargs != 1: - return self.type.split_envvar_value(rv) - - return rv - - def handle_parse_result( - self, ctx: Context, opts: cabc.Mapping[str, t.Any], args: list[str] - ) -> tuple[t.Any, list[str]]: - """Process the value produced by the parser from user input. - - Always process the value through the Parameter's :attr:`type`, wherever it - comes from. - - If the parameter is deprecated, this method warn the user about it. But only if - the value has been explicitly set by the user (and as such, is not coming from - a default). - - :meta private: - """ - # Capture the slot's existing state before we mutate - # ``_parameter_source`` so the write decision below can compare our - # incoming source against the source of the option that already wrote - # the slot (if any). - existing_value = ctx.params.get(self.name, UNSET) - existing_source = ctx.get_parameter_source(self.name) - existing_default_explicit = ctx._param_default_explicit.get(self.name, False) - - with augment_usage_errors(ctx, param=self): - value, source = self.consume_value(ctx, opts) - - # Record the source before processing so eager callbacks and type - # conversion can inspect it. Restored after arbitration if this - # option loses a feature-switch group. - ctx.set_parameter_source(self.name, source) - - # Display a deprecation warning if necessary. - if ( - self.deprecated - and value is not UNSET - and source < ParameterSource.DEFAULT_MAP - ): - message = _( - "DeprecationWarning: The {param_type} {name!r} is deprecated." - "{extra_message}" - ).format( - param_type=self.param_type_name, - name=self.human_readable_name, - extra_message=_format_deprecated_suffix(self.deprecated), - ) - echo(style(message, fg="red"), err=True) - - # Process the value through the parameter's type. - try: - value = self.process_value(ctx, value) - except Exception: - if not ctx.resilient_parsing: - raise - # In resilient parsing mode, we do not want to fail the command if the - # value is incompatible with the parameter type, so we reset the value - # to UNSET, which will be interpreted as a missing value. - value = UNSET - - # Arbitrate the slot when several parameters target the same variable - # name (feature-switch groups). See: https://github.com/pallets/click/issues/3403 - slot_empty = existing_value is UNSET - more_explicit = existing_source is not None and source < existing_source - same_source = existing_source is not None and source == existing_source - auto_would_downgrade_explicit = ( - same_source - and source == ParameterSource.DEFAULT - and existing_default_explicit - and not self._default_explicit - ) - is_winner = ( - slot_empty - or more_explicit - or (same_source and not auto_would_downgrade_explicit) - ) - - if is_winner: - if self.expose_value: - ctx.params[self.name] = value - ctx._param_default_explicit[self.name] = self._default_explicit - elif existing_source is not None: - # Lost arbitration; restore the winning option's source. - ctx.set_parameter_source(self.name, existing_source) - # else: ctx.params[self.name] was populated by code that bypassed - # handle_parse_result (from another option's callback for example). Keep - # the provisional source recorded before process_value so downstream - # lookups don't return ``None``. - - return value, args - - def get_help_record(self, ctx: Context) -> tuple[str, str] | None: - return None - - def get_usage_pieces(self, ctx: Context) -> list[str]: - return [] - - def get_error_hint(self, ctx: Context | None) -> str: - """Get a stringified version of the param for use in error messages to - indicate which param caused the error. - - .. versionchanged:: 8.4.0 - ``ctx`` can be ``None``. - """ - hint_list = self.opts or [self.human_readable_name] - return " / ".join(f"'{x}'" for x in hint_list) - - def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: - """Return a list of completions for the incomplete value. If a - ``shell_complete`` function was given during init, it is used. - Otherwise, the :attr:`type` - :meth:`~click.types.ParamType[t.Any].shell_complete` function is used. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - if self._custom_shell_complete is not None: - results = self._custom_shell_complete(ctx, self, incomplete) - - if results and isinstance(results[0], str): - from click.shell_completion import CompletionItem - - results = [CompletionItem(c) for c in results] - - return t.cast("list[CompletionItem]", results) - - return self.type.shell_complete(ctx, self, incomplete) - - -class Option(Parameter): - """Options are usually optional values on the command line and - have some extra features that arguments don't have. - - All other parameters are passed onwards to the parameter constructor. - - :param show_default: Show the default value for this option in its - help text. Values are not shown by default, unless - :attr:`Context.show_default` is ``True``. If this value is a - string, it shows that string in parentheses instead of the - actual value. This is particularly useful for dynamic options. - For single option boolean flags, the default remains hidden if - its value is ``False``. - :param show_envvar: Controls if an environment variable should be - shown on the help page and error messages. - Normally, environment variables are not shown. - :param prompt: If set to ``True`` or a non empty string then the - user will be prompted for input. If set to ``True`` the prompt - will be the option name capitalized. A deprecated option cannot be - prompted. - :param confirmation_prompt: Prompt a second time to confirm the - value if it was prompted for. Can be set to a string instead of - ``True`` to customize the message. - :param prompt_required: If set to ``False``, the user will be - prompted for input only when the option was specified as a flag - without a value. - :param hide_input: If this is ``True`` then the input on the prompt - will be hidden from the user. This is useful for password input. - :param is_flag: forces this option to act as a flag. The default is - auto detection. - :param flag_value: which value should be used for this flag if it's - enabled. This is set to a boolean automatically if - the option string contains a slash to mark two options. - :param multiple: if this is set to `True` then the argument is accepted - multiple times and recorded. This is similar to ``nargs`` - in how it works but supports arbitrary number of - arguments. - :param count: this flag makes an option increment an integer. - :param allow_from_autoenv: if this is enabled then the value of this - parameter will be pulled from an environment - variable in case a prefix is defined on the - context. - :param help: the help string. - :param hidden: hide this option from help outputs. - :param attrs: Other command arguments described in :class:`Parameter`. - - .. versionchanged:: 8.4.0 - Non-basic ``flag_value`` types (not ``str``, ``int``, ``float``, or - ``bool``) are passed through unchanged instead of being stringified. - Previously, ``type=click.UNPROCESSED`` was required to preserve them. - - .. versionchanged:: 8.2 - ``envvar`` used with ``flag_value`` will always use the ``flag_value``, - previously it would use the value of the environment variable. - - .. versionchanged:: 8.1 - Help text indentation is cleaned here instead of only in the - ``@option`` decorator. - - .. versionchanged:: 8.1 - The ``show_default`` parameter overrides - ``Context.show_default``. - - .. versionchanged:: 8.1 - The default of a single option boolean flag is not shown if the - default value is ``False``. - - .. versionchanged:: 8.0.1 - ``type`` is detected from ``flag_value`` if given, for basic Python - types (``str``, ``int``, ``float``, ``bool``). - """ - - param_type_name = "option" - - prompt: str | None - confirmation_prompt: bool | str - prompt_required: bool - hide_input: bool - hidden: bool - - _flag_needs_value: bool - is_flag: bool - is_bool_flag: bool - flag_value: t.Any - - count: bool - allow_from_autoenv: bool - help: str | None - show_default: bool | str | None - show_choices: bool - show_envvar: bool - - def __init__( - self, - param_decls: cabc.Sequence[str] | None = None, - show_default: bool | str | None = None, - prompt: bool | str = False, - confirmation_prompt: bool | str = False, - prompt_required: bool = True, - hide_input: bool = False, - is_flag: bool | None = None, - flag_value: t.Any = UNSET, - multiple: bool = False, - count: bool = False, - allow_from_autoenv: bool = True, - type: types.ParamType[t.Any] | t.Any | None = None, - help: str | None = None, - hidden: bool = False, - show_choices: bool = True, - show_envvar: bool = False, - deprecated: bool | str = False, - **attrs: t.Any, - ) -> None: - if help: - help = inspect.cleandoc(help) - - super().__init__( - param_decls, type=type, multiple=multiple, deprecated=deprecated, **attrs - ) - - if prompt is True: - if not self.name: - raise TypeError("'name' is required with 'prompt=True'.") - - prompt_text = self.name.replace("_", " ").capitalize() - elif prompt is False: - prompt_text = None - else: - prompt_text = prompt - - if deprecated: - label = _format_deprecated_label(deprecated) - help = f"{help} {label}" if help else label - - self.prompt = prompt_text - self.confirmation_prompt = confirmation_prompt - self.prompt_required = prompt_required - self.hide_input = hide_input - self.hidden = hidden - - # The _flag_needs_value property tells the parser that this option is a flag - # that cannot be used standalone and needs a value. With this information, the - # parser can determine whether to consider the next user-provided argument in - # the CLI as a value for this flag or as a new option. - # If prompt is enabled but not required, then it opens the possibility for the - # option to gets its value from the user. - self._flag_needs_value = self.prompt is not None and not self.prompt_required - - # Auto-detect if this is a flag or not. - if is_flag is None: - # Implicitly a flag because flag_value was set. - if flag_value is not UNSET: - is_flag = True - # Not a flag, but when used as a flag it shows a prompt. - elif self._flag_needs_value: - is_flag = False - # Implicitly a flag because secondary options names were given. - elif self.secondary_opts: - is_flag = True - - # The option is explicitly not a flag, but to determine whether or not it needs - # value, we need to check if `flag_value` or `default` was set. Either one is - # sufficient. - # Ref: https://github.com/pallets/click/issues/3084 - elif is_flag is False and not self._flag_needs_value: - self._flag_needs_value = flag_value is not UNSET or self.default is UNSET - - if is_flag: - # Set missing default for flags if not explicitly required or prompted. - if self.default is UNSET and not self.required and not self.prompt: - if multiple: - self.default = () - - # Auto-detect the type of the flag based on the flag_value. - if type is None: - # A flag without a flag_value is a boolean flag. - if flag_value is UNSET: - self.type: types.ParamType[t.Any] = types.BoolParamType() - # If the flag value is a boolean, use BoolParamType. - elif isinstance(flag_value, bool): - self.type = types.BoolParamType() - # Otherwise, guess the type from the flag value. - else: - guessed = types.convert_type(None, flag_value) - if ( - isinstance(guessed, types.StringParamType) - and not isinstance(flag_value, str) - and flag_value is not None - ): - # The flag_value type couldn't be auto-detected - # (not str, int, float, or bool). Since flag_value - # is a programmer-provided Python object, not CLI - # input, pass it through unchanged instead of - # stringifying it. - self.type = types.UNPROCESSED - else: - self.type = guessed - - self.is_flag = bool(is_flag) - self.is_bool_flag = self.is_flag and isinstance(self.type, types.BoolParamType) - self.flag_value = flag_value - - # Set boolean flag default to False if unset and not required. - if self.is_bool_flag: - if self.default is UNSET and not self.required: - self.default = False - - # The alignment of default to the flag_value is resolved lazily in - # get_default() to prevent callable flag_values (like classes) from - # being instantiated. Refs: - # https://github.com/pallets/click/issues/3121 - # https://github.com/pallets/click/issues/3024#issuecomment-3146199461 - # https://github.com/pallets/click/pull/3030/commits/06847da - - # Set the default flag_value if it is not set. - if self.flag_value is UNSET: - if self.is_flag: - self.flag_value = True - else: - self.flag_value = None - - # Counting. - self.count = count - if count: - if type is None: - self.type = types.IntRange(min=0) - if self.default is UNSET: - self.default = 0 - - self.allow_from_autoenv = allow_from_autoenv - self.help = help - self.show_default = show_default - self.show_choices = show_choices - self.show_envvar = show_envvar - - if __debug__: - if deprecated and prompt: - raise ValueError("`deprecated` options cannot use `prompt`.") - - if self.nargs == -1: - raise TypeError("nargs=-1 is not supported for options.") - - if not self.is_bool_flag and self.secondary_opts: - raise TypeError("Secondary flag is not valid for non-boolean flag.") - - if self.is_bool_flag and self.hide_input and self.prompt is not None: - raise TypeError( - "'prompt' with 'hide_input' is not valid for boolean flag." - ) - - if self.count: - if self.multiple: - raise TypeError("'count' is not valid with 'multiple'.") - - if self.is_flag: - raise TypeError("'count' is not valid with 'is_flag'.") - - def to_info_dict(self) -> dict[str, t.Any]: - """ - .. versionchanged:: 8.3.0 - Returns ``None`` for the :attr:`flag_value` if it was not set. - """ - info_dict = super().to_info_dict() - info_dict.update( - help=self.help, - prompt=self.prompt, - is_flag=self.is_flag, - # We explicitly hide the :attr:`UNSET` value to the user, as we choose to - # make it an implementation detail. And because ``to_info_dict`` has been - # designed for documentation purposes, we return ``None`` instead. - flag_value=self.flag_value if self.flag_value is not UNSET else None, - count=self.count, - hidden=self.hidden, - ) - return info_dict - - def get_default( - self, ctx: Context, call: bool = True - ) -> t.Any | t.Callable[[], t.Any] | None: - """Return the default value for this option. - - For non-boolean flag options, ``default=True`` is treated as a sentinel - meaning "activate this flag by default" and is resolved to - :attr:`flag_value`. For example, with ``--upper/--lower`` feature - switches where ``flag_value="upper"`` and ``default=True``, the default - resolves to ``"upper"``. - - .. caution:: - This substitution only applies to non-boolean flags - (:attr:`is_bool_flag` is ``False``). For boolean flags, ``True`` is - a legitimate Python value and ``default=True`` is returned as-is. - - .. versionchanged:: 8.3.3 - ``default=True`` is no longer substituted with ``flag_value`` for - boolean flags, fixing negative boolean flags like - ``flag_value=False, default=True``. - """ - value = super().get_default(ctx, call=False) - - # Resolve default=True to flag_value lazily (here instead of - # __init__) to prevent callable flag_values (like classes) from - # being instantiated by the callable check below. - if value is True and self.is_flag and not self.is_bool_flag: - value = self.flag_value - elif call and callable(value): - value = value() - - return value - - def get_error_hint(self, ctx: Context | None) -> str: - result = super().get_error_hint(ctx) - if self.show_envvar and self.envvar is not None: - result += f" (env var: '{self.envvar}')" - return result - - def _parse_decls( - self, decls: cabc.Sequence[str], expose_value: bool - ) -> tuple[str, list[str], list[str]]: - opts = [] - secondary_opts = [] - name = None - possible_names = [] - - for decl in decls: - if decl.isidentifier(): - if name is not None: - raise TypeError(_("Name '{name}' defined twice").format(name=name)) - 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( - _( - "Boolean option {decl!r} cannot use the" - " same flag for true/false." - ).format(decl=decl) - ) - 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 "", opts, secondary_opts - raise TypeError( - _( - "Could not determine name for option with declarations {decls!r}" - ).format(decls=decls) - ) - - if not opts and not secondary_opts: - raise TypeError( - _( - "No options defined but a name was passed ({name})." - " Did you mean to declare an argument instead? Did" - " you mean to pass '--{name}'?" - ).format(name=name) - ) - - return name, opts, secondary_opts - - def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: - if self.multiple: - action = "append" - elif self.count: - action = "count" - else: - action = "store" - - if self.is_flag: - action = f"{action}_const" - - if self.is_bool_flag and self.secondary_opts: - parser.add_option( - obj=self, opts=self.opts, dest=self.name, action=action, const=True - ) - parser.add_option( - obj=self, - opts=self.secondary_opts, - dest=self.name, - action=action, - const=False, - ) - else: - parser.add_option( - obj=self, - opts=self.opts, - dest=self.name, - action=action, - const=self.flag_value, - ) - else: - parser.add_option( - obj=self, - opts=self.opts, - dest=self.name, - action=action, - nargs=self.nargs, - ) - - def get_help_record(self, ctx: Context) -> tuple[str, str] | None: - if self.hidden: - return None - - any_prefix_is_slash = False - - def _write_opts(opts: cabc.Sequence[str]) -> str: - nonlocal any_prefix_is_slash - - rv, any_slashes = join_options(opts) - - if any_slashes: - any_prefix_is_slash = True - - if not self.is_flag and not self.count: - rv += f" {self.make_metavar(ctx=ctx)}" - - return rv - - rv = [_write_opts(self.opts)] - - if self.secondary_opts: - rv.append(_write_opts(self.secondary_opts)) - - help = self.help or "" - - extra = self.get_help_extra(ctx) - extra_items = [] - if "envvars" in extra: - extra_items.append( - _("env var: {var}").format(var=", ".join(extra["envvars"])) - ) - if "default" in extra: - extra_items.append(_("default: {default}").format(default=extra["default"])) - if "range" in extra: - extra_items.append(extra["range"]) - if "required" in extra: - extra_items.append(_(extra["required"])) - - if extra_items: - extra_str = "; ".join(extra_items) - help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" - - return ("; " if any_prefix_is_slash else " / ").join(rv), help - - def get_help_extra(self, ctx: Context) -> types.OptionHelpExtra: - extra: types.OptionHelpExtra = {} - - if self.show_envvar: - envvar = self.envvar - - if envvar is None: - if ( - self.allow_from_autoenv - and ctx.auto_envvar_prefix is not None - and self.name - ): - envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" - - if envvar is not None: - if isinstance(envvar, str): - extra["envvars"] = (envvar,) - else: - extra["envvars"] = tuple(str(d) for d in envvar) - - # Temporarily enable resilient parsing to avoid type casting - # failing for the default. Might be possible to extend this to - # help formatting in general. - resilient = ctx.resilient_parsing - ctx.resilient_parsing = True - - try: - default_value = self.get_default(ctx, call=False) - finally: - ctx.resilient_parsing = resilient - - show_default = False - show_default_is_str = False - - if self.show_default is not None: - if isinstance(self.show_default, str): - show_default_is_str = show_default = True - else: - show_default = self.show_default - elif ctx.show_default is not None: - show_default = ctx.show_default - - if show_default_is_str or ( - show_default and (default_value not in (None, UNSET)) - ): - 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 isinstance(default_value, enum.Enum): - default_string = default_value.name - elif inspect.isfunction(default_value): - default_string = _("(dynamic)") - elif self.is_bool_flag and self.secondary_opts: - # For boolean flags that have distinct True/False opts, - # use the opt without prefix instead of the value. - default_string = _split_opt( - (self.opts if default_value else self.secondary_opts)[0] - )[1] - elif self.is_bool_flag and not self.secondary_opts and not default_value: - default_string = "" - elif isinstance(default_value, str) and default_value == "": - default_string = '""' - else: - default_string = str(default_value) - - if default_string: - extra["default"] = default_string - - if ( - isinstance(self.type, types._NumberRangeBase) - # skip count with default range type - and not (self.count and self.type.min == 0 and self.type.max is None) - ): - range_str = self.type._describe_range() - - if range_str: - extra["range"] = range_str - - if self.required: - extra["required"] = "required" - - return extra - - 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 lock in the value before - # attempting any user interaction. - default = self.get_default(ctx) - - # A boolean flag can use a simplified [y/n] confirmation prompt. - if self.is_bool_flag: - # If we have no boolean default, we force the user to explicitly provide - # one. - if default in (UNSET, None): - default = None - # Nothing prevent you to declare an option that is simultaneously: - # 1) auto-detected as a boolean flag, - # 2) allowed to prompt, and - # 3) still declare a non-boolean default. - # This forced casting into a boolean is necessary to align any non-boolean - # default to the prompt, which is going to be a [y/n]-style confirmation - # because the option is still a boolean flag. That way, instead of [y/n], - # we get [Y/n] or [y/N] depending on the truthy value of the default. - # Refs: https://github.com/pallets/click/pull/3030#discussion_r2289180249 - else: - default = bool(default) - return confirm(self.prompt, default) - - # If show_default is given, provide this to `prompt` as well, - # otherwise we use `prompt`'s default behavior - prompt_kwargs: t.Any = {} - if self.show_default is not None: - prompt_kwargs["show_default"] = self.show_default - - return prompt( - self.prompt, - # Use ``None`` to inform the prompt() function to reiterate until a valid - # value is provided by the user if we have no default. - default=None if default is UNSET else default, - type=self.type, - hide_input=self.hide_input, - show_choices=self.show_choices, - confirmation_prompt=self.confirmation_prompt, - value_proc=lambda x: self.process_value(ctx, x), - **prompt_kwargs, - ) - - def resolve_envvar_value(self, ctx: Context) -> str | None: - """:class:`Option` resolves its environment variable the same way as - :func:`Parameter.resolve_envvar_value`, but it also supports - :attr:`Context.auto_envvar_prefix`. If we could not find an environment from - the :attr:`envvar` property, we fallback on :attr:`Context.auto_envvar_prefix` - to build dynamiccaly the environment variable name using the - :python:`{ctx.auto_envvar_prefix}_{self.name.upper()}` template. - - :meta private: - """ - 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: - envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" - rv = os.environ.get(envvar) - - if rv: - return rv - - return None - - def value_from_envvar(self, ctx: Context) -> t.Any: - """For :class:`Option`, this method processes the raw environment variable - string the same way as :func:`Parameter.value_from_envvar` does. - - But in the case of non-boolean flags, the value is analyzed to determine if the - flag is activated or not, and returns a boolean of its activation, or the - :attr:`flag_value` if the latter is set. - - This method also takes care of repeated options (i.e. options with - :attr:`multiple` set to ``True``). - - :meta private: - """ - rv = self.resolve_envvar_value(ctx) - - # Absent environment variable or an empty string is interpreted as unset. - if rv is None: - return None - - # Non-boolean flags are more liberal in what they accept. But a flag being a - # flag, its envvar value still needs to be analyzed to determine if the flag is - # activated or not. - if self.is_flag and not self.is_bool_flag: - # If the flag_value is set and match the envvar value, return it - # directly. - if self.flag_value is not UNSET and rv == self.flag_value: - return self.flag_value - # Analyze the envvar value as a boolean to know if the flag is - # activated or not. - return types.BoolParamType.str_to_bool(rv) - - # Split the envvar value if it is allowed to be repeated. - value_depth = (self.nargs != 1) + bool(self.multiple) - if value_depth > 0: - multi_rv = self.type.split_envvar_value(rv) - if self.multiple and self.nargs != 1: - multi_rv = batch(multi_rv, self.nargs) # type: ignore[assignment] - - return multi_rv - - return rv - - def consume_value( - self, ctx: Context, opts: cabc.Mapping[str, Parameter] - ) -> tuple[t.Any, ParameterSource]: - """For :class:`Option`, the value can be collected from an interactive prompt - if the option is a flag that needs a value (and the :attr:`prompt` property is - set). - - Additionally, this method handles flag option that are activated without a - value, in which case the :attr:`flag_value` is returned. - - :meta private: - """ - value, source = super().consume_value(ctx, opts) - - # The parser will emit a sentinel value if the option is allowed to as a flag - # without a value. - if value is FLAG_NEEDS_VALUE: - # If the option allows for a prompt, we start an interaction with the user. - if self.prompt is not None and not ctx.resilient_parsing: - value = self.prompt_for_value(ctx) - source = ParameterSource.PROMPT - # Else the flag takes its flag_value as value. - else: - value = self.flag_value - source = ParameterSource.COMMANDLINE - - # A flag which is activated always returns the flag value, unless the value - # comes from the explicitly sets default. - elif ( - self.is_flag - and value is True - and not self.is_bool_flag - and source < ParameterSource.DEFAULT_MAP - ): - value = self.flag_value - - # Re-interpret a multiple option which has been sent as-is by the parser. - # Here we replace each occurrence of value-less flags (marked by the - # FLAG_NEEDS_VALUE sentinel) with the flag_value. - elif ( - self.multiple - and value is not UNSET - and isinstance(value, cabc.Iterable) - and source < ParameterSource.DEFAULT_MAP - 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 for one to the user - # if prompting is enabled. - elif ( - (value is UNSET or source >= ParameterSource.DEFAULT_MAP) - 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 - - def process_value(self, ctx: Context, value: t.Any) -> t.Any: - # process_value has to be overridden on Options in order to capture - # `value == UNSET` cases before `type_cast_value()` gets called. - # - # Refs: - # https://github.com/pallets/click/issues/3069 - if self.is_flag and not self.required and self.is_bool_flag and value is UNSET: - value = False - - if self.callback is not None: - value = self.callback(ctx, self, value) - - return value - - # in the normal case, rely on Parameter.process_value - return super().process_value(ctx, value) - - -class Argument(Parameter): - """Arguments are positional parameters to a command. They generally - provide fewer features than options but can have infinite ``nargs`` - and are required by default. - - All parameters are passed onwards to the constructor of :class:`Parameter`. - """ - - param_type_name = "argument" - - def __init__( - self, - param_decls: cabc.Sequence[str], - required: bool | None = None, - **attrs: t.Any, - ) -> None: - # Auto-detect the requirement status of the argument if not explicitly set. - if required is None: - # The argument gets automatically required if it has no explicit default - # value set and is setup to match at least one value. - if attrs.get("default", UNSET) is UNSET: - required = attrs.get("nargs", 1) > 0 - # If the argument has a default value, it is not required. - else: - required = False - - if "multiple" in attrs: - raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") - - super().__init__(param_decls, required=required, **attrs) - - @property - def human_readable_name(self) -> str: - if self.metavar is not None: - return self.metavar - return self.name.upper() - - def make_metavar(self, ctx: Context) -> str: - if self.metavar is not None: - return self.metavar - var = self.type.get_metavar(param=self, ctx=ctx) - if not var: - var = self.name.upper() - # Types like ``Choice`` and ``DateTime`` already surround their metavar - # with square brackets to enumerate the allowed values. Reuse those - # outer brackets as the optional-argument indicator instead of wrapping - # the metavar in a second pair, which would produce ``[[a|b|c]]``. - already_bracketed = var.startswith("[") and var.endswith("]") - if self.deprecated: - var += "!" - if not self.required and not already_bracketed: - var = f"[{var}]" - if self.nargs != 1: - var += "..." - return var - - def _parse_decls( - self, decls: cabc.Sequence[str], expose_value: bool - ) -> tuple[str, list[str], list[str]]: - if not decls: - if not expose_value: - return "", [], [] - raise TypeError("Argument is marked as exposed, but does not have a name.") - if len(decls) == 1: - name = arg = decls[0] - name = name.replace("-", "_").lower() - else: - raise TypeError( - _( - "Arguments take exactly one parameter declaration, got" - " {length}: {decls}." - ).format(length=len(decls), decls=decls) - ) - return name, [arg], [] - - def get_usage_pieces(self, ctx: Context) -> list[str]: - return [self.make_metavar(ctx)] - - def get_error_hint(self, ctx: Context | None) -> str: - if ctx is not None: - return f"'{self.make_metavar(ctx)}'" - return f"'{self.human_readable_name}'" - - def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: - parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) - - -def __getattr__(name: str) -> object: - import warnings - - if name == "BaseCommand": - warnings.warn( - "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" - " 'Command' instead.", - DeprecationWarning, - stacklevel=2, - ) - return _BaseCommand - - if name == "MultiCommand": - warnings.warn( - "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" - " 'Group' instead.", - DeprecationWarning, - stacklevel=2, - ) - return _MultiCommand - - raise AttributeError(name) diff --git a/.venv/lib/python3.12/site-packages/click/decorators.py b/.venv/lib/python3.12/site-packages/click/decorators.py deleted file mode 100644 index db6a45eb..00000000 --- a/.venv/lib/python3.12/site-packages/click/decorators.py +++ /dev/null @@ -1,575 +0,0 @@ -from __future__ import annotations - -import inspect -import typing as t -from functools import update_wrapper -from gettext import gettext as _ - -from .core import Argument -from .core import Command -from .core import Context -from .core import Group -from .core import Option -from .core import Parameter -from .globals import get_current_context -from .utils import echo - -if t.TYPE_CHECKING: - import typing_extensions as te - - P = te.ParamSpec("P") - -R = t.TypeVar("R") -T = t.TypeVar("T") -_AnyCallable = t.Callable[..., t.Any] -FC = t.TypeVar("FC", bound="_AnyCallable | Command") - - -def pass_context(f: t.Callable[te.Concatenate[Context, P], R]) -> t.Callable[P, R]: - """Marks a callback as wanting to receive the current context - object as first argument. - """ - - def new_func(*args: P.args, **kwargs: P.kwargs) -> R: - return f(get_current_context(), *args, **kwargs) - - return update_wrapper(new_func, f) - - -def pass_obj(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: - """Similar to :func:`pass_context`, but only pass the object on the - context onwards (:attr:`Context.obj`). This is useful if that object - represents the state of a nested system. - """ - - def new_func(*args: P.args, **kwargs: P.kwargs) -> R: - return f(get_current_context().obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - -def make_pass_decorator( - object_type: type[T], ensure: bool = False -) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: - """Given an object type this creates a decorator that will work - similar to :func:`pass_obj` but instead of passing the object of the - current context, it will find the innermost context of type - :func:`object_type`. - - This generates a decorator that works roughly like this:: - - from functools import update_wrapper - - def decorator(f): - @pass_context - def new_func(ctx, *args, **kwargs): - obj = ctx.find_object(object_type) - return ctx.invoke(f, obj, *args, **kwargs) - return update_wrapper(new_func, f) - return decorator - - :param object_type: the type of the object to pass. - :param ensure: if set to `True`, a new object will be created and - remembered on the context if it's not there yet. - """ - - def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: - def new_func(*args: P.args, **kwargs: P.kwargs) -> R: - ctx = get_current_context() - - obj: T | None - if ensure: - obj = ctx.ensure_object(object_type) - else: - obj = ctx.find_object(object_type) - - if obj is None: - raise RuntimeError( - "Managed to invoke callback without a context" - f" object of type {object_type.__name__!r}" - " existing." - ) - - return ctx.invoke(f, obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - return decorator - - -def pass_meta_key( - key: str, *, doc_description: str | None = None -) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: - """Create a decorator that passes a key from - :attr:`click.Context.meta` as the first argument to the decorated - function. - - :param key: Key in ``Context.meta`` to pass. - :param doc_description: Description of the object being passed, - inserted into the decorator's docstring. Defaults to "the 'key' - key from Context.meta". - - .. versionadded:: 8.0 - """ - - def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: - def new_func(*args: P.args, **kwargs: P.kwargs) -> R: - ctx = get_current_context() - obj = ctx.meta[key] - return ctx.invoke(f, obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - if doc_description is None: - doc_description = f"the {key!r} key from :attr:`click.Context.meta`" - - decorator.__doc__ = ( - f"Decorator that passes {doc_description} as the first argument" - " to the decorated function." - ) - return decorator - - -CmdType = t.TypeVar("CmdType", bound=Command) - - -# variant: no call, directly as decorator for a function. -@t.overload -def command(name: _AnyCallable) -> Command: ... - - -# variant: with positional name and with positional or keyword cls argument: -# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) -@t.overload -def command( - name: str | None, - cls: type[CmdType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], CmdType]: ... - - -# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) -@t.overload -def command( - name: None = None, - *, - cls: type[CmdType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], CmdType]: ... - - -# variant: with optional string name, no cls argument provided. -@t.overload -def command( - name: str | None = ..., cls: None = None, **attrs: t.Any -) -> t.Callable[[_AnyCallable], Command]: ... - - -def command( - name: str | _AnyCallable | None = None, - cls: type[CmdType] | None = None, - **attrs: t.Any, -) -> Command | t.Callable[[_AnyCallable], Command | CmdType]: - r"""Creates a new :class:`Command` and uses the decorated function as - callback. This will also automatically attach all decorated - :func:`option`\s and :func:`argument`\s as parameters to the command. - - The name of the command defaults to the name of the function, converted to - lowercase, with underscores ``_`` replaced by dashes ``-``, and the suffixes - ``_command``, ``_cmd``, ``_group``, and ``_grp`` are removed. For example, - ``init_data_command`` becomes ``init-data``. - - All keyword arguments are forwarded to the underlying command class. - For the ``params`` argument, any decorated params are appended to - the end of the list. - - Once decorated the function turns into a :class:`Command` instance - that can be invoked as a command line utility or be attached to a - command :class:`Group`. - - :param name: The name of the command. Defaults to modifying the function's - name as described above. - :param cls: The command class to create. Defaults to :class:`Command`. - - .. versionchanged:: 8.2 - The suffixes ``_command``, ``_cmd``, ``_group``, and ``_grp`` are - removed when generating the name. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.1 - The ``params`` argument can be used. Decorated params are - appended to the end of the list. - """ - - func: t.Callable[[_AnyCallable], t.Any] | None = None - - if callable(name): - func = name - name = None - assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." - assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." - - if cls is None: - cls = t.cast("type[CmdType]", Command) - - def decorator(f: _AnyCallable) -> CmdType: - if isinstance(f, Command): - raise TypeError("Attempted to convert a callback into a command twice.") - - attr_params = attrs.pop("params", None) - params = attr_params if attr_params is not None else [] - - try: - decorator_params = f.__click_params__ # type: ignore - except AttributeError: - pass - else: - del f.__click_params__ # type: ignore - params.extend(reversed(decorator_params)) - - if attrs.get("help") is None: - attrs["help"] = f.__doc__ - - if t.TYPE_CHECKING: - assert cls is not None - assert not callable(name) - - if name is not None: - cmd_name = name - else: - cmd_name = f.__name__.lower().replace("_", "-") - cmd_left, sep, suffix = cmd_name.rpartition("-") - - if sep and suffix in {"command", "cmd", "group", "grp"}: - cmd_name = cmd_left - - cmd = cls(name=cmd_name, callback=f, params=params, **attrs) - cmd.__doc__ = f.__doc__ - return cmd - - if func is not None: - return decorator(func) - - return decorator - - -GrpType = t.TypeVar("GrpType", bound=Group) - - -# variant: no call, directly as decorator for a function. -@t.overload -def group(name: _AnyCallable) -> Group: ... - - -# variant: with positional name and with positional or keyword cls argument: -# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) -@t.overload -def group( - name: str | None, - cls: type[GrpType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], GrpType]: ... - - -# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) -@t.overload -def group( - name: None = None, - *, - cls: type[GrpType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], GrpType]: ... - - -# variant: with optional string name, no cls argument provided. -@t.overload -def group( - name: str | None = ..., cls: None = None, **attrs: t.Any -) -> t.Callable[[_AnyCallable], Group]: ... - - -def group( - name: str | _AnyCallable | None = None, - cls: type[GrpType] | None = None, - **attrs: t.Any, -) -> Group | t.Callable[[_AnyCallable], Group | GrpType]: - """Creates a new :class:`Group` with a function as callback. This - works otherwise the same as :func:`command` just that the `cls` - parameter is set to :class:`Group`. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - """ - if cls is None: - cls = t.cast("type[GrpType]", Group) - - if callable(name): - return command(cls=cls, **attrs)(name) - - return command(name, cls, **attrs) - - -def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: - if isinstance(f, Command): - f.params.append(param) - else: - if not hasattr(f, "__click_params__"): - f.__click_params__ = [] # type: ignore - - f.__click_params__.append(param) # type: ignore - - -def argument( - *param_decls: str, cls: type[Argument] | None = None, **attrs: t.Any -) -> t.Callable[[FC], FC]: - """Attaches an argument to the command. All positional arguments are - passed as parameter declarations to :class:`Argument`; all keyword - arguments are forwarded unchanged (except ``cls``). - This is equivalent to creating an :class:`Argument` instance manually - and attaching it to the :attr:`Command.params` list. - - For the default argument class, refer to :class:`Argument` and - :class:`Parameter` for descriptions of parameters. - - :param cls: the argument class to instantiate. This defaults to - :class:`Argument`. - :param param_decls: Passed as positional arguments to the constructor of - ``cls``. - :param attrs: Passed as keyword arguments to the constructor of ``cls``. - """ - if cls is None: - cls = Argument - - def decorator(f: FC) -> FC: - _param_memo(f, cls(param_decls, **attrs)) - return f - - return decorator - - -def option( - *param_decls: str, cls: type[Option] | None = None, **attrs: t.Any -) -> t.Callable[[FC], FC]: - """Attaches an option to the command. All positional arguments are - passed as parameter declarations to :class:`Option`; all keyword - arguments are forwarded unchanged (except ``cls``). - This is equivalent to creating an :class:`Option` instance manually - and attaching it to the :attr:`Command.params` list. - - For the default option class, refer to :class:`Option` and - :class:`Parameter` for descriptions of parameters. - - :param cls: the option class to instantiate. This defaults to - :class:`Option`. - :param param_decls: Passed as positional arguments to the constructor of - ``cls``. - :param attrs: Passed as keyword arguments to the constructor of ``cls``. - """ - if cls is None: - cls = Option - - def decorator(f: FC) -> FC: - _param_memo(f, cls(param_decls, **attrs)) - return f - - return decorator - - -def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--yes`` option which shows a prompt before continuing if - not passed. If the prompt is declined, the program will exit. - - :param param_decls: One or more option names. Defaults to the single - value ``"--yes"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - - def callback(ctx: Context, param: Parameter, value: bool) -> None: - if not value: - ctx.abort() - - if not param_decls: - param_decls = ("--yes",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("callback", callback) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("prompt", _("Do you want to continue?")) - kwargs.setdefault("help", _("Confirm the action without prompting.")) - return option(*param_decls, **kwargs) - - -def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--password`` option which prompts for a password, hiding - input and asking to enter the value again for confirmation. - - :param param_decls: One or more option names. Defaults to the single - value ``"--password"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - if not param_decls: - param_decls = ("--password",) - - kwargs.setdefault("prompt", True) - kwargs.setdefault("confirmation_prompt", True) - kwargs.setdefault("hide_input", True) - return option(*param_decls, **kwargs) - - -def version_option( - version: str | None = None, - *param_decls: str, - package_name: str | None = None, - prog_name: str | None = None, - message: str | None = None, - **kwargs: t.Any, -) -> t.Callable[[FC], FC]: - """Add a ``--version`` option which immediately prints the version - number and exits the program. - - If ``version`` is not provided, Click will try to detect it using - :func:`importlib.metadata.version` to get the version for the - ``package_name``. - - If ``package_name`` is not provided, Click will try to detect it by - inspecting the stack frames. If the detected (or given) name does - not match an installed distribution, Click resolves it as an import - (top-level module) name via - :func:`importlib.metadata.packages_distributions`, so e.g. ``PIL`` - resolves to the ``Pillow`` distribution. - - :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=``. - - .. versionchanged:: 8.4.2 - When ``package_name`` does not match an installed distribution, - Click now resolves it as an import (top-level module). - """ - 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 - nonlocal package_name - - if prog_name is None: - prog_name = ctx.find_root().info_name - - if version is None and package_name is not None: - import importlib.metadata - - try: - version = importlib.metadata.version(package_name) - except importlib.metadata.PackageNotFoundError: - # The given name didn't match an installed distribution. - # Try resolving it as an import (top-level module) name, - # e.g. ``PIL`` is provided by the ``Pillow`` distribution. - distributions = importlib.metadata.packages_distributions().get( - package_name, [] - ) - if len(distributions) == 1: - package_name = distributions[0] - version = importlib.metadata.version(package_name) - elif len(distributions) > 1: - raise RuntimeError( - f"{package_name!r} maps to multiple installed" - f" distributions ({', '.join(distributions)})." - " Pass 'package_name' to disambiguate." - ) from None - else: - raise RuntimeError( - f"{package_name!r} is not installed. Try passing" - " 'package_name' instead." - ) from None - - if version is None: - raise RuntimeError( - f"Could not determine the version for {package_name!r} automatically." - ) - - echo( - message % {"prog": prog_name, "package": package_name, "version": version}, - color=ctx.color, - ) - ctx.exit() - - if not param_decls: - param_decls = ("--version",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("is_eager", True) - kwargs.setdefault("help", _("Show the version and exit.")) - kwargs["callback"] = callback - return option(*param_decls, **kwargs) - - -def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Pre-configured ``--help`` option which immediately prints the help page - and exits the program. - - :param param_decls: One or more option names. Defaults to the single - value ``"--help"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - - def show_help(ctx: Context, param: Parameter, value: bool) -> None: - """Callback that print the help page on ```` and exits.""" - if value and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - if not param_decls: - param_decls = ("--help",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("is_eager", True) - kwargs.setdefault("help", _("Show this message and exit.")) - kwargs.setdefault("callback", show_help) - - return option(*param_decls, **kwargs) diff --git a/.venv/lib/python3.12/site-packages/click/exceptions.py b/.venv/lib/python3.12/site-packages/click/exceptions.py deleted file mode 100644 index 6272c38a..00000000 --- a/.venv/lib/python3.12/site-packages/click/exceptions.py +++ /dev/null @@ -1,378 +0,0 @@ -from __future__ import annotations - -import collections.abc as cabc -import typing as t -from gettext import gettext as _ -from gettext import ngettext - -from ._compat import get_text_stderr -from .globals import resolve_color_default -from .utils import echo -from .utils import format_filename - -if t.TYPE_CHECKING: - from .core import Command - from .core import Context - from .core import Parameter - - -def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None: - if param_hint is not None and not isinstance(param_hint, str): - return " / ".join(repr(x) for x in param_hint) - - return param_hint - - -def _format_possibilities(possibilities: list[str]) -> str: - possibility_str = ", ".join(repr(p) for p in sorted(possibilities)) - return ngettext( - "Did you mean {possibility}?", - "(Did you mean one of: {possibilities}?)", - len(possibilities), - ).format(possibility=possibility_str, possibilities=possibility_str) - - -class ClickException(Exception): - """An exception that Click can handle and show to the user.""" - - #: The exit code for this exception. - exit_code: t.ClassVar[int] = 1 - - show_color: t.Final[bool | None] - message: t.Final[str] - - def __init__(self, message: str) -> None: - super().__init__(message) - # The context will be removed by the time we print the message, so cache - # the color settings here to be used later on (in `show`) - self.show_color = resolve_color_default() - self.message = message - - def format_message(self) -> str: - return self.message - - def __str__(self) -> str: - return self.message - - def show(self, file: t.IO[t.Any] | None = None) -> None: - if file is None: - file = get_text_stderr() - - echo( - _("Error: {message}").format(message=self.format_message()), - file=file, - color=self.show_color, - ) - - -class UsageError(ClickException): - """An internal exception that signals a usage error. This typically - aborts any further handling. - - :param message: the error message to display. - :param ctx: optionally the context that caused this error. Click will - fill in the context automatically in some situations. - """ - - exit_code: t.ClassVar[int] = 2 - - ctx: Context | None - cmd: t.Final[Command | None] - - def __init__(self, message: str, ctx: Context | None = None) -> None: - super().__init__(message) - self.ctx = ctx - self.cmd = self.ctx.command if self.ctx else None - - def show(self, file: t.IO[t.Any] | None = None) -> None: - if file is None: - file = get_text_stderr() - color = None - hint = "" - if ( - self.ctx is not None - and self.ctx.command.get_help_option(self.ctx) is not None - ): - help_names = self.ctx.command.get_help_option_names(self.ctx) - # Pick the longest name (like ``--help`` over ``-h``) for - # readability in error messages. - hint = _("Try '{command} {option}' for help.").format( - command=self.ctx.command_path, - option=max(help_names, key=len), - ) - 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. - """ - - param: Parameter | None - param_hint: cabc.Sequence[str] | str | None - - def __init__( - self, - message: str, - ctx: Context | None = None, - param: Parameter | None = None, - param_hint: cabc.Sequence[str] | str | None = None, - ) -> None: - super().__init__(message, ctx) - self.param = param - self.param_hint = param_hint - - def format_message(self) -> str: - if self.param_hint is not None: - param_hint = self.param_hint - elif self.param is not None: - param_hint = self.param.get_error_hint(self.ctx) - 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'``. - """ - - param_type: t.Final[str | None] - - def __init__( - self, - message: str | None = None, - ctx: Context | None = None, - param: Parameter | None = None, - param_hint: cabc.Sequence[str] | str | None = None, - param_type: str | None = None, - ) -> None: - super().__init__(message or "", ctx, param, param_hint) - self.param_type = param_type - - def format_message(self) -> str: - if self.param_hint is not None: - param_hint: cabc.Sequence[str] | str | None = self.param_hint - elif self.param is not None: - param_hint = self.param.get_error_hint(self.ctx) - else: - param_hint = None - - param_hint = _join_param_hints(param_hint) - param_hint = f" {param_hint}" if param_hint else "" - - param_type = self.param_type - if param_type is None and self.param is not None: - param_type = self.param.param_type_name - - msg = self.message - if self.param is not None: - msg_extra = self.param.type.get_missing_message( - param=self.param, ctx=self.ctx - ) - if msg_extra: - if msg: - msg += f". {msg_extra}" - else: - msg = msg_extra - - msg = f" {msg}" if msg else "" - - # Translate param_type for known types. - if param_type == "argument": - missing = _("Missing argument") - elif param_type == "option": - missing = _("Missing option") - elif param_type == "parameter": - missing = _("Missing parameter") - else: - missing = _("Missing {param_type}").format(param_type=param_type) - - return f"{missing}{param_hint}.{msg}" - - def __str__(self) -> str: - if not self.message: - param_name = self.param.name if self.param else None - return _("Missing parameter: {param_name}").format(param_name=param_name) - else: - return self.message - - -class NoSuchOption(UsageError): - """Raised if Click attempted to handle an option that does not exist. - - .. versionadded:: 4.0 - """ - - option_name: t.Final[str] - possibilities: t.Final[list[str] | None] - - def __init__( - self, - option_name: str, - message: str | None = None, - possibilities: cabc.Iterable[str] | None = None, - ctx: Context | None = None, - ) -> None: - if message is None: - message = _("No such option {name!r}.").format(name=option_name) - - super().__init__(message, ctx) - self.option_name = option_name - - if possibilities: - from difflib import get_close_matches - - possibilities_ = get_close_matches(option_name, possibilities) - else: - possibilities_ = None - self.possibilities = possibilities_ - - def format_message(self) -> str: - if not self.possibilities: - return self.message - return f"{self.message} {_format_possibilities(self.possibilities)}" - - -class NoSuchCommand(UsageError): - """Raised if Click attempted to handle a command that does not exist. - - .. versionadded:: 8.4.0 - """ - - command_name: t.Final[str] - possibilities: t.Final[list[str] | None] - - def __init__( - self, - command_name: str, - message: str | None = None, - possibilities: cabc.Iterable[str] | None = None, - ctx: Context | None = None, - ) -> None: - if message is None: - message = _("No such command {name!r}.").format(name=command_name) - - super().__init__(message, ctx) - self.command_name = command_name - - if possibilities: - from difflib import get_close_matches - - possibilities_ = get_close_matches(command_name, possibilities) - else: - possibilities_ = None - self.possibilities = possibilities_ - - def format_message(self) -> str: - if not self.possibilities: - return self.message - return f"{self.message} {_format_possibilities(self.possibilities)}" - - -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. - """ - - option_name: t.Final[str] - - def __init__( - self, option_name: str, message: str, ctx: Context | None = None - ) -> None: - super().__init__(message, ctx) - self.option_name = option_name - - -class BadArgumentUsage(UsageError): - """Raised if an argument is generally supplied but the use of the argument - was incorrect. This is for instance raised if the number of values - for an argument is not correct. - - .. versionadded:: 6.0 - """ - - -class NoArgsIsHelpError(UsageError): - ctx: Context - - def __init__(self, ctx: Context) -> None: - super().__init__(ctx.get_help(), ctx=ctx) - - def show(self, file: t.IO[t.Any] | None = None) -> None: - echo(self.format_message(), file=file, err=True, color=self.ctx.color) - - -class FileError(ClickException): - """Raised if a file cannot be opened.""" - - ui_filename: t.Final[str] - filename: t.Final[str] - - def __init__(self, filename: str, hint: str | None = None) -> None: - if hint is None: - hint = _("unknown error") - - super().__init__(hint) - self.ui_filename = format_filename(filename) - self.filename = filename - - def format_message(self) -> str: - return _("Could not open file {filename!r}: {message}").format( - filename=self.ui_filename, message=self.message - ) - - -class Abort(RuntimeError): - """An internal signalling exception that signals Click to abort.""" - - -class Exit(RuntimeError): - """An exception that indicates that the application should exit with some - status code. - - :param code: the status code to exit with. - """ - - __slots__ = ("exit_code",) - - exit_code: t.Final[int] - - def __init__(self, code: int = 0) -> None: - self.exit_code = code diff --git a/.venv/lib/python3.12/site-packages/click/formatting.py b/.venv/lib/python3.12/site-packages/click/formatting.py deleted file mode 100644 index c4aa2de5..00000000 --- a/.venv/lib/python3.12/site-packages/click/formatting.py +++ /dev/null @@ -1,320 +0,0 @@ -from __future__ import annotations - -import collections.abc as cabc -from contextlib import contextmanager -from gettext import gettext as _ - -from ._compat import term_len -from .parser import _split_opt - -# Can force a width. This is used by the test system -FORCED_WIDTH: int | None = None - - -def measure_table(rows: cabc.Iterable[tuple[str, str]]) -> tuple[int, ...]: - widths: dict[int, int] = {} - - for row in rows: - for idx, col in enumerate(row): - widths[idx] = max(widths.get(idx, 0), term_len(col)) - - return tuple(y for x, y in sorted(widths.items())) - - -def iter_rows( - rows: cabc.Iterable[tuple[str, str]], col_count: int -) -> cabc.Iterator[tuple[str, ...]]: - for row in rows: - yield row + ("",) * (col_count - len(row)) - - -def wrap_text( - text: str, - width: int = 78, - initial_indent: str = "", - subsequent_indent: str = "", - preserve_paragraphs: bool = False, -) -> str: - """A helper function that intelligently wraps text. By default, it - assumes that it operates on a single paragraph of text but if the - `preserve_paragraphs` parameter is provided it will intelligently - handle paragraphs (defined by two empty lines). - - If paragraphs are handled, a paragraph can be prefixed with an empty - line containing the ``\\b`` character (``\\x08``) to indicate that - no rewrapping should happen in that block. - - :param text: the text that should be rewrapped. - :param width: the maximum width for the text. - :param initial_indent: the initial indent that should be placed on the - first line as a string. - :param subsequent_indent: the indent string that should be placed on - each consecutive line. - :param preserve_paragraphs: if this flag is set then the wrapping will - intelligently handle paragraphs. - - .. versionchanged:: 8.4.0 - Width is measured in visible characters. ANSI escape sequences in - ``text``, ``initial_indent``, or ``subsequent_indent`` no longer - count toward the width budget, so styled input wraps based on what - the user sees instead of raw byte length. - """ - from ._textwrap import TextWrapper - - text = text.expandtabs() - wrapper = TextWrapper( - width, - initial_indent=initial_indent, - subsequent_indent=subsequent_indent, - replace_whitespace=False, - ) - if not preserve_paragraphs: - return wrapper.fill(text) - - p: list[tuple[int, bool, str]] = [] - buf: list[str] = [] - indent = None - - def _flush_par() -> None: - if not buf: - return - if buf[0].strip() == "\b": - p.append((indent or 0, True, "\n".join(buf[1:]))) - else: - p.append((indent or 0, False, " ".join(buf))) - del buf[:] - - for line in text.splitlines(): - if not line: - _flush_par() - indent = None - else: - if indent is None: - orig_len = term_len(line) - line = line.lstrip() - indent = orig_len - term_len(line) - buf.append(line) - _flush_par() - - rv = [] - for indent, raw, text in p: - with wrapper.extra_indent(" " * indent): - if raw: - rv.append(wrapper.indent_only(text)) - else: - rv.append(wrapper.fill(text)) - - return "\n\n".join(rv) - - -class HelpFormatter: - """This class helps with formatting text-based help pages. It's - usually just needed for very special internal cases, but it's also - exposed so that developers can write their own fancy outputs. - - At present, it always writes into memory. - - :param indent_increment: the additional increment for each level. - :param width: the width for the text. This defaults to the terminal - width clamped to a maximum of 78. - """ - - indent_increment: int - width: int - current_indent: int - buffer: list[str] - - def __init__( - self, - indent_increment: int = 2, - width: int | None = None, - max_width: int | None = None, - ) -> None: - self.indent_increment = indent_increment - if max_width is None: - max_width = 80 - if width is None: - import shutil - - 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 = [] - - def write(self, string: str) -> None: - """Writes a unicode string into the internal buffer.""" - self.buffer.append(string) - - def indent(self) -> None: - """Increases the indentation.""" - self.current_indent += self.indent_increment - - def dedent(self) -> None: - """Decreases the indentation.""" - self.current_indent -= self.indent_increment - - def write_usage(self, prog: str, args: str = "", prefix: str | None = None) -> None: - """Writes a usage line into the buffer. - - :param prog: the program name. - :param args: whitespace separated list of arguments. - :param prefix: The prefix for the first line. Defaults to - ``"Usage: "``. - """ - if prefix is None: - prefix = "{usage} ".format(usage=_("Usage:")) - - usage_prefix = f"{prefix:>{self.current_indent}}{prog} " - text_width = self.width - self.current_indent - - if not args: - # Without args, the prefix's trailing space and the wrap_text - # call that would normally place args on the line are both - # unnecessary. Emit just the prefix line. - self.write(usage_prefix.rstrip(" ")) - self.write("\n") - return - - if text_width >= (term_len(usage_prefix) + 20): - # The arguments will fit to the right of the prefix. - indent = " " * term_len(usage_prefix) - self.write( - wrap_text( - args, - text_width, - initial_indent=usage_prefix, - subsequent_indent=indent, - ) - ) - else: - # The prefix is too long, put the arguments on the next line. - self.write(usage_prefix) - self.write("\n") - indent = " " * (max(self.current_indent, term_len(prefix)) + 4) - self.write( - wrap_text( - args, text_width, initial_indent=indent, subsequent_indent=indent - ) - ) - - self.write("\n") - - def write_heading(self, heading: str) -> None: - """Writes a heading into the buffer.""" - self.write(f"{'':>{self.current_indent}}{heading}:\n") - - def write_paragraph(self) -> None: - """Writes a paragraph into the buffer.""" - if self.buffer: - self.write("\n") - - def write_text(self, text: str) -> None: - """Writes re-indented text into the buffer. This rewraps and - preserves paragraphs. - """ - indent = " " * self.current_indent - self.write( - wrap_text( - text, - self.width, - initial_indent=indent, - subsequent_indent=indent, - preserve_paragraphs=True, - ) - ) - self.write("\n") - - def write_dl( - self, - rows: cabc.Iterable[tuple[str, str]], - col_max: int = 30, - col_spacing: int = 2, - ) -> None: - """Writes a definition list into the buffer. This is how options - and commands are usually formatted. - - :param rows: a list of two item tuples for the terms and values. - :param col_max: the maximum width of the first column. - :param col_spacing: the number of spaces between the first and - second column. - """ - rows = list(rows) - widths = measure_table(rows) - if len(widths) != 2: - raise TypeError("Expected two columns for definition list") - - first_col = min(widths[0], col_max) + col_spacing - - for first, second in iter_rows(rows, len(widths)): - self.write(f"{'':>{self.current_indent}}{first}") - if not second: - self.write("\n") - continue - if term_len(first) <= first_col - col_spacing: - self.write(" " * (first_col - term_len(first))) - else: - self.write("\n") - self.write(" " * (first_col + self.current_indent)) - - text_width = max(self.width - first_col - 2, 10) - wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) - lines = wrapped_text.splitlines() - - if lines: - self.write(f"{lines[0]}\n") - - for line in lines[1:]: - self.write(f"{'':>{first_col + self.current_indent}}{line}\n") - else: - self.write("\n") - - @contextmanager - def section(self, name: str) -> cabc.Generator[None]: - """Helpful context manager that writes a paragraph, a heading, - and the indents. - - :param name: the section name that is written as heading. - """ - self.write_paragraph() - self.write_heading(name) - self.indent() - try: - yield - finally: - self.dedent() - - @contextmanager - def indentation(self) -> cabc.Generator[None]: - """A context manager that increases the indentation.""" - self.indent() - try: - yield - finally: - self.dedent() - - def getvalue(self) -> str: - """Returns the buffer contents.""" - return "".join(self.buffer) - - -def join_options(options: cabc.Iterable[str]) -> tuple[str, bool]: - """Given a list of option strings this joins them in the most appropriate - way and returns them in the form ``(formatted_string, - any_prefix_is_slash)`` where the second item in the tuple is a flag that - indicates if any of the option prefixes was a slash. - """ - rv = [] - any_prefix_is_slash = False - - for opt in options: - prefix = _split_opt(opt)[0] - - if prefix == "/": - any_prefix_is_slash = True - - rv.append((len(prefix), opt)) - - rv.sort(key=lambda x: x[0]) - return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/.venv/lib/python3.12/site-packages/click/globals.py b/.venv/lib/python3.12/site-packages/click/globals.py deleted file mode 100644 index a2f91723..00000000 --- a/.venv/lib/python3.12/site-packages/click/globals.py +++ /dev/null @@ -1,67 +0,0 @@ -from __future__ import annotations - -import typing as t -from threading import local - -if t.TYPE_CHECKING: - from .core import Context - -_local = local() - - -@t.overload -def get_current_context(silent: t.Literal[False] = False) -> Context: ... - - -@t.overload -def get_current_context(silent: bool = ...) -> Context | None: ... - - -def get_current_context(silent: bool = False) -> Context | None: - """Returns the current click context. This can be used as a way to - access the current context object from anywhere. This is a more implicit - alternative to the :func:`pass_context` decorator. This function is - primarily useful for helpers such as :func:`echo` which might be - interested in changing its behavior based on the current context. - - To push the current context, :meth:`Context.scope` can be used. - - .. versionadded:: 5.0 - - :param silent: if set to `True` the return value is `None` if no context - is available. The default behavior is to raise a - :exc:`RuntimeError`. - """ - try: - return t.cast("Context", _local.stack[-1]) - except (AttributeError, IndexError) as e: - if not silent: - raise RuntimeError("There is no active click context.") from e - - return None - - -def push_context(ctx: Context) -> None: - """Pushes a new context to the current stack.""" - _local.__dict__.setdefault("stack", []).append(ctx) - - -def pop_context() -> None: - """Removes the top level from the stack.""" - _local.stack.pop() - - -def resolve_color_default(color: bool | None = None) -> bool | None: - """Internal helper to get the default value of the color flag. If a - value is passed it's returned unchanged, otherwise it's looked up from - the current context. - """ - if color is not None: - return color - - ctx = get_current_context(silent=True) - - if ctx is not None: - return ctx.color - - return None diff --git a/.venv/lib/python3.12/site-packages/click/parser.py b/.venv/lib/python3.12/site-packages/click/parser.py deleted file mode 100644 index 4fcbf7ca..00000000 --- a/.venv/lib/python3.12/site-packages/click/parser.py +++ /dev/null @@ -1,533 +0,0 @@ -""" -This module started out as largely a copy paste from the stdlib's -optparse module with the features removed that we do not need from -optparse because we implement them in Click on a higher level (for -instance type handling, help formatting and a lot more). - -The plan is to remove more and more from here over time. - -The reason this is a different module and not optparse from the stdlib -is that there are differences in 2.x and 3.x about the error messages -generated and optparse in the stdlib uses gettext for no good reason -and might cause us issues. - -Click uses parts of optparse written by Gregory P. Ward and maintained -by the Python Software Foundation. This is limited to code in parser.py. - -Copyright 2001-2006 Gregory P. Ward. All rights reserved. -Copyright 2002-2006 Python Software Foundation. All rights reserved. -""" - -# This code uses parts of optparse written by Gregory P. Ward and -# maintained by the Python Software Foundation. -# Copyright 2001-2006 Gregory P. Ward -# Copyright 2002-2006 Python Software Foundation -from __future__ import annotations - -import collections.abc as cabc -import typing as t -from collections import deque -from gettext import gettext as _ -from gettext import ngettext - -from ._utils import FLAG_NEEDS_VALUE -from ._utils import UNSET -from .exceptions import BadArgumentUsage -from .exceptions import BadOptionUsage -from .exceptions import NoSuchOption -from .exceptions import UsageError - -if t.TYPE_CHECKING: - from ._utils import T_FLAG_NEEDS_VALUE - from ._utils import T_UNSET - 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") - - -def _unpack_args( - args: cabc.Sequence[str], nargs_spec: cabc.Sequence[int] -) -> tuple[cabc.Sequence[str | cabc.Sequence[str | T_UNSET] | T_UNSET], 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 ``UNSET``. - """ - args = deque(args) - nargs_spec = deque(nargs_spec) - rv: list[str | tuple[str | T_UNSET, ...] | T_UNSET] = [] - spos: int | None = None - - def _fetch(c: deque[str]) -> str | T_UNSET: - try: - if spos is None: - return c.popleft() - else: - return c.pop() - except IndexError: - return UNSET - - while nargs_spec: - if spos is None: - nargs = nargs_spec.popleft() - else: - nargs = nargs_spec.pop() - - if nargs == 1: - rv.append(_fetch(args)) - elif nargs > 1: - x: list[str | T_UNSET] = [_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(UNSET) - - # spos is the position of the wildcard (star). If it's not `None`, - # we fill it with the remainder. - if spos is not None: - rv[spos] = tuple(args) - args = [] - rv[spos + 1 :] = reversed(rv[spos + 1 :]) - - return tuple(rv), list(args) - - -def _split_opt(opt: str) -> tuple[str, str]: - first = opt[:1] - if first.isalnum(): - return "", opt - if opt[1:2] == first: - return opt[:2], opt[2:] - return first, opt[1:] - - -def _normalize_opt(opt: str, ctx: Context | None) -> str: - if ctx is None or ctx.token_normalize_func is None: - return opt - prefix, opt = _split_opt(opt) - return f"{prefix}{ctx.token_normalize_func(opt)}" - - -class _Option: - def __init__( - self, - obj: CoreOption, - opts: cabc.Sequence[str], - dest: str | None, - action: str | None = None, - nargs: int = 1, - const: t.Any | None = None, - ): - self._short_opts = [] - self._long_opts = [] - self.prefixes: set[str] = set() - - for opt in opts: - prefix, value = _split_opt(opt) - if not prefix: - raise ValueError( - _("Invalid start character for option ({option})").format( - option=opt - ) - ) - self.prefixes.add(prefix[0]) - if len(prefix) == 1 and len(value) == 1: - self._short_opts.append(opt) - else: - self._long_opts.append(opt) - self.prefixes.add(prefix) - - if action is None: - action = "store" - - self.dest = dest - self.action = action - self.nargs = nargs - self.const = const - self.obj = obj - - @property - def takes_value(self) -> bool: - return self.action in ("store", "append") - - def process(self, value: t.Any, state: _ParsingState) -> None: - if self.action == "store": - state.opts[self.dest] = value # type: ignore - elif self.action == "store_const": - state.opts[self.dest] = self.const # type: ignore - elif self.action == "append": - state.opts.setdefault(self.dest, []).append(value) # type: ignore - elif self.action == "append_const": - state.opts.setdefault(self.dest, []).append(self.const) # type: ignore - elif self.action == "count": - state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore - else: - raise ValueError(f"unknown action '{self.action}'") - state.order.append(self.obj) - - -class _Argument: - def __init__(self, obj: CoreArgument, dest: str | None, nargs: int = 1): - self.dest = dest - self.nargs = nargs - self.obj = obj - - def process( - self, - value: str | cabc.Sequence[str | T_UNSET] | T_UNSET, - state: _ParsingState, - ) -> None: - if self.nargs > 1: - assert isinstance(value, cabc.Sequence) - holes = sum(x is UNSET for x in value) - if holes == len(value): - value = UNSET - elif holes != 0: - raise BadArgumentUsage( - _("Argument {name!r} takes {nargs} values.").format( - name=self.dest, nargs=self.nargs - ) - ) - - # We failed to collect any argument value so we consider the argument as unset. - if value == (): - value = UNSET - - state.opts[self.dest] = value # type: ignore - state.order.append(self.obj) - - -class _ParsingState: - def __init__(self, rargs: list[str]) -> None: - self.opts: dict[str, t.Any] = {} - self.largs: list[str] = [] - self.rargs = rargs - self.order: list[CoreParameter] = [] - - -class _OptionParser: - """The option parser is an internal class that is ultimately used to - parse options and arguments. It's modelled after optparse and brings - a similar but vastly simplified API. It should generally not be used - directly as the high level Click classes wrap it for you. - - It's not nearly as extensible as optparse or argparse as it does not - implement features that are implemented on a higher level (such as - types or defaults). - - :param ctx: optionally the :class:`~click.Context` where this parser - should go with. - - .. deprecated:: 8.2 - Will be removed in Click 9.0. - """ - - def __init__(self, ctx: Context | None = None) -> None: - #: The :class:`~click.Context` for this parser. This might be - #: `None` for some advanced use cases. - self.ctx = ctx - #: This controls how the parser deals with interspersed arguments. - #: If this is set to `False`, the parser will stop on the first - #: non-option. Click uses this to implement nested subcommands - #: safely. - self.allow_interspersed_args: bool = True - #: This tells the parser how to deal with unknown options. By - #: default it will error out (which is sensible), but there is a - #: second mode where it will ignore it and continue processing - #: after shifting all the unknown options into the resulting args. - self.ignore_unknown_options: bool = False - - if ctx is not None: - self.allow_interspersed_args = ctx.allow_interspersed_args - self.ignore_unknown_options = ctx.ignore_unknown_options - - self._short_opt: dict[str, _Option] = {} - self._long_opt: dict[str, _Option] = {} - self._opt_prefixes = {"-", "--"} - self._args: list[_Argument] = [] - - def add_option( - self, - obj: CoreOption, - opts: cabc.Sequence[str], - dest: str | None, - action: str | None = None, - nargs: int = 1, - const: t.Any | None = None, - ) -> None: - """Adds a new option named `dest` to the parser. The destination - is not inferred (unlike with optparse) and needs to be explicitly - provided. Action can be any of ``store``, ``store_const``, - ``append``, ``append_const`` or ``count``. - - The `obj` can be used to identify the option in the order list - that is returned from the parser. - """ - opts = [_normalize_opt(opt, self.ctx) for opt in opts] - option = _Option(obj, opts, dest, action=action, nargs=nargs, const=const) - self._opt_prefixes.update(option.prefixes) - for opt in option._short_opts: - self._short_opt[opt] = option - for opt in option._long_opts: - self._long_opt[opt] = option - - def add_argument(self, obj: CoreArgument, dest: str | None, nargs: int = 1) -> None: - """Adds a positional argument named `dest` to the parser. - - The `obj` can be used to identify the option in the order list - that is returned from the parser. - """ - self._args.append(_Argument(obj, dest=dest, nargs=nargs)) - - def parse_args( - self, args: list[str] - ) -> tuple[dict[str, t.Any], list[str], list[CoreParameter]]: - """Parses positional arguments and returns ``(values, args, order)`` - for the parsed options and arguments as well as the leftover - arguments if there are any. The order is a list of objects as they - appear on the command line. If arguments appear multiple times they - will be memorized multiple times as well. - """ - state = _ParsingState(args) - try: - self._process_args_for_options(state) - self._process_args_for_args(state) - except UsageError: - if self.ctx is None or not self.ctx.resilient_parsing: - raise - return state.opts, state.largs, state.order - - def _process_args_for_args(self, state: _ParsingState) -> None: - pargs, args = _unpack_args( - state.largs + state.rargs, [x.nargs for x in self._args] - ) - - for idx, arg in enumerate(self._args): - arg.process(pargs[idx], state) - - state.largs = args - state.rargs = [] - - def _process_args_for_options(self, state: _ParsingState) -> None: - while state.rargs: - arg = state.rargs.pop(0) - arglen = len(arg) - # Double dashes always handled explicitly regardless of what - # prefixes are valid. - if arg == "--": - return - elif arg[:1] in self._opt_prefixes and arglen > 1: - self._process_opts(arg, state) - elif self.allow_interspersed_args: - state.largs.append(arg) - else: - state.rargs.insert(0, arg) - return - - # Say this is the original argument list: - # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] - # ^ - # (we are about to process arg(i)). - # - # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of - # [arg0, ..., arg(i-1)] (any options and their arguments will have - # been removed from largs). - # - # The while loop will usually consume 1 or more arguments per pass. - # If it consumes 1 (eg. arg is an option that takes no arguments), - # then after _process_arg() is done the situation is: - # - # largs = subset of [arg0, ..., arg(i)] - # rargs = [arg(i+1), ..., arg(N-1)] - # - # If allow_interspersed_args is false, largs will always be - # *empty* -- still a subset of [arg0, ..., arg(i-1)], but - # not a very interesting subset! - - def _match_long_opt( - self, opt: str, explicit_value: str | None, state: _ParsingState - ) -> None: - if opt not in self._long_opt: - raise NoSuchOption(opt, possibilities=self._long_opt, 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 = UNSET - - 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 = UNSET - - option.process(value, state) - - if stop: - break - - # If we got any unknown options we recombine the string of the - # remaining options and re-attach the prefix, then report that - # to the state as new large. 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 - ) -> str | cabc.Sequence[str] | T_UNSET | T_FLAG_NEEDS_VALUE: - nargs = option.nargs - - value: str | cabc.Sequence[str] | T_UNSET | T_FLAG_NEEDS_VALUE - - if len(state.rargs) < nargs: - if option.obj._flag_needs_value: - # Option allows omitting the value. - value = FLAG_NEEDS_VALUE - else: - raise BadOptionUsage( - option_name, - ngettext( - "Option {name!r} requires an argument.", - "Option {name!r} requires {nargs} arguments.", - nargs, - ).format(name=option_name, nargs=nargs), - ) - elif nargs == 1: - next_rarg = state.rargs[0] - - if ( - option.obj._flag_needs_value - and isinstance(next_rarg, str) - and next_rarg[:1] in self._opt_prefixes - and len(next_rarg) > 1 - ): - # The next arg looks like the start of an option, don't - # use it as the value if omitting the value is allowed. - value = FLAG_NEEDS_VALUE - else: - value = state.rargs.pop(0) - else: - value = tuple(state.rargs[:nargs]) - del state.rargs[:nargs] - - return value - - def _process_opts(self, arg: str, state: _ParsingState) -> None: - explicit_value = None - # Long option handling happens in two parts. The first part is - # supporting explicitly attached values. In any case, we will try - # to long match the option first. - if "=" in arg: - long_opt, explicit_value = arg.split("=", 1) - else: - long_opt = arg - norm_long_opt = _normalize_opt(long_opt, self.ctx) - - # At this point we will match the (assumed) long option through - # the long option matching code. Note that this allows options - # like "-foo" to be matched as long options. - try: - self._match_long_opt(norm_long_opt, explicit_value, state) - except NoSuchOption: - # At this point the long option matching failed, and we need - # to try with short options. However there is a special rule - # which says, that if we have a two character options prefix - # (applies to "--foo" for instance), we do not dispatch to the - # short option code and will instead raise the no option - # error. - if arg[:2] not in self._opt_prefixes: - self._match_short_opt(arg, state) - return - - if not self.ignore_unknown_options: - raise - - state.largs.append(arg) - - -def __getattr__(name: str) -> object: - import warnings - - if name in { - "OptionParser", - "Argument", - "Option", - "split_opt", - "normalize_opt", - "ParsingState", - }: - warnings.warn( - f"'parser.{name}' is deprecated and will be removed in Click 9.0." - " The old parser is available in 'optparse'.", - DeprecationWarning, - stacklevel=2, - ) - return globals()[f"_{name}"] - - if name == "split_arg_string": - from .shell_completion import split_arg_string - - warnings.warn( - "Importing 'parser.split_arg_string' is deprecated, it will only be" - " available in 'shell_completion' in Click 9.0.", - DeprecationWarning, - stacklevel=2, - ) - return split_arg_string - - raise AttributeError(name) diff --git a/.venv/lib/python3.12/site-packages/click/py.typed b/.venv/lib/python3.12/site-packages/click/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/click/shell_completion.py b/.venv/lib/python3.12/site-packages/click/shell_completion.py deleted file mode 100644 index 468ee772..00000000 --- a/.venv/lib/python3.12/site-packages/click/shell_completion.py +++ /dev/null @@ -1,705 +0,0 @@ -from __future__ import annotations - -import collections.abc as cabc -import os -import re -import typing as t -from gettext import gettext as _ - -from .core import Argument -from .core import Command -from .core import Context -from .core import Group -from .core import Option -from .core import Parameter -from .core import ParameterSource -from .utils import echo - - -def shell_complete( - cli: Command, - ctx_args: cabc.MutableMapping[str, t.Any], - prog_name: str, - complete_var: str, - instruction: str, -) -> t.Literal[0, 1]: - """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) - - # Write bytes, otherwise Windows text stdout translates LF to CRLF and breaks. - if instruction == "source": - echo(comp.source().encode(), nl=False) - return 0 - - if instruction == "complete": - echo(comp.complete().encode()) - return 0 - - return 1 - - -if t.TYPE_CHECKING: - from typing_extensions import TypeVar - - # `Any` is used as default for backwards compatibility (instead of e.g. `str`) - _ValueT_co = TypeVar("_ValueT_co", covariant=True, default=t.Any) -else: - _ValueT_co = t.TypeVar("_ValueT_co", covariant=True) - - -class CompletionItem(t.Generic[_ValueT_co]): - """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: _ValueT_co, - type: str = "plain", - help: str | None = None, - **kwargs: t.Any, - ) -> None: - self.value: _ValueT_co = value - self.type: str = type - self.help: str | None = help - self._info = kwargs - - def __getattr__(self, name: str) -> t.Any: - return self._info.get(name) - - -# Only Bash >= 4.4 has the nosort option. -_SOURCE_BASH = """\ -%(complete_func)s() { - local IFS=$'\\n' - local response - - response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ -%(complete_var)s=bash_complete $1) - - for completion in $response; do - IFS=',' read type value <<< "$completion" - - if [[ $type == 'dir' ]]; then - COMPREPLY=() - compopt -o dirnames - elif [[ $type == 'file' ]]; then - COMPREPLY=() - compopt -o default - elif [[ $type == 'plain' ]]; then - COMPREPLY+=($value) - fi - done - - return 0 -} - -%(complete_func)s_setup() { - complete -o nosort -F %(complete_func)s %(prog_name)s -} - -%(complete_func)s_setup; -""" - -# See ZshComplete.format_completion below, and issue #2703, before -# changing this script. -# -# (TL;DR: _describe is picky about the format, but this Zsh script snippet -# is already widely deployed. So freeze this script, and use clever-ish -# handling of colons in ZshComplet.format_completion.) -_SOURCE_ZSH = """\ -#compdef %(prog_name)s - -%(complete_func)s() { - local -a completions - local -a completions_with_descriptions - local -a response - (( ! $+commands[%(prog_name)s] )) && return 1 - - response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ -%(complete_var)s=zsh_complete %(prog_name)s)}") - - for type key descr in ${response}; do - if [[ "$type" == "plain" ]]; then - if [[ "$descr" == "_" ]]; then - completions+=("$key") - else - completions_with_descriptions+=("$key":"$descr") - fi - elif [[ "$type" == "dir" ]]; then - _path_files -/ - elif [[ "$type" == "file" ]]; then - _path_files -f - fi - done - - if [ -n "$completions_with_descriptions" ]; then - _describe -V unsorted completions_with_descriptions -U - fi - - if [ -n "$completions" ]; then - compadd -U -V unsorted -a completions - fi -} - -if [[ $zsh_eval_context[-1] == loadautofunc ]]; then - # autoload from fpath, call function directly - %(complete_func)s "$@" -else - # eval/source/. command, register function for later - compdef %(complete_func)s %(prog_name)s -fi -""" - -_SOURCE_FISH = """\ -function %(complete_func)s; - set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ -COMP_CWORD=(commandline -t) %(prog_name)s); - - for completion in $response; - set -l metadata (string split "," $completion); - - if test $metadata[1] = "dir"; - __fish_complete_directories $metadata[2]; - else if test $metadata[1] = "file"; - __fish_complete_path $metadata[2]; - else if test $metadata[1] = "plain"; - echo $metadata[2]; - end; - end; -end; - -complete --no-files --command %(prog_name)s --arguments \ -"(%(complete_func)s)"; -""" - - -class _SourceVarsDict(t.TypedDict): - complete_func: str - complete_var: str - prog_name: str - - -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. - """ - - cli: Command - ctx_args: cabc.MutableMapping[str, t.Any] - prog_name: str - complete_var: str - - def __init__( - self, - cli: Command, - ctx_args: cabc.MutableMapping[str, t.Any], - prog_name: str, - complete_var: str, - ) -> None: - self.cli = cli - self.ctx_args = ctx_args - self.prog_name = prog_name - self.complete_var = complete_var - - @property - def func_name(self) -> str: - """The name of the shell function defined by the completion - script. - """ - safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) - return f"_{safe_name}_completion" - - def source_vars(self) -> _SourceVarsDict: - """Vars for formatting :attr:`source_template`. - - By default this provides ``complete_func``, ``complete_var``, - and ``prog_name``. - """ - return { - "complete_func": self.func_name, - "complete_var": self.complete_var, - "prog_name": self.prog_name, - } - - def source(self) -> str: - """Produce the shell script that defines the completion - function. By default this ``%``-style formats - :attr:`source_template` with the dict returned by - :meth:`source_vars`. - """ - return self.source_template % self.source_vars() - - def get_completion_args(self) -> tuple[list[str], str]: - """Use the env vars defined by the shell script to return a - tuple of ``args, incomplete``. This must be implemented by - subclasses. - """ - raise NotImplementedError - - def get_completions( - self, args: list[str], incomplete: str - ) -> list[CompletionItem[str]]: - """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]) -> 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: t.ClassVar[str] = "bash" - source_template: t.ClassVar[str] = _SOURCE_BASH - - @staticmethod - def _check_version() -> None: - import shutil - import subprocess - - bash_exe = shutil.which("bash") - - if bash_exe is None: - match = None - else: - output = subprocess.run( - [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'], - stdout=subprocess.PIPE, - ) - match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) - - if match is not None: - major, minor = match.groups() - - if major < "4" or major == "4" and minor < "4": - echo( - _( - "Shell completion is not supported for Bash" - " versions older than 4.4." - ), - err=True, - ) - else: - echo( - _("Couldn't detect Bash version, shell completion is not supported."), - err=True, - ) - - def source(self) -> str: - self._check_version() - return super().source() - - def get_completion_args(self) -> tuple[list[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - cword = int(os.environ["COMP_CWORD"]) - args = cwords[1:cword] - - try: - incomplete = cwords[cword] - except IndexError: - incomplete = "" - - return args, incomplete - - def format_completion(self, item: CompletionItem[t.Any]) -> str: - return f"{item.type},{item.value}" - - -class ZshComplete(ShellComplete): - """Shell completion for Zsh.""" - - name: t.ClassVar[str] = "zsh" - source_template: t.ClassVar[str] = _SOURCE_ZSH - - def get_completion_args(self) -> tuple[list[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - cword = int(os.environ["COMP_CWORD"]) - args = cwords[1:cword] - - try: - incomplete = cwords[cword] - except IndexError: - incomplete = "" - - return args, incomplete - - def format_completion(self, item: CompletionItem[str]) -> str: - help_ = item.help or "_" - # The zsh completion script uses `_describe` on items with help - # texts (which splits the item help from the item value at the - # first unescaped colon) and `compadd` on items without help - # text (which uses the item value as-is and does not support - # colon escaping). So escape colons in the item value if and - # only if the item help is not the sentinel "_" value, as used - # by the completion script. - # - # (The zsh completion script is potentially widely deployed, and - # thus harder to fix than this method.) - # - # See issue #1812 and issue #2703 for further context. - value = item.value.replace(":", r"\:") if help_ != "_" else item.value - return f"{item.type}\n{value}\n{help_}" - - -class FishComplete(ShellComplete): - """Shell completion for Fish.""" - - name: t.ClassVar[str] = "fish" - source_template: t.ClassVar[str] = _SOURCE_FISH - - def get_completion_args(self) -> tuple[list[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - incomplete = os.environ["COMP_CWORD"] - if incomplete: - incomplete = split_arg_string(incomplete)[0] - 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]) -> str: - """ - .. versionchanged:: 8.4.2 - Escape newlines and replace tabs with spaces in the help text to - fix completion errors with multi-line help strings. - """ - # According to https://fishshell.com/docs/current/cmds/complete.html - # Command substitutions found in ARGUMENTS should return a newline- - # separated list of arguments, and each argument may optionally have a tab - # character followed by the argument description. - if item.help: - help_ = item.help.replace("\n", "\\n").replace("\t", " ") - return f"{item.type},{item.value}\t{help_}" - - return f"{item.type},{item.value}" - - -_available_shells: t.Final[dict[str, type[ShellComplete]]] = { - "bash": BashComplete, - "fish": FishComplete, - "zsh": ZshComplete, -} - -_ShellCompleteT = t.TypeVar("_ShellCompleteT", bound="ShellComplete") - - -def add_completion_class( - cls: type[_ShellCompleteT], name: str | None = None -) -> type[_ShellCompleteT]: - """Register a :class:`ShellComplete` subclass under the given name. - The name will be provided by the completion instruction environment - variable during completion. - - :param cls: The completion class that will handle completion for the - shell. - :param name: Name to register the class under. Defaults to the - class's ``name`` attribute. - """ - if name is None: - name = cls.name - - _available_shells[name] = cls - - return cls - - -@t.overload -def get_completion_class(shell: t.Literal["bash"]) -> type[BashComplete]: ... -@t.overload -def get_completion_class(shell: t.Literal["fish"]) -> type[FishComplete]: ... -@t.overload -def get_completion_class(shell: t.Literal["zsh"]) -> type[ZshComplete]: ... -@t.overload -def get_completion_class(shell: str) -> type[ShellComplete] | None: ... -def get_completion_class(shell: str) -> type[ShellComplete] | None: - """Look up a registered :class:`ShellComplete` subclass by the name - provided by the completion instruction environment variable. If the - name isn't registered, returns ``None``. - - :param shell: Name the class is registered under. - """ - return _available_shells.get(shell) - - -def split_arg_string(string: str) -> list[str]: - """Split an argument string as with :func:`shlex.split`, but don't - fail if the string is incomplete. Ignores a missing closing quote or - incomplete escape sequence and uses the partial token as-is. - - .. code-block:: python - - split_arg_string("example 'my file") - ["example", "my file"] - - split_arg_string("example my\\") - ["example", "my"] - - :param string: String to split. - - .. versionchanged:: 8.2 - Moved to ``shell_completion`` from ``parser``. - """ - import shlex - - lex = shlex.shlex(string, posix=True) - lex.whitespace_split = True - lex.commenters = "" - out = [] - - try: - for token in lex: - out.append(token) - except ValueError: - # Raised when end-of-string is reached in an invalid state. Use - # the partial token as-is. The quote or escape character is in - # lex.state, not lex.token. - out.append(lex.token) - - return out - - -def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: - """Determine if the given parameter is an argument that can still - accept values. - - :param ctx: Invocation context for the command represented by the - parsed complete args. - :param param: Argument object being checked. - """ - if not isinstance(param, Argument): - return False - - value = ctx.params.get(param.name) - return ( - param.nargs == -1 - or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE - or ( - param.nargs > 1 - and isinstance(value, (tuple, list)) - and len(value) < param.nargs - ) - ) - - -def _start_of_option(ctx: Context, value: str) -> bool: - """Check if the value looks like the start of an option.""" - if not value: - return False - - c = value[0] - return c in ctx._opt_prefixes - - -def _is_incomplete_option(ctx: Context, args: list[str], param: Parameter) -> bool: - """Determine if the given parameter is an option that needs a value. - - :param args: List of complete args before the incomplete value. - :param param: Option object being checked. - """ - if not isinstance(param, Option): - return False - - if param.is_flag or param.count: - return False - - last_option = None - - for index, arg in enumerate(reversed(args)): - if index + 1 > param.nargs: - break - - if _start_of_option(ctx, arg): - last_option = arg - break - - return last_option is not None and last_option in param.opts - - -def _resolve_context( - cli: Command, - ctx_args: cabc.MutableMapping[str, t.Any], - prog_name: str, - args: list[str], -) -> Context: - """Produce the context hierarchy starting with the command and - traversing the complete arguments. This only follows the commands, - it doesn't trigger input prompts or callbacks. - - :param cli: Command being called. - :param prog_name: Name of the executable in the shell. - :param args: List of complete args before the incomplete value. - """ - ctx_args["resilient_parsing"] = True - with cli.make_context(prog_name, args.copy(), **ctx_args) as ctx: - args = ctx._protected_args + ctx.args - - while args: - command = ctx.command - - if isinstance(command, Group): - if not command.chain: - name, cmd, args = command.resolve_command(ctx, args) - - if cmd is None: - return ctx - - with cmd.make_context( - name, args, parent=ctx, resilient_parsing=True - ) as sub_ctx: - ctx = sub_ctx - args = ctx._protected_args + ctx.args - else: - sub_ctx = ctx - - while args: - name, cmd, args = command.resolve_command(ctx, args) - - if cmd is None: - return ctx - - with cmd.make_context( - name, - args, - parent=ctx, - allow_extra_args=True, - allow_interspersed_args=False, - resilient_parsing=True, - ) as sub_sub_ctx: - sub_ctx = sub_sub_ctx - args = sub_ctx.args - - ctx = sub_ctx - args = [*sub_ctx._protected_args, *sub_ctx.args] - else: - break - - return ctx - - -def _resolve_incomplete( - ctx: Context, args: list[str], incomplete: str -) -> tuple[Command | Parameter, str]: - """Find the Click object that will handle the completion of the - incomplete value. Return the object and the incomplete value. - - :param ctx: Invocation context for the command represented by - the parsed complete args. - :param args: List of complete args before the incomplete value. - :param incomplete: Value being completed. May be empty. - """ - # Different shells treat an "=" between a long option name and - # value differently. Might keep the value joined, return the "=" - # as a separate item, or return the split name and value. Always - # split and discard the "=" to make completion easier. - if incomplete == "=": - incomplete = "" - elif "=" in incomplete and _start_of_option(ctx, incomplete): - name, _, incomplete = incomplete.partition("=") - args.append(name) - - # The "--" marker tells Click to stop treating values as options - # even if they start with the option character. If it hasn't been - # given and the incomplete arg looks like an option, the current - # command will provide option name completions. - if "--" not in args and _start_of_option(ctx, incomplete): - return ctx.command, incomplete - - params = ctx.command.get_params(ctx) - - # If the last complete arg is an option name with an incomplete - # value, the option will provide value completions. - for param in params: - if _is_incomplete_option(ctx, args, param): - return param, incomplete - - # It's not an option name or value. The first argument without a - # parsed value will provide value completions. - for param in params: - if _is_incomplete_argument(ctx, param): - return param, incomplete - - # There were no unparsed arguments, the command may be a group that - # will provide command name completions. - return ctx.command, incomplete diff --git a/.venv/lib/python3.12/site-packages/click/termui.py b/.venv/lib/python3.12/site-packages/click/termui.py deleted file mode 100644 index 9bc88db1..00000000 --- a/.venv/lib/python3.12/site-packages/click/termui.py +++ /dev/null @@ -1,945 +0,0 @@ -from __future__ import annotations - -import collections.abc as cabc -import inspect -import io -import itertools -import re -import sys -import typing as t -from contextlib import AbstractContextManager -from contextlib import redirect_stdout -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" - - -_HIDDEN_INPUT_MASK = "'***'" - - -def _mask_hidden_input(message: str, value: str) -> str: - """Replace occurrences of ``value`` in ``message`` with a fixed mask. - - Both ``repr(value)`` (the form built-in :class:`ParamType` errors use - via ``{value!r}``) and the raw value are masked. The raw-value pass - uses word-boundary lookarounds so a substring like ``"1"`` does not - match inside ``"10"``, and ``"ent"`` does not match inside - ``"Authentication"``. The empty string is skipped to avoid matching - at every boundary. - """ - message = message.replace(repr(value), _HIDDEN_INPUT_MASK) - if value: - message = re.sub( - rf"(? str: - import getpass - - return getpass.getpass(prompt) - - -def _readline_prompt(func: t.Callable[[str], str], text: str, err: bool) -> str: - """Call a prompt function, passing the full prompt on non-Windows so - readline can handle line editing and cursor positioning correctly. - - On Windows the prompt is written separately via :func:`echo` for - colorama support, with only the last character passed to *func*. - """ - if WIN: - # Write the prompt separately so that we get nice coloring - # through colorama on Windows. - echo(text[:-1], nl=False, err=err) - # Echo the last character to stdout to work around an issue - # where readline causes backspace to clear the whole line. - return func(text[-1:]) - if err: - with redirect_stdout(sys.stderr): - return func(text) - return func(text) - - -def _build_prompt( - text: str, - suffix: str, - show_default: bool | str = False, - default: t.Any | None = None, - show_choices: bool = True, - type: ParamType[t.Any] | None = None, -) -> str: - prompt = text - if type is not None and show_choices and isinstance(type, Choice): - prompt += f" ({', '.join(map(str, type.choices))})" - if isinstance(show_default, str): - default = f"({show_default})" - if default is not None and show_default: - prompt = f"{prompt} [{_format_default(default)}]" - return f"{prompt}{suffix}" - - -def _format_default(default: t.Any) -> t.Any: - if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): - return default.name - - return default - - -def prompt( - text: str, - default: t.Any | None = None, - hide_input: bool = False, - confirmation_prompt: bool | str = False, - type: ParamType[t.Any] | t.Any | None = None, - value_proc: t.Callable[[str], t.Any] | None = None, - prompt_suffix: str = ": ", - show_default: bool | str = 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. - If this value is a string, it shows that string - in parentheses instead of the actual value. - :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): ". - - .. versionchanged:: 8.3.3 - ``show_default`` can be a string to show a custom value instead - of the actual default, matching the help text behavior. - - .. versionchanged:: 8.3.1 - A space is no longer appended to the prompt. - - .. 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: - return _readline_prompt(f, text, err) - 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: - message = _mask_hidden_input(e.message, value) if hide_input else e.message - echo(_("Error: {message}").format(message=message), err=err) - continue - if not confirmation_prompt: - return result - while True: - value2 = prompt_func(confirmation_prompt) - is_empty = not value and not value2 - if value2 or is_empty: - break - if value == value2: - return result - echo(_("Error: The two entered values do not match."), err=err) - - -def confirm( - text: str, - default: bool | None = False, - abort: bool = False, - prompt_suffix: str = ": ", - show_default: bool = True, - err: bool = False, -) -> bool: - """Prompts for confirmation (yes/no question). - - If the user aborts the input by sending a interrupt signal this - function will catch it and raise a :exc:`Abort` exception. - - :param text: the question to ask. - :param default: The default value to use when no input is given. If - ``None``, repeat until input is given. - :param abort: if this is set to `True` a negative answer aborts the - exception by raising :exc:`Abort`. - :param prompt_suffix: a suffix that should be added to the prompt. - :param show_default: shows or hides the default value in the prompt. - :param err: if set to true the file defaults to ``stderr`` instead of - ``stdout``, the same as with echo. - - .. versionchanged:: 8.3.1 - A space is no longer appended to the prompt. - - .. 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: - value = _readline_prompt(visible_prompt_func, prompt, err).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 get_pager_file( - color: bool | None = None, -) -> t.ContextManager[t.TextIO]: - """Context manager. - - Yields a writable file-like object which can be used as an output pager. - - .. versionadded:: 8.4.0 - - :param color: controls if the pager supports ANSI colors or not. The - default is autodetection. - """ - from ._termui_impl import get_pager_file - - color = resolve_color_default(color) - - return get_pager_file(color=color) - - -def echo_via_pager( - text_or_generator: cabc.Iterable[str] | t.Callable[[], cabc.Iterable[str]] | str, - color: bool | None = None, -) -> None: - """This function takes a text and shows it via an environment specific - pager on stdout. - - .. versionchanged:: 3.0 - Added the `color` flag. - - :param text_or_generator: the text to page, or alternatively, a - generator emitting the text to page. - :param color: controls if the pager supports ANSI colors or not. The - default is autodetection. - """ - - if inspect.isgeneratorfunction(text_or_generator): - i = t.cast("t.Callable[[], cabc.Iterable[str]]", text_or_generator)() - elif isinstance(text_or_generator, str): - i = [text_or_generator] - else: - i = iter(t.cast("cabc.Iterable[str]", text_or_generator)) - - # convert every element of i to a text type if necessary - text_generator = (el if isinstance(el, str) else str(el) for el in i) - - with get_pager_file(color=color) as pager: - for text in itertools.chain(text_generator, "\n"): - pager.write(text) - # Flush after each write so a slow generator streams to the pager - # incrementally rather than staying invisible until the pipe buffer - # fills (~8 KB). - pager.flush() - - -@t.overload -def progressbar( - *, - length: int, - label: str | None = None, - hidden: bool = False, - show_eta: bool = True, - show_percent: bool | None = None, - show_pos: bool = False, - fill_char: str = "#", - empty_char: str = "-", - bar_template: str = "%(label)s [%(bar)s] %(info)s", - info_sep: str = " ", - width: int = 36, - file: t.TextIO | None = None, - color: bool | None = None, - update_min_steps: int = 1, -) -> ProgressBar[int]: ... - - -@t.overload -def progressbar( - iterable: cabc.Iterable[V] | None = None, - length: int | None = None, - label: str | None = None, - hidden: bool = False, - show_eta: bool = True, - show_percent: bool | None = None, - show_pos: bool = False, - item_show_func: t.Callable[[V | None], str | None] | None = None, - fill_char: str = "#", - empty_char: str = "-", - bar_template: str = "%(label)s [%(bar)s] %(info)s", - info_sep: str = " ", - width: int = 36, - file: t.TextIO | None = None, - color: bool | None = None, - update_min_steps: int = 1, -) -> ProgressBar[V]: ... - - -def progressbar( - iterable: cabc.Iterable[V] | None = None, - length: int | None = None, - label: str | None = None, - hidden: bool = False, - show_eta: bool = True, - show_percent: bool | None = None, - show_pos: bool = False, - item_show_func: t.Callable[[V | None], str | None] | None = None, - fill_char: str = "#", - empty_char: str = "-", - bar_template: str = "%(label)s [%(bar)s] %(info)s", - info_sep: str = " ", - width: int = 36, - file: t.TextIO | None = None, - color: bool | None = None, - update_min_steps: int = 1, -) -> ProgressBar[V]: - """This function creates an iterable context manager that can be used - to iterate over something while showing a progress bar. It will - either iterate over the `iterable` or `length` items (that are counted - up). While iteration happens, this function will print a rendered - progress bar to the given `file` (defaults to stdout) and will attempt - to calculate remaining time and more. By default, this progress bar - will not be rendered if the file is not a terminal. - - The context manager creates the progress bar. When the context - manager is entered the progress bar is already created. With every - iteration over the progress bar, the iterable passed to the bar is - advanced and the bar is updated. When the context manager exits, - a newline is printed and the progress bar is finalized on screen. - - Note: The progress bar is currently designed for use cases where the - total progress can be expected to take at least several seconds. - Because of this, the ProgressBar class object won't display - progress that is considered too fast, and progress where the time - between steps is less than a second. - - No printing must happen or the progress bar will be unintentionally - destroyed. - - Example usage:: - - with progressbar(items) as bar: - for item in bar: - do_something_with(item) - - Alternatively, if no iterable is specified, one can manually update the - progress bar through the `update()` method instead of directly - iterating over the progress bar. The update method accepts the number - of steps to increment the bar with:: - - with progressbar(length=chunks.total_bytes) as bar: - for chunk in chunks: - process_chunk(chunk) - bar.update(chunks.bytes) - - The ``update()`` method also takes an optional value specifying the - ``current_item`` at the new position. This is useful when used - together with ``item_show_func`` to customize the output for each - manual step:: - - with click.progressbar( - length=total_size, - label='Unzipping archive', - item_show_func=lambda a: a.filename - ) as bar: - for archive in zip_file: - archive.extract() - bar.update(archive.size, archive) - - :param iterable: an iterable to iterate over. If not provided the length - is required. - :param length: the number of items to iterate over. By default the - progressbar will attempt to ask the iterator about its - length, which might or might not work. If an iterable is - also provided this parameter can be used to override the - length. If an iterable is not provided the progress bar - will iterate over a range of that length. - :param label: the label to show next to the progress bar. - :param hidden: hide the progressbar. Defaults to ``False``. When no tty is - detected, it will only print the progressbar label. Setting this to - ``False`` also disables that. - :param show_eta: enables or disables the estimated time display. This is - automatically disabled if the length cannot be - determined. - :param show_percent: enables or disables the percentage display. The - default is `True` if the iterable has a length or - `False` if not. - :param show_pos: enables or disables the absolute position display. The - default is `False`. - :param item_show_func: A function called with the current item which - can return a string to show next to the progress bar. If the - function returns ``None`` nothing is shown. The current item can - be ``None``, such as when entering and exiting the bar. - :param fill_char: the character to use to show the filled part of the - progress bar. - :param empty_char: the character to use to show the non-filled part of - the progress bar. - :param bar_template: the format string to use as template for the bar. - The parameters in it are ``label`` for the label, - ``bar`` for the progress bar and ``info`` for the - info section. - :param info_sep: the separator between multiple info items (eta etc.) - :param width: the width of the progress bar in characters, 0 means full - terminal width - :param file: The file to write to. If this is not a terminal then - only the label is printed. - :param color: controls if the terminal supports ANSI colors or not. The - default is autodetection. This is only needed if ANSI - codes are included anywhere in the progress bar output - which is not the case by default. - :param update_min_steps: Render only when this many updates have - completed. This allows tuning for very fast iterators. - - .. versionadded:: 8.2 - The ``hidden`` argument. - - .. versionchanged:: 8.0 - Output is shown even if execution time is less than 0.5 seconds. - - .. versionchanged:: 8.0 - ``item_show_func`` shows the current item, not the previous one. - - .. versionchanged:: 8.0 - Labels are echoed if the output is not a TTY. Reverts a change - in 7.0 that removed all output. - - .. versionadded:: 8.0 - The ``update_min_steps`` parameter. - - .. versionadded:: 4.0 - The ``color`` parameter and ``update`` method. - - .. versionadded:: 2.0 - """ - from ._termui_impl import ProgressBar - - color = resolve_color_default(color) - return ProgressBar( - iterable=iterable, - length=length, - hidden=hidden, - show_eta=show_eta, - show_percent=show_percent, - show_pos=show_pos, - item_show_func=item_show_func, - fill_char=fill_char, - empty_char=empty_char, - bar_template=bar_template, - info_sep=info_sep, - file=file, - label=label, - width=width, - color=color, - update_min_steps=update_min_steps, - ) - - -def clear() -> None: - """Clears the terminal screen. This will have the effect of clearing - the whole visible space of the terminal and moving the cursor to the - top left. This does not do anything if not connected to a terminal. - - .. versionadded:: 2.0 - """ - if not isatty(sys.stdout): - return - - # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor - echo("\033[2J\033[1;1H", nl=False) - - -def _interpret_color(color: int | tuple[int, int, int] | str, offset: int = 0) -> str: - if isinstance(color, int): - return f"{38 + offset};5;{color:d}" - - if isinstance(color, (tuple, list)): - r, g, b = color - return f"{38 + offset};2;{r:d};{g:d};{b:d}" - - return str(_ansi_colors[color] + offset) - - -def style( - text: t.Any, - fg: int | tuple[int, int, int] | str | None = None, - bg: int | tuple[int, int, int] | str | None = None, - bold: bool | None = None, - dim: bool | None = None, - underline: bool | None = None, - overline: bool | None = None, - italic: bool | None = None, - blink: bool | None = None, - reverse: bool | None = None, - strikethrough: bool | None = None, - reset: bool = True, -) -> str: - """Styles a text with ANSI styles and returns the new string. By - default the styling is self contained which means that at the end - of the string a reset code is issued. This can be prevented by - passing ``reset=False``. - - Examples:: - - click.echo(click.style('Hello World!', fg='green')) - click.echo(click.style('ATTENTION!', blink=True)) - click.echo(click.style('Some things', reverse=True, fg='cyan')) - click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) - - Supported color names: - - * ``black`` (might be a gray) - * ``red`` - * ``green`` - * ``yellow`` (might be an orange) - * ``blue`` - * ``magenta`` - * ``cyan`` - * ``white`` (might be light gray) - * ``bright_black`` - * ``bright_red`` - * ``bright_green`` - * ``bright_yellow`` - * ``bright_blue`` - * ``bright_magenta`` - * ``bright_cyan`` - * ``bright_white`` - * ``reset`` (reset the color code only) - - If the terminal supports it, color may also be specified as: - - - An integer in the interval [0, 255]. The terminal must support - 8-bit/256-color mode. - - An RGB tuple of three integers in [0, 255]. The terminal must - support 24-bit/true-color mode. - - See https://en.wikipedia.org/wiki/ANSI_color and - https://gist.github.com/XVilka/8346728 for more information. - - :param text: the string to style with ansi codes. - :param fg: if provided this will become the foreground color. - :param bg: if provided this will become the background color. - :param bold: if provided this will enable or disable bold mode. - :param dim: if provided this will enable or disable dim mode. This is - badly supported. - :param underline: if provided this will enable or disable underline. - :param overline: if provided this will enable or disable overline. - :param italic: if provided this will enable or disable italic. - :param blink: if provided this will enable or disable blinking. - :param reverse: if provided this will enable or disable inverse - rendering (foreground becomes background and the - other way round). - :param strikethrough: if provided this will enable or disable - striking through text. - :param reset: by default a reset-all code is added at the end of the - string which means that styles do not carry over. This - can be disabled to compose styles. - - .. versionchanged:: 8.0 - A non-string ``message`` is converted to a string. - - .. versionchanged:: 8.0 - Added support for 256 and RGB color codes. - - .. versionchanged:: 8.0 - Added the ``strikethrough``, ``italic``, and ``overline`` - parameters. - - .. versionchanged:: 7.0 - Added support for bright colors. - - .. versionadded:: 2.0 - """ - if not isinstance(text, str): - text = str(text) - - bits = [] - - if fg: - try: - bits.append(f"\033[{_interpret_color(fg)}m") - except KeyError: - raise TypeError(_("Unknown color {colour!r}").format(colour=fg)) from None - - if bg: - try: - bits.append(f"\033[{_interpret_color(bg, 10)}m") - except KeyError: - raise TypeError(_("Unknown color {colour!r}").format(colour=bg)) from None - - if bold is not None: - bits.append(f"\033[{1 if bold else 22}m") - if dim is not None: - bits.append(f"\033[{2 if dim else 22}m") - if underline is not None: - bits.append(f"\033[{4 if underline else 24}m") - if overline is not None: - bits.append(f"\033[{53 if overline else 55}m") - if italic is not None: - bits.append(f"\033[{3 if italic else 23}m") - if blink is not None: - bits.append(f"\033[{5 if blink else 25}m") - if reverse is not None: - bits.append(f"\033[{7 if reverse else 27}m") - if strikethrough is not None: - bits.append(f"\033[{9 if strikethrough else 29}m") - bits.append(text) - if reset: - bits.append(_ansi_reset_all) - return "".join(bits) - - -def unstyle(text: str) -> str: - """Removes ANSI styling information from a string. Usually it's not - necessary to use this function as Click's echo function will - automatically remove styling if necessary. - - .. versionadded:: 2.0 - - :param text: the text to remove style information from. - """ - return strip_ansi(text) - - -def secho( - message: t.Any | None = None, - file: t.IO[t.AnyStr] | None = None, - nl: bool = True, - err: bool = False, - color: bool | None = None, - **styles: t.Any, -) -> None: - """This function combines :func:`echo` and :func:`style` into one - call. As such the following two calls are the same:: - - click.secho('Hello World!', fg='green') - click.echo(click.style('Hello World!', fg='green')) - - All keyword arguments are forwarded to the underlying functions - depending on which one they go with. - - Non-string types will be converted to :class:`str`. However, - :class:`bytes` are passed directly to :meth:`echo` without applying - style. If you want to style bytes that represent text, call - :meth:`bytes.decode` first. - - .. versionchanged:: 8.0 - A non-string ``message`` is converted to a string. Bytes are - passed through without style applied. - - .. versionadded:: 2.0 - """ - if message is not None and not isinstance(message, (bytes, bytearray)): - message = style(message, **styles) - - return echo(message, file=file, nl=nl, err=err, color=color) - - -@t.overload -def edit( - text: bytes | bytearray, - editor: str | None = None, - env: cabc.Mapping[str, str] | None = None, - require_save: bool = False, - extension: str = ".txt", -) -> bytes | None: ... - - -@t.overload -def edit( - text: str, - editor: str | None = None, - env: cabc.Mapping[str, str] | None = None, - require_save: bool = True, - extension: str = ".txt", -) -> str | None: ... - - -@t.overload -def edit( - text: None = None, - editor: str | None = None, - env: cabc.Mapping[str, str] | None = None, - require_save: bool = True, - extension: str = ".txt", - filename: str | cabc.Iterable[str] | None = None, -) -> None: ... - - -def edit( - text: str | bytes | bytearray | None = None, - editor: str | None = None, - env: cabc.Mapping[str, str] | None = None, - require_save: bool = True, - extension: str = ".txt", - filename: str | cabc.Iterable[str] | None = None, -) -> str | bytes | bytearray | None: - r"""Edits the given text in the defined editor. If an editor is given - (should be the full path to the executable but the regular operating - system search path is used for finding the executable) it overrides - the detected editor. Optionally, some environment variables can be - used. If the editor is closed without changes, `None` is returned. In - case a file is edited directly the return value is always `None` and - `require_save` and `extension` are ignored. - - If the editor cannot be opened a :exc:`UsageError` is raised. - - Note for Windows: to simplify cross-platform usage, the newlines are - automatically converted from POSIX to Windows and vice versa. As such, - the message here will have ``\n`` as newline markers. - - :param text: the text to edit. - :param editor: optionally the editor to use. Defaults to automatic - detection. - :param env: environment variables to forward to the editor. - :param require_save: if this is true, then not saving in the editor - will make the return value become `None`. - :param extension: the extension to tell the editor about. This defaults - to `.txt` but changing this might change syntax - highlighting. - :param filename: if provided it will edit this file instead of the - provided text contents. It will not use a temporary - file as an indirection in that case. If the editor supports - editing multiple files at once, a sequence of files may be - passed as well. Invoke `click.file` once per file instead - if multiple files cannot be managed at once or editing the - files serially is desired. - - .. versionchanged:: 8.2.0 - ``filename`` now accepts any ``Iterable[str]`` in addition to a ``str`` - if the ``editor`` supports editing multiple files at once. - - """ - from ._termui_impl import Editor - - ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) - - if filename is None: - return ed.edit(text) - - if isinstance(filename, str): - filename = (filename,) - - ed.edit_files(filenames=filename) - return None - - -def launch(url: str, wait: bool = False, locate: bool = False) -> int: - """This function launches the given URL (or filename) in the default - viewer application for this file type. If this is an executable, it - might launch the executable in a new session. The return value is - the exit code of the launched application. Usually, ``0`` indicates - success. - - Examples:: - - click.launch('https://click.palletsprojects.com/') - click.launch('/my/downloaded/file', locate=True) - - .. versionadded:: 2.0 - - :param url: URL or filename of the thing to launch. - :param wait: Wait for the program to exit before returning. This - only works if the launched program blocks. In particular, - ``xdg-open`` on Linux does not block. - :param locate: if this is set to `True` then instead of launching the - application associated with the URL it will attempt to - launch a file manager with the file located. This - might have weird effects if the URL does not point to - the filesystem. - """ - from ._termui_impl import open_url - - return open_url(url, wait=wait, locate=locate) - - -# If this is provided, getchar() calls into this instead. This is used -# for unittesting purposes. -_getchar: t.Callable[[bool], str] | None = None - - -def getchar(echo: bool = False) -> str: - """Fetches a single character from the terminal and returns it. This - will always return a unicode character and under certain rare - circumstances this might return more than one character. The - situations which more than one character is returned is when for - whatever reason multiple characters end up in the terminal buffer or - standard input was not actually a terminal. - - Note that this will always read from the terminal, even if something - is piped into the standard input. - - Note for Windows: in rare cases when typing non-ASCII characters, this - function might wait for a second character and then return both at once. - This is because certain Unicode characters look like special-key markers. - - .. versionadded:: 2.0 - - :param echo: if set to `True`, the character read will also show up on - the terminal. The default is to not show it. - """ - global _getchar - - if _getchar is None: - from ._termui_impl import getchar as f - - _getchar = f - - return _getchar(echo) - - -def raw_terminal() -> AbstractContextManager[int]: - from ._termui_impl import raw_terminal as f - - return f() - - -def pause(info: str | None = None, err: bool = False) -> None: - """This command stops execution and waits for the user to press any - key to continue. This is similar to the Windows batch "pause" - command. If the program is not run through a terminal, this command - will instead do nothing. - - .. versionadded:: 2.0 - - .. versionadded:: 4.0 - Added the `err` parameter. - - :param info: The message to print before pausing. Defaults to - ``"Press any key to continue..."``. - :param err: if set to message goes to ``stderr`` instead of - ``stdout``, the same as with echo. - """ - if not isatty(sys.stdin) or not isatty(sys.stdout): - return - - if info is None: - info = _("Press any key to continue...") - - try: - if info: - echo(info, nl=False, err=err) - try: - getchar() - except (KeyboardInterrupt, EOFError): - pass - finally: - if info: - echo(err=err) diff --git a/.venv/lib/python3.12/site-packages/click/testing.py b/.venv/lib/python3.12/site-packages/click/testing.py deleted file mode 100644 index 19fae4a6..00000000 --- a/.venv/lib/python3.12/site-packages/click/testing.py +++ /dev/null @@ -1,772 +0,0 @@ -from __future__ import annotations - -import collections.abc as cabc -import contextlib -import io -import os -import pdb -import shlex -import sys -import tempfile -import typing as t -from types import TracebackType - -from . import _compat -from . import formatting -from . import termui -from . import utils -from ._compat import _find_binary_reader - -if t.TYPE_CHECKING: - from _typeshed import ReadableBuffer - - from .core import Command - -if sys.platform == "win32": - CaptureMode: t.TypeAlias = t.Literal["sys"] # pyright: ignore[reportRedeclaration] -else: - CaptureMode: t.TypeAlias = t.Literal["sys", "fd"] # pyright: ignore[reportRedeclaration] -ExceptionInfo: t.TypeAlias = tuple[type[BaseException], BaseException, TracebackType] - - -class EchoingStdin: - _input: t.BinaryIO - _output: t.BinaryIO - _paused: bool - - def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: - self._input = input - self._output = output - self._paused = False - - def __getattr__(self, x: str) -> t.Any: - return getattr(self._input, x) - - def _echo(self, rv: bytes) -> bytes: - if not self._paused: - self._output.write(rv) - - return rv - - def read(self, n: int = -1) -> bytes: - return self._echo(self._input.read(n)) - - def read1(self, n: int = -1) -> bytes: - return self._echo(self._input.read1(n)) # type: ignore - - def readline(self, n: int = -1) -> bytes: - return self._echo(self._input.readline(n)) - - def readlines(self) -> list[bytes]: - return [self._echo(x) for x in self._input.readlines()] - - def __iter__(self) -> cabc.Iterator[bytes]: - return iter(self._echo(x) for x in self._input) - - def __repr__(self) -> str: - return repr(self._input) - - -@contextlib.contextmanager -def _pause_echo(stream: EchoingStdin | None) -> cabc.Generator[None]: - if stream is None: - yield - else: - stream._paused = True - yield - stream._paused = False - - -class _FDCapture: - """Redirect a file descriptor to a temporary file for capture. - - Saves the current target of *targetfd* via :func:`os.dup`, then - redirects it to a temporary file via :func:`os.dup2`. On - :meth:`stop`, restores the original ``fd`` and returns the captured - bytes. Inspired by Pytest's ``FDCapture``. - - .. versionadded:: 8.4.0 - """ - - _targetfd: int - saved_fd: int - _tmpfile: t.BinaryIO | None - - def __init__(self, targetfd: int) -> None: - self._targetfd = targetfd - self.saved_fd = -1 - self._tmpfile = None - - def start(self) -> None: - self.saved_fd = os.dup(self._targetfd) - self._tmpfile = tempfile.TemporaryFile(buffering=0) - os.dup2(self._tmpfile.fileno(), self._targetfd) - - def stop(self) -> bytes: - assert self._tmpfile is not None, "_FDCapture.start() was not called" - os.dup2(self.saved_fd, self._targetfd) - os.close(self.saved_fd) - self.saved_fd = -1 - self._tmpfile.seek(0) - data = self._tmpfile.read() - self._tmpfile.close() - self._tmpfile = None - return data - - -class BytesIOCopy(io.BytesIO): - """Patch ``io.BytesIO`` to let the written stream be copied to another. - - .. versionadded:: 8.2 - """ - - copy_to: io.BytesIO - - def __init__(self, copy_to: io.BytesIO) -> None: - super().__init__() - self.copy_to = copy_to - - def flush(self) -> None: - super().flush() - self.copy_to.flush() - - def write(self, b: ReadableBuffer) -> int: - self.copy_to.write(b) - return super().write(b) - - -class StreamMixer: - """Mixes `` and `` streams. - - The result is available in the ``output`` attribute. - - .. versionadded:: 8.2 - """ - - output: io.BytesIO - stdout: BytesIOCopy - stderr: BytesIOCopy - - def __init__(self) -> None: - self.output = io.BytesIO() - self.stdout = BytesIOCopy(copy_to=self.output) - self.stderr = BytesIOCopy(copy_to=self.output) - - -class _NamedTextIOWrapper(io.TextIOWrapper): - """A :class:`~io.TextIOWrapper` with custom ``name`` and ``mode`` - that does not close its underlying buffer. - - When ``CliRunner`` runs in ``fd`` mode, ``_original_fd`` is patched to - point at the saved (pre-redirection) ``fd``, so C-level consumers that call - :meth:`fileno` (like ``faulthandler`` or ``subprocess``) keep working. In - the default ``sys`` mode ``_original_fd`` stays at ``-1`` and - :meth:`fileno` raises :exc:`io.UnsupportedOperation`, matching the - pre-``8.3.3`` behavior. - """ - - _name: str - _mode: str - _original_fd: int - - def __init__( - self, - buffer: t.BinaryIO, - name: str, - mode: str, - **kwargs: t.Any, - ) -> None: - super().__init__(buffer, **kwargs) - self._name = name - self._mode = mode - self._original_fd = -1 - - def close(self) -> None: - """The buffer this object contains belongs to some other object, - so prevent the default ``__del__`` implementation from closing - that buffer. - - .. versionadded:: 8.3.2 - """ - - def fileno(self) -> int: - """Return the file descriptor of the saved original stream when - ``CliRunner`` runs in ``fd`` mode. Otherwise delegate to - :class:`~io.TextIOWrapper`, which raises - :exc:`io.UnsupportedOperation` for a ``BytesIO``-backed buffer. - """ - if self._original_fd >= 0: - return self._original_fd - return super().fileno() - - @property - def name(self) -> str: - return self._name - - @property - def mode(self) -> str: - return self._mode - - -def make_input_stream( - input: str | bytes | t.IO[t.Any] | None, charset: str -) -> t.BinaryIO: - # Is already an input stream. - if hasattr(input, "read"): - rv = _find_binary_reader(t.cast("t.IO[t.Any]", input)) - - if rv is not None: - return rv - - raise TypeError("Could not find binary reader for input stream.") - - if input is None: - input = b"" - elif isinstance(input, str): - input = input.encode(charset) - - return io.BytesIO(input) - - -class Result: - """Holds the captured result of an invoked CLI script. - - :param runner: The runner that created the result - :param stdout_bytes: The standard output as bytes. - :param stderr_bytes: The standard error as bytes. - :param output_bytes: A mix of ``stdout_bytes`` and ``stderr_bytes``, as the - user would see it in its terminal. - :param return_value: The value returned from the invoked command. - :param exit_code: The exit code as integer. - :param exception: The exception that happened if one did. - :param exc_info: Exception information (exception type, exception instance, - traceback type). - - .. versionchanged:: 8.2 - ``stderr_bytes`` no longer optional, ``output_bytes`` introduced and - ``mix_stderr`` has been removed. - - .. versionadded:: 8.0 - Added ``return_value``. - """ - - runner: CliRunner - stdout_bytes: bytes - stderr_bytes: bytes - output_bytes: bytes - return_value: t.Any - exit_code: int - exception: BaseException | None - exc_info: ExceptionInfo | None - - def __init__( - self, - runner: CliRunner, - stdout_bytes: bytes, - stderr_bytes: bytes, - output_bytes: bytes, - return_value: t.Any, - exit_code: int, - exception: BaseException | None, - exc_info: ExceptionInfo | None = None, - ) -> None: - self.runner = runner - self.stdout_bytes = stdout_bytes - self.stderr_bytes = stderr_bytes - self.output_bytes = output_bytes - self.return_value = return_value - self.exit_code = exit_code - self.exception = exception - self.exc_info = exc_info - - @property - def output(self) -> str: - """The terminal output as unicode string, as the user would see it. - - .. versionchanged:: 8.2 - No longer a proxy for ``self.stdout``. Now has its own independent stream - that is mixing `` and ``, in the order they were written. - """ - return self.output_bytes.decode(self.runner.charset, "replace").replace( - "\r\n", "\n" - ) - - @property - def stdout(self) -> str: - """The standard output as unicode string.""" - return self.stdout_bytes.decode(self.runner.charset, "replace").replace( - "\r\n", "\n" - ) - - @property - def stderr(self) -> str: - """The standard error as unicode string. - - .. versionchanged:: 8.2 - No longer raise an exception, always returns the `` string. - """ - return self.stderr_bytes.decode(self.runner.charset, "replace").replace( - "\r\n", "\n" - ) - - def __repr__(self) -> str: - exc_str = repr(self.exception) if self.exception else "okay" - return f"<{type(self).__name__} {exc_str}>" - - -class CliRunner: - """The CLI runner provides functionality to invoke a Click command line - script for unittesting purposes in a isolated environment. This only - works in single-threaded systems without any concurrency as it changes the - global interpreter state. - - :param charset: the character set for the input and output data. - :param env: a dictionary with environment variables for overriding. - :param echo_stdin: if this is set to `True`, then reading from `` writes - to ``. This is useful for showing examples in - some circumstances. Note that regular prompts - will automatically echo the input. - :param catch_exceptions: Whether to catch any exceptions other than - ``SystemExit`` when running :meth:`~CliRunner.invoke`. - :param capture: Selects the output capture strategy. ``sys`` (default) - captures Python-level writes only and leaves - :meth:`sys.stdout.fileno` raising :exc:`io.UnsupportedOperation`, so - user code that calls :func:`os.dup2` on ``sys.stdout.fileno()`` cannot - clobber the host runner's stdout. ``fd`` redirects file descriptors - ``1`` and ``2`` via :func:`os.dup2` to a temporary file, also catching - output from stale stream references, C extensions, and subprocesses. - ``fd`` is not supported on Windows. - - .. versionchanged:: 8.4.0 - Added the ``capture`` parameter. The default ``sys`` mode no longer - exposes the original fd through :meth:`fileno`, reverting the change - introduced in ``8.3.3`` that broke Pytest's ``fd``-level capture - teardown. Use ``capture="fd"`` to restore that behavior with proper - isolation. :issue:`3384` - - .. versionchanged:: 8.2 - Added the ``catch_exceptions`` parameter. - - .. versionchanged:: 8.2 - ``mix_stderr`` parameter has been removed. - """ - - charset: str - env: cabc.Mapping[str, str | None] - echo_stdin: bool - catch_exceptions: bool - capture: CaptureMode - - def __init__( - self, - charset: str = "utf-8", - env: cabc.Mapping[str, str | None] | None = None, - echo_stdin: bool = False, - catch_exceptions: bool = True, - capture: CaptureMode = "sys", - ) -> None: - if capture not in {"sys", "fd"}: - raise ValueError( - f"capture={capture!r} is not valid. Choose from 'sys' or 'fd'." - ) - if capture == "fd" and sys.platform == "win32": - raise ValueError( - f"capture={capture!r} is not supported on Windows. Use 'sys'." - ) - self.charset = charset - self.env = env or {} - self.echo_stdin = echo_stdin - self.catch_exceptions = catch_exceptions - self.capture = capture - - def get_default_prog_name(self, cli: Command) -> str: - """Given a command object it will return the default program name - for it. The default is the `name` attribute or ``"root"`` if not - set. - """ - return cli.name or "root" - - def make_env( - self, overrides: cabc.Mapping[str, str | None] | None = None - ) -> cabc.Mapping[str, str | None]: - """Returns the environment overrides for invoking a script.""" - rv = dict(self.env) - if overrides: - rv.update(overrides) - return rv - - @contextlib.contextmanager - def isolation( - self, - input: str | bytes | t.IO[t.Any] | None = None, - env: cabc.Mapping[str, str | None] | None = None, - color: bool = False, - ) -> cabc.Generator[tuple[io.BytesIO, io.BytesIO, io.BytesIO]]: - """A context manager that sets up the isolation for invoking of a - command line tool. This sets up `` with the given input data - and `os.environ` with the overrides from the given dictionary. - This also rebinds some internals in Click to be mocked (like the - prompt functionality). - - This is automatically done in the :meth:`invoke` method. - - :param input: the input stream to put into `sys.stdin`. - :param env: the environment overrides as dictionary. - :param color: whether the output should contain color codes. The - application can still override this explicitly. - - .. versionadded:: 8.2 - An additional output stream is returned, which is a mix of - `` and `` streams. - - .. versionchanged:: 8.2 - Always returns the `` stream. - - .. versionchanged:: 8.0 - `` is opened with ``errors="backslashreplace"`` - instead of the default ``"strict"``. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - """ - bytes_input = make_input_stream(input, self.charset) - echo_input = None - - old_stdin = sys.stdin - old_stdout = sys.stdout - old_stderr = sys.stderr - old_forced_width = formatting.FORCED_WIDTH - formatting.FORCED_WIDTH = 80 - - env = self.make_env(env) - - stream_mixer = StreamMixer() - - if self.echo_stdin: - bytes_input = echo_input = t.cast( - t.BinaryIO, EchoingStdin(bytes_input, stream_mixer.stdout) - ) - - sys.stdin = text_input = _NamedTextIOWrapper( - bytes_input, encoding=self.charset, name="", mode="r" - ) - - if self.echo_stdin: - # Force unbuffered reads, otherwise TextIOWrapper reads a - # large chunk which is echoed early. - text_input._CHUNK_SIZE = 1 # type: ignore - - sys.stdout = _NamedTextIOWrapper( - stream_mixer.stdout, - encoding=self.charset, - name="", - mode="w", - ) - - sys.stderr = _NamedTextIOWrapper( - stream_mixer.stderr, - encoding=self.charset, - name="", - mode="w", - errors="backslashreplace", - ) - - @_pause_echo(echo_input) # type: ignore - def visible_input(prompt: str | None = None) -> str: - sys.stdout.write(prompt or "") - try: - val = next(text_input).rstrip("\r\n") - except StopIteration as e: - raise EOFError() from e - sys.stdout.write(f"{val}\n") - sys.stdout.flush() - return val - - @_pause_echo(echo_input) # type: ignore - def hidden_input(prompt: str | None = None) -> str: - sys.stdout.write(f"{prompt or ''}\n") - sys.stdout.flush() - try: - return next(text_input).rstrip("\r\n") - except StopIteration as e: - raise EOFError() from e - - @_pause_echo(echo_input) # type: ignore - def _getchar(echo: bool) -> str: - char = sys.stdin.read(1) - - if echo: - sys.stdout.write(char) - - sys.stdout.flush() - return char - - default_color = color - - def should_strip_ansi( - stream: t.IO[t.Any] | None = None, color: bool | None = None - ) -> bool: - if color is None: - return not default_color - return not color - - old_visible_prompt_func = termui.visible_prompt_func - old_hidden_prompt_func = termui.hidden_prompt_func - old__getchar_func = termui._getchar - old_should_strip_ansi = utils.should_strip_ansi # type: ignore - old__compat_should_strip_ansi = _compat.should_strip_ansi - old_pdb_init = pdb.Pdb.__init__ - termui.visible_prompt_func = visible_input - termui.hidden_prompt_func = hidden_input - termui._getchar = _getchar - utils.should_strip_ansi = should_strip_ansi # type: ignore - _compat.should_strip_ansi = should_strip_ansi - - def _patched_pdb_init( - self: pdb.Pdb, - completekey: str = "tab", - stdin: t.IO[str] | None = None, - stdout: t.IO[str] | None = None, - **kwargs: t.Any, - ) -> None: - """Default ``pdb.Pdb`` to real terminal streams during - ``CliRunner`` isolation. - - Without this patch, ``pdb.Pdb.__init__`` inherits from - ``cmd.Cmd`` which falls back to ``sys.stdin``/``sys.stdout`` - when no explicit streams are provided. During isolation - those are ``BytesIO``-backed wrappers, so the debugger - reads from an empty buffer and writes to captured output, - making interactive debugging impossible. - - By defaulting to ``sys.__stdin__``/``sys.__stdout__`` (the - original terminal streams Python preserves regardless of - redirection), debuggers can interact with the user while - ``click.echo`` output is still captured normally. - - This covers ``pdb.set_trace()``, ``breakpoint()``, - ``pdb.post_mortem()``, and debuggers that subclass - ``pdb.Pdb`` (ipdb, pdbpp). Explicit ``stdin``/``stdout`` - arguments are honored and not overridden. Debuggers that - do not subclass ``pdb.Pdb`` (pudb, debugpy) are not - covered. - """ - if stdin is None: - stdin = sys.__stdin__ - if stdout is None: - stdout = sys.__stdout__ - old_pdb_init( - self, completekey=completekey, stdin=stdin, stdout=stdout, **kwargs - ) - - pdb.Pdb.__init__ = _patched_pdb_init # type: ignore[assignment] - - old_env = {} - try: - for key, value in env.items(): - old_env[key] = os.environ.get(key) - if value is None: - try: - del os.environ[key] - except Exception: - pass - else: - os.environ[key] = value - yield (stream_mixer.stdout, stream_mixer.stderr, stream_mixer.output) - finally: - for key, value in old_env.items(): - if value is None: - try: - del os.environ[key] - except Exception: - pass - else: - os.environ[key] = value - sys.stdout = old_stdout - sys.stderr = old_stderr - sys.stdin = old_stdin - termui.visible_prompt_func = old_visible_prompt_func - termui.hidden_prompt_func = old_hidden_prompt_func - termui._getchar = old__getchar_func - utils.should_strip_ansi = old_should_strip_ansi # type: ignore - _compat.should_strip_ansi = old__compat_should_strip_ansi - formatting.FORCED_WIDTH = old_forced_width - pdb.Pdb.__init__ = old_pdb_init # type: ignore[method-assign] - - def invoke( - self, - cli: Command, - args: str | cabc.Sequence[str] | None = None, - input: str | bytes | t.IO[t.Any] | None = None, - env: cabc.Mapping[str, str | None] | None = None, - catch_exceptions: bool | None = None, - color: bool = False, - **extra: t.Any, - ) -> Result: - """Invokes a command in an isolated environment. The arguments are - forwarded directly to the command line script, the `extra` keyword - arguments are passed to the :meth:`~clickpkg.Command.main` function of - the command. - - This returns a :class:`Result` object. - - :param cli: the command to invoke - :param args: the arguments to invoke. It may be given as an iterable - or a string. When given as string it will be interpreted - as a Unix shell command. More details at - :func:`shlex.split`. - :param input: the input data for `sys.stdin`. - :param env: the environment overrides. - :param catch_exceptions: Whether to catch any other exceptions than - ``SystemExit``. If :data:`None`, the value - from :class:`CliRunner` is used. - :param extra: the keyword arguments to pass to :meth:`main`. - :param color: whether the output should contain color codes. The - application can still override this explicitly. - - .. versionadded:: 8.2 - The result object has the ``output_bytes`` attribute with - the mix of ``stdout_bytes`` and ``stderr_bytes``, as the user would - see it in its terminal. - - .. versionchanged:: 8.2 - The result object always returns the ``stderr_bytes`` stream. - - .. versionchanged:: 8.0 - The result object has the ``return_value`` attribute with - the value returned from the invoked command. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - - .. versionchanged:: 3.0 - Added the ``catch_exceptions`` parameter. - - .. versionchanged:: 3.0 - The result object has the ``exc_info`` attribute with the - traceback if available. - """ - exc_info = None - if catch_exceptions is None: - catch_exceptions = self.catch_exceptions - - # Set up fd capture before isolation replaces sys.stdout and sys.stderr. - cap_out: _FDCapture | None = None - cap_err: _FDCapture | None = None - - if self.capture == "fd": - cap_out = _FDCapture(1) - cap_err = _FDCapture(2) - try: - cap_out.start() - cap_err.start() - except OSError: - cap_out = cap_err = None - - with self.isolation(input=input, env=env, color=color) as outstreams: - # Point the captured streams' fileno() at the saved (original) - # fd so that C-level consumers like faulthandler keep working - # while fd 1/2 are redirected to the capture tmpfile. - if cap_out is not None and cap_err is not None: - sys.stdout._original_fd = cap_out.saved_fd # type: ignore[union-attr] - sys.stderr._original_fd = cap_err.saved_fd # type: ignore[union-attr] - - return_value = None - exception: BaseException | None = None - exit_code = 0 - - if isinstance(args, str): - args = shlex.split(args) - - try: - prog_name = extra.pop("prog_name") - except KeyError: - prog_name = self.get_default_prog_name(cli) - - try: - return_value = cli.main(args=args or (), prog_name=prog_name, **extra) - except SystemExit as e: - exc_info = sys.exc_info() - e_code = t.cast("int | t.Any | None", e.code) - - if e_code is None: - e_code = 0 - - if e_code != 0: - exception = e - - if not isinstance(e_code, int): - sys.stdout.write(str(e_code)) - sys.stdout.write("\n") - e_code = 1 - - exit_code = e_code - - except Exception as e: - if not catch_exceptions: - raise - exception = e - exit_code = 1 - exc_info = sys.exc_info() - finally: - sys.stdout.flush() - sys.stderr.flush() - - # Stop fd capture and merge the captured bytes into - # the stdout/stderr BytesIO streams. BytesIOCopy mirrors - # those writes into outstreams[2] automatically. - if cap_out is not None and cap_err is not None: - fd_out = cap_out.stop() - fd_err = cap_err.stop() - if fd_out: - outstreams[0].write(fd_out) - if fd_err: - outstreams[1].write(fd_err) - - stdout = outstreams[0].getvalue() - stderr = outstreams[1].getvalue() - output = outstreams[2].getvalue() - - return Result( - runner=self, - stdout_bytes=stdout, - stderr_bytes=stderr, - output_bytes=output, - return_value=return_value, - exit_code=exit_code, - exception=exception, - exc_info=exc_info, # type: ignore - ) - - @contextlib.contextmanager - def isolated_filesystem( - self, temp_dir: str | os.PathLike[str] | None = None - ) -> cabc.Generator[str]: - """A context manager that creates a temporary directory and - changes the current working directory to it. This isolates tests - that affect the contents of the CWD to prevent them from - interfering with each other. - - :param temp_dir: Create the temporary directory under this - directory. If given, the created directory is not removed - when exiting. - - .. versionchanged:: 8.0 - Added the ``temp_dir`` parameter. - """ - cwd = os.getcwd() - dt = tempfile.mkdtemp(dir=temp_dir) - os.chdir(dt) - - try: - yield dt - finally: - os.chdir(cwd) - - if temp_dir is None: - import shutil - - try: - shutil.rmtree(dt) - except OSError: - pass diff --git a/.venv/lib/python3.12/site-packages/click/types.py b/.venv/lib/python3.12/site-packages/click/types.py deleted file mode 100644 index 1e9872e4..00000000 --- a/.venv/lib/python3.12/site-packages/click/types.py +++ /dev/null @@ -1,1374 +0,0 @@ -from __future__ import annotations - -import abc -import collections.abc as cabc -import enum -import os -import stat -import sys -import typing as t -import uuid -from datetime import datetime -from gettext import gettext as _ -from gettext import ngettext - -from ._compat import _get_argv_encoding -from ._compat import open_stream -from .exceptions import BadParameter -from .utils import format_filename -from .utils import LazyFile -from .utils import safecall - -if t.TYPE_CHECKING: - import typing_extensions as te - - from .core import Context - from .core import Parameter - from .shell_completion import CompletionItem - -_ValueT = t.TypeVar("_ValueT") -_ValueT_contra = t.TypeVar("_ValueT_contra", contravariant=True) -_ValueT_co = t.TypeVar("_ValueT_co", covariant=True) - -_FloatValueT = t.TypeVar("_FloatValueT", bound=float) -_FloatValueT_co = t.TypeVar("_FloatValueT_co", bound=float, covariant=True) - - -class ParamTypeInfoDict(t.TypedDict): - param_type: str - name: str - - -class ParamType(t.Generic[_ValueT_co], abc.ABC): - """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. - - .. versionchanged:: 8.4.0 - Now a generic abstract base class. Parameterize with the - converted value type (``ParamType[int]`` for an integer-returning - type) so that :meth:`convert` and downstream consumers carry the - narrowed return type. - """ - - is_composite: t.ClassVar[bool] = False - arity: int = 1 # read-only - - #: the descriptive name of this type - name: str - - #: if a list of this type is expected and the value is pulled from a - #: string environment variable, this is what splits it up. `None` - #: means any whitespace. For all parameters the general rule is that - #: whitespace splits them up. The exception are paths and files which - #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on - #: Windows). - envvar_list_splitter: t.ClassVar[str | None] = None - - def to_info_dict(self) -> ParamTypeInfoDict: - """Gather information that could be useful for a tool generating - user-facing documentation. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - .. versionadded:: 8.0 - """ - # The class name without the "ParamType" suffix. - param_type = type(self).__name__.partition("ParamType")[0] - param_type = param_type.partition("ParameterType")[0] - - # Custom subclasses might not remember to set a name. - if hasattr(self, "name"): - name = self.name - else: - name = param_type - - return {"param_type": param_type, "name": name} - - def __call__( - self, - value: t.Any, - param: Parameter | None = None, - ctx: Context | None = None, - ) -> _ValueT_co | None: - if value is not None: - return self.convert(value, param, ctx) - return None - - def get_metavar(self, param: Parameter, ctx: Context) -> str | None: - """Returns the metavar default for this param if it provides one.""" - - def get_missing_message(self, param: Parameter, ctx: Context | None) -> str | None: - """Optionally might return extra information about a missing - parameter. - - .. versionadded:: 2.0 - """ - - def convert( - self, value: t.Any, param: Parameter | None, ctx: Context | None - ) -> _ValueT_co: - """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``. - """ - # The default returns the value as-is so subclasses that only customize - # metadata are not forced to redeclare ``convert``. - return t.cast("_ValueT_co", value) - - def split_envvar_value(self, rv: str) -> cabc.Sequence[str]: - """Given a value from an environment variable this splits it up - into small chunks depending on the defined envvar list splitter. - - If the splitter is set to `None`, which means that whitespace splits, - then leading and trailing whitespace is ignored. Otherwise, leading - and trailing splitters usually lead to empty items being included. - """ - return (rv or "").split(self.envvar_list_splitter) - - def fail( - self, - message: str, - param: Parameter | None = None, - ctx: Context | None = None, - ) -> t.NoReturn: - """Helper method to fail with an invalid value message.""" - raise BadParameter(message, ctx=ctx, param=param) - - def shell_complete( - self, ctx: Context, param: Parameter, incomplete: str - ) -> list[CompletionItem]: - """Return a list of - :class:`~click.shell_completion.CompletionItem` objects for the - incomplete value. Most types do not provide completions, but - some do, and this allows custom types to provide custom - completions as well. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - return [] - - -class CompositeParamType(ParamType[_ValueT_co]): - is_composite: t.ClassVar[bool] = True - - @property - @abc.abstractmethod - def arity(self) -> int: ... # type: ignore[override] - - -if t.TYPE_CHECKING: - # on Python 3.10 this will raise a TypeError - - class FuncParamTypeInfoDict( - ParamTypeInfoDict, - t.Generic[_ValueT_contra, _ValueT_co], - ): - func: t.Callable[[_ValueT_contra], _ValueT_co] -else: - - class FuncParamTypeInfoDict(ParamTypeInfoDict): - func: t.Callable[[t.Any], t.Any] - - -class FuncParamType(ParamType[_ValueT_co], t.Generic[_ValueT_contra, _ValueT_co]): - name: str - func: t.Callable[[_ValueT_contra], _ValueT_co] - - def __init__(self, func: t.Callable[[_ValueT_contra], _ValueT_co]) -> None: - self.name = func.__name__ - self.func = func - - def to_info_dict(self) -> FuncParamTypeInfoDict[_ValueT_contra, _ValueT_co]: - return {"func": self.func, **super().to_info_dict()} - - def convert( - self, value: _ValueT_contra, param: Parameter | None, ctx: Context | None - ) -> _ValueT_co: - try: - return self.func(value) - except ValueError as exc: - message = str(exc) - - if not message: - try: - message = str(value) - except UnicodeError: - message = t.cast("bytes", value).decode("utf-8", "replace") - - self.fail(message, param, ctx) - - -class UnprocessedParamType(ParamType[t.Any]): - name = "text" - - def convert( - self, value: _ValueT, param: Parameter | None, ctx: Context | None - ) -> _ValueT: - return value - - def __repr__(self) -> str: - return "UNPROCESSED" - - -class StringParamType(ParamType[str]): - name = "text" - - def convert( - self, value: t.Any, param: Parameter | None, ctx: Context | None - ) -> str: - if isinstance(value, bytes): - enc = _get_argv_encoding() - try: - return value.decode(enc) - except UnicodeError: - fs_enc = sys.getfilesystemencoding() - if fs_enc != enc: - try: - return value.decode(fs_enc) - except UnicodeError: - return value.decode("utf-8", "replace") - else: - return value.decode("utf-8", "replace") - return str(value) - - def __repr__(self) -> str: - return "STRING" - - -if t.TYPE_CHECKING: - # on Python 3.10 this will raise a TypeError - - class ChoiceInfoDict(ParamTypeInfoDict, t.Generic[_ValueT_co]): - choices: tuple[_ValueT_co, ...] - case_sensitive: bool -else: - - class ChoiceInfoDict(ParamTypeInfoDict): - choices: tuple[t.Any, ...] - case_sensitive: bool - - -class Choice(ParamType[_ValueT_co], t.Generic[_ValueT_co]): - """The choice type allows a value to be checked against a fixed set - of supported values. - - You may pass any iterable value which will be converted to a tuple - and thus will only be iterated once. - - The resulting value will always be one of the originally passed choices. - See :meth:`normalize_choice` for more info on the mapping of strings - to choices. See :ref:`choice-opts` for an example. - - :param case_sensitive: Set to false to make choices case - insensitive. Defaults to true. - - .. versionchanged:: 8.4.0 - Now generic in the choice value type. Parameterize with the type of - the choice values (``Choice[HashType]`` for an enum, ``Choice[str]`` - for plain strings) to enable type-checked consumers. - - .. versionchanged:: 8.2.0 - Non-``str`` ``choices`` are now supported. It can additionally be any - iterable. Before you were not recommended to pass anything but a list or - tuple. - - .. versionadded:: 8.2.0 - Choice normalization can be overridden via :meth:`normalize_choice`. - """ - - name: str = "choice" - - choices: tuple[_ValueT_co, ...] - case_sensitive: bool - - def __init__( - self, choices: cabc.Iterable[_ValueT_co], case_sensitive: bool = True - ) -> None: - self.choices = tuple(choices) - self.case_sensitive = case_sensitive - - def to_info_dict(self) -> ChoiceInfoDict[_ValueT_co]: - return { - "choices": self.choices, - "case_sensitive": self.case_sensitive, - **super().to_info_dict(), - } - - def _normalized_mapping( - self, ctx: Context | None = None - ) -> cabc.Mapping[_ValueT_co, str]: - """ - Returns mapping where keys are the original choices and the values are - the normalized values that are accepted via the command line. - - This is a simple wrapper around :meth:`normalize_choice`, use that - instead which is supported. - """ - return { - choice: self.normalize_choice( - choice=choice, - ctx=ctx, - ) - for choice in self.choices - } - - def normalize_choice(self, choice: object, ctx: Context | None) -> str: - """ - Normalize a choice value, used to map a passed string to a choice. - Each choice must have a unique normalized value. - - By default uses :meth:`Context.token_normalize_func` and if not case - sensitive, convert it to a casefolded value. - - .. versionadded:: 8.2.0 - """ - normed_value = choice.name if isinstance(choice, enum.Enum) else str(choice) - - if ctx is not None and ctx.token_normalize_func is not None: - normed_value = ctx.token_normalize_func(normed_value) - - if not self.case_sensitive: - normed_value = normed_value.casefold() - - return normed_value - - def get_metavar(self, param: Parameter, ctx: Context) -> str | None: - if param.param_type_name == "option" and not param.show_choices: # type: ignore[attr-defined] - choice_metavars = [ - convert_type(type(choice)).name.upper() for choice in self.choices - ] - choices_str = "|".join([*dict.fromkeys(choice_metavars)]) - else: - choices_str = "|".join( - [str(i) for i in self._normalized_mapping(ctx=ctx).values()] - ) - - # Use curly braces to indicate a required argument. - if param.required and param.param_type_name == "argument": - return f"{{{choices_str}}}" - - # Use square braces to indicate an option or optional argument. - return f"[{choices_str}]" - - def get_missing_message(self, param: Parameter, ctx: Context | None) -> str: - """ - Message shown when no choice is passed. - - .. versionchanged:: 8.2.0 Added ``ctx`` argument. - """ - return _("Choose from:\n\t{choices}").format( - choices=",\n\t".join(self._normalized_mapping(ctx=ctx).values()) - ) - - def convert( - self, value: t.Any, param: Parameter | None, ctx: Context | None - ) -> _ValueT_co: - """ - For a given value from the parser, normalize it and find its - matching normalized value in the list of choices. Then return the - matched "original" choice. - """ - normed_value = self.normalize_choice(choice=value, ctx=ctx) - normalized_mapping = self._normalized_mapping(ctx=ctx) - - try: - return next( - original - for original, normalized in normalized_mapping.items() - if normalized == normed_value - ) - except StopIteration: - self.fail( - self.get_invalid_choice_message(value=value, ctx=ctx), - param=param, - ctx=ctx, - ) - - def get_invalid_choice_message(self, value: t.Any, ctx: Context | None) -> str: - """Get the error message when the given choice is invalid. - - :param value: The invalid value. - - .. versionadded:: 8.2 - """ - choices_str = ", ".join(map(repr, self._normalized_mapping(ctx=ctx).values())) - return ngettext( - "{value!r} is not {choice}.", - "{value!r} is not one of {choices}.", - len(self.choices), - ).format(value=value, choice=choices_str, choices=choices_str) - - def __repr__(self) -> str: - return _("Choice({choices})").format(choices=list(self.choices)) - - def shell_complete( - self, ctx: Context, param: Parameter, incomplete: str - ) -> list[CompletionItem]: - """Complete choices that start with the incomplete value. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - str_choices = [self.normalize_choice(choice, ctx) for choice in 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 DateTimeInfoDict(ParamTypeInfoDict): - formats: cabc.Sequence[str] - - -class DateTime(ParamType[datetime]): - """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" - - formats: cabc.Sequence[str] - - def __init__(self, formats: cabc.Sequence[str] | None = 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) -> DateTimeInfoDict: - return {"formats": self.formats, **super().to_info_dict()} - - def get_metavar(self, param: Parameter, ctx: Context) -> str: - return f"[{'|'.join(self.formats)}]" - - def _try_to_convert_date(self, value: t.Any, format: str) -> datetime | None: - try: - return datetime.strptime(value, format) - except ValueError: - return None - - def convert( - self, value: t.Any, param: Parameter | None, ctx: Context | None - ) -> datetime: - 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[_ValueT_co], t.Generic[_ValueT_contra, _ValueT_co] -): - _number_class: t.Callable[[_ValueT_contra], _ValueT_co] - - def convert( - self, value: _ValueT_contra, param: Parameter | None, ctx: Context | None - ) -> _ValueT_co: - 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, - ) - - -if t.TYPE_CHECKING: - # on Python 3.10 this will raise a TypeError - - class NumberRangeInfoDict(ParamTypeInfoDict, t.Generic[_FloatValueT_co]): - min: _FloatValueT_co | None - max: _FloatValueT_co | None - min_open: bool - max_open: bool - clamp: bool -else: - - class NumberRangeInfoDict(ParamTypeInfoDict): - min: t.Any | None - max: t.Any | None - min_open: bool - max_open: bool - clamp: bool - - -class _NumberRangeBase( - _NumberParamTypeBase[_ValueT_contra, _FloatValueT_co], - t.Generic[_ValueT_contra, _FloatValueT_co], -): - min: _FloatValueT_co | None - max: _FloatValueT_co | None - min_open: bool - max_open: bool - clamp: bool - - def __init__( - self, - min: _FloatValueT_co | None = None, - max: _FloatValueT_co | None = None, - min_open: bool = False, - max_open: bool = False, - clamp: bool = False, - ) -> None: - self.min = min - self.max = max - self.min_open = min_open - self.max_open = max_open - self.clamp = clamp - - def to_info_dict(self) -> NumberRangeInfoDict[_FloatValueT_co]: - return { - "min": self.min, - "max": self.max, - "min_open": self.min_open, - "max_open": self.max_open, - "clamp": self.clamp, - **super().to_info_dict(), - } - - def convert( - self, value: _ValueT_contra, param: Parameter | None, ctx: Context | None - ) -> _FloatValueT_co: - import operator - - rv = super().convert(value, param, ctx) - min = self.min - max = self.max - lt_min: bool = min is not None and ( - operator.le if self.min_open else operator.lt - )(rv, min) - gt_max: bool = max is not None and ( - operator.ge if self.max_open else operator.gt - )(rv, max) - - if self.clamp: - if min is not None and lt_min: - return self._clamp(min, 1, self.min_open) - - if max is not None and gt_max: - return self._clamp(max, -1, self.max_open) - - 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 - - @abc.abstractmethod - def _clamp( - # Covariant type variables cannot be used in input positions, so we use a - # separate method-scoped type variable instead. - self: _NumberRangeBase[t.Any, _FloatValueT], - bound: _FloatValueT, - dir: t.Literal[1, -1], - open: bool, - ) -> _FloatValueT: - """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. - """ - ... - - 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[t.SupportsInt | t.SupportsIndex, int]): - name = "integer" - _number_class = int - - def __repr__(self) -> str: - return "INT" - - -class IntRange(_NumberRangeBase[int, int], 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(self, bound: int, dir: t.Literal[1, -1], open: bool) -> int: - if not open: - return bound - - return bound + dir - - -class FloatParamType(_NumberParamTypeBase[t.SupportsFloat | t.SupportsIndex, float]): - name = "float" - _number_class = float - - def __repr__(self) -> str: - return "FLOAT" - - -class FloatRange(_NumberRangeBase[float, float], FloatParamType): - """Restrict a :data:`click.FLOAT` value to a range of accepted - values. See :ref:`ranges`. - - If ``min`` or ``max`` are not passed, any value is accepted in that - direction. If ``min_open`` or ``max_open`` are enabled, the - corresponding boundary is not included in the range. - - If ``clamp`` is enabled, a value outside the range is clamped to the - boundary instead of failing. This is not supported if either - boundary is marked ``open``. - - .. versionchanged:: 8.0 - Added the ``min_open`` and ``max_open`` parameters. - """ - - name = "float range" - - def __init__( - self, - min: float | None = None, - max: float | None = None, - min_open: bool = False, - max_open: bool = False, - clamp: bool = False, - ) -> None: - super().__init__( - min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp - ) - - if (min_open or max_open) and clamp: - raise TypeError("Clamping is not supported for open bounds.") - - def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float: - if not open: - return bound - - # Could use math.nextafter here, but clamping an - # open float range doesn't seem to be particularly useful. It's - # left up to the user to write a callback to do it if needed. - raise RuntimeError("Clamping is not supported for open bounds.") - - -class BoolParamType(ParamType[bool]): - name = "boolean" - - bool_states: dict[str, bool] = { - "1": True, - "0": False, - "yes": True, - "no": False, - "true": True, - "false": False, - "on": True, - "off": False, - "t": True, - "f": False, - "y": True, - "n": False, - # Absence of value is considered False. - "": False, - } - """A mapping of string values to boolean states. - - Mapping is inspired by :py:attr:`configparser.ConfigParser.BOOLEAN_STATES` - and extends it. - - .. caution:: - String values are lower-cased, as the ``str_to_bool`` comparison function - below is case-insensitive. - - .. warning:: - The mapping is not exhaustive, and does not cover all possible boolean strings - representations. It will remains as it is to avoid endless bikeshedding. - - Future work my be considered to make this mapping user-configurable from public - API. - """ - - @staticmethod - def str_to_bool(value: str | bool) -> bool | None: - """Convert a string to a boolean value. - - If the value is already a boolean, it is returned as-is. If the value is a - string, it is stripped of whitespaces and lower-cased, then checked against - the known boolean states pre-defined in the `BoolParamType.bool_states` mapping - above. - - Returns `None` if the value does not match any known boolean state. - """ - if isinstance(value, bool): - return value - return BoolParamType.bool_states.get(value.strip().lower()) - - def convert( - self, value: t.Any, param: Parameter | None, ctx: Context | None - ) -> bool: - normalized = self.str_to_bool(value) - if normalized is None: - self.fail( - _( - "{value!r} is not a valid boolean. Recognized values: {states}" - ).format(value=value, states=", ".join(sorted(self.bool_states))), - param, - ctx, - ) - return normalized - - def __repr__(self) -> str: - return "BOOL" - - -class UUIDParameterType(ParamType[uuid.UUID]): - name = "uuid" - - def convert( - self, value: uuid.UUID | str, param: Parameter | None, ctx: Context | None - ) -> uuid.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 FileInfoDict(ParamTypeInfoDict): - mode: str - encoding: str | None - - -class File(ParamType[t.IO[t.Any]]): - """Declares a parameter to be a file for reading or writing. The file - is automatically closed once the context tears down (after the command - finished working). - - Files can be opened for reading or writing. The special value ``-`` - indicates stdin or stdout depending on the mode. - - By default, the file is opened for reading text data, but it can also be - opened in binary mode or for writing. The encoding parameter can be used - to force a specific encoding. - - The `lazy` flag controls if the file should be opened immediately or upon - first IO. The default is to be non-lazy for standard input and output - streams as well as files opened for reading, `lazy` otherwise. When opening a - file lazily for reading, it is still opened temporarily for validation, but - will not be held open until first IO. lazy is mainly useful when opening - for writing to avoid creating the file until it is needed. - - Files can also be opened atomically in which case all writes go into a - separate file in the same folder and upon completion the file will - be moved over to the original location. This is useful if a file - regularly read by other users is modified. - - See :ref:`file-args` for more information. - - .. versionchanged:: 2.0 - Added the ``atomic`` parameter. - """ - - name = "filename" - envvar_list_splitter: t.ClassVar[str] = os.path.pathsep - - mode: str - encoding: str | None - errors: str | None - lazy: bool | None - atomic: bool - - def __init__( - self, - mode: str = "r", - encoding: str | None = None, - errors: str | None = "strict", - lazy: bool | None = None, - atomic: bool = False, - ) -> None: - self.mode = mode - self.encoding = encoding - self.errors = errors - self.lazy = lazy - self.atomic = atomic - - def to_info_dict(self) -> FileInfoDict: - return { - "mode": self.mode, - "encoding": self.encoding, - **super().to_info_dict(), - } - - def resolve_lazy_flag(self, value: str | os.PathLike[str]) -> bool: - if self.lazy is not None: - return self.lazy - if os.fspath(value) == "-": - return False - elif "w" in self.mode: - return True - return False - - def convert( - self, - value: str | os.PathLike[str] | t.IO[t.Any], - param: Parameter | None, - ctx: Context | None, - ) -> t.IO[t.Any]: - if _is_file_like(value): - return value - - try: - lazy = self.resolve_lazy_flag(value) - - if lazy: - lf = LazyFile( - value, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - - if ctx is not None: - ctx.call_on_close(lf.close_intelligently) - - return t.cast("t.IO[t.Any]", lf) - - f, should_close = open_stream( - value, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - - # If a context is provided, we automatically close the file - # at the end of the context execution (or flush out). If a - # context does not exist, it's the caller's responsibility to - # properly close the file. This for instance happens when the - # type is used with prompts. - if ctx is not None: - if should_close: - ctx.call_on_close(safecall(f.close)) - else: - ctx.call_on_close(safecall(f.flush)) - - return f - except OSError as e: - self.fail( - f"'{format_filename(value)}': {e.strerror}", - param, - ctx, - ) - - def shell_complete( - self, ctx: Context, param: Parameter, incomplete: str - ) -> list[CompletionItem]: - """Return a special completion marker that tells the completion - system to use the shell to provide file path completions. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - return [CompletionItem(incomplete, type="file")] - - -def _is_file_like(value: t.Any) -> te.TypeIs[t.IO[t.Any]]: - return hasattr(value, "read") or hasattr(value, "write") - - -class PathInfoDict(ParamTypeInfoDict): - exists: bool - file_okay: bool - dir_okay: bool - writable: bool - readable: bool - allow_dash: bool - - -class Path(ParamType[str | bytes | os.PathLike[str]]): - """The ``Path`` type is similar to the :class:`File` type, but - returns the filename instead of an open file. Various checks can be - enabled to validate the type of file and permissions. - - :param exists: The file or directory needs to exist for the value to - be valid. If this is not set to ``True``, and the file does not - exist, then all further checks are silently skipped. - :param file_okay: Allow a file as a value. - :param dir_okay: Allow a directory as a value. - :param readable: if true, a readable check is performed. - :param writable: if true, a writable check is performed. - :param executable: if true, an executable check is performed. - :param resolve_path: Make the value absolute and resolve any - symlinks. A ``~`` is not expanded, as this is supposed to be - done by the shell only. - :param allow_dash: Allow a single dash as a value, which indicates - a standard stream (but does not open it). Use - :func:`~click.open_file` to handle opening this value. - :param path_type: Convert the incoming path value to this type. If - ``None``, keep Python's default, which is ``str``. Useful to - convert to :class:`pathlib.Path`. - - .. versionchanged:: 8.1 - Added the ``executable`` parameter. - - .. versionchanged:: 8.0 - Allow passing ``path_type=pathlib.Path``. - - .. versionchanged:: 6.0 - Added the ``allow_dash`` parameter. - """ - - envvar_list_splitter: t.ClassVar[str] = os.path.pathsep - - exists: bool - file_okay: bool - dir_okay: bool - readable: bool - writable: bool - executable: bool - resolve_path: bool - allow_dash: bool - name: str - - def __init__( - self, - exists: bool = False, - file_okay: bool = True, - dir_okay: bool = True, - writable: bool = False, - readable: bool = True, - resolve_path: bool = False, - allow_dash: bool = False, - path_type: type | None = None, - executable: bool = False, - ) -> None: - 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: type | None = 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) -> PathInfoDict: - return { - "exists": self.exists, - "file_okay": self.file_okay, - "dir_okay": self.dir_okay, - "writable": self.writable, - "readable": self.readable, - "allow_dash": self.allow_dash, - **super().to_info_dict(), - } - - def coerce_path_result( - self, value: str | os.PathLike[str] - ) -> str | bytes | os.PathLike[str]: - if self.type is not None and not isinstance(value, self.type): - if self.type is str: - return os.fsdecode(value) - elif self.type is bytes: - return os.fsencode(value) - else: - return t.cast("os.PathLike[str]", self.type(value)) - - return value - - def convert( - self, - value: str | os.PathLike[str], - param: Parameter | None, - ctx: Context | None, - ) -> str | bytes | os.PathLike[str]: - rv = value - - is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") - - if not is_dash: - if self.resolve_path: - rv = os.path.realpath(rv) - - try: - st = os.stat(rv) - except OSError: - if not self.exists: - return self.coerce_path_result(rv) - self.fail( - _("{name} {filename!r} does not exist.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if not self.file_okay and stat.S_ISREG(st.st_mode): - self.fail( - _("{name} {filename!r} is a file.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - if not self.dir_okay and stat.S_ISDIR(st.st_mode): - self.fail( - _("{name} {filename!r} is a directory.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.readable and not os.access(rv, os.R_OK): - self.fail( - _("{name} {filename!r} is not readable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.writable and not os.access(rv, os.W_OK): - self.fail( - _("{name} {filename!r} is not writable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.executable and not os.access(value, os.X_OK): - self.fail( - _("{name} {filename!r} is not executable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - return self.coerce_path_result(rv) - - def shell_complete( - self, ctx: Context, param: Parameter, incomplete: str - ) -> list[CompletionItem]: - """Return a special completion marker that tells the completion - system to use the shell to provide path completions for only - directories or any paths. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - type = "dir" if self.dir_okay and not self.file_okay else "file" - return [CompletionItem(incomplete, type=type)] - - -class TupleInfoDict(ParamTypeInfoDict): - types: cabc.Sequence[ParamTypeInfoDict] - - -class Tuple(CompositeParamType[tuple[t.Any, ...]]): - """The default behavior of Click is to apply a type on a value directly. - This works well in most cases, except for when `nargs` is set to a fixed - count and different types should be used for different items. In this - case the :class:`Tuple` type can be used. This type can only be used - if `nargs` is set to a fixed number. - - For more information see :ref:`tuple-type`. - - This can be selected by using a Python tuple literal as a type. - - :param types: a list of types that should be used for the tuple items. - """ - - def __init__(self, types: cabc.Sequence[type[t.Any] | ParamType[t.Any]]) -> None: - self.types: cabc.Sequence[ParamType[t.Any]] = [convert_type(ty) for ty in types] - - def to_info_dict(self) -> TupleInfoDict: - return { - "types": [ty.to_info_dict() for ty in self.types], - **super().to_info_dict(), - } - - @property - def name(self) -> str: # type: ignore[override] - return f"<{' '.join(ty.name for ty in self.types)}>" - - @property - def arity(self) -> int: # type: ignore[override] - return len(self.types) - - def convert( - self, value: t.Any, param: Parameter | None, ctx: Context | None - ) -> tuple[t.Any, ...]: - len_type = len(self.types) - len_value = len(value) - - if len_value != len_type: - self.fail( - ngettext( - "{len_type} values are required, but {len_value} was given.", - "{len_type} values are required, but {len_value} were given.", - len_value, - ).format(len_type=len_type, len_value=len_value), - param=param, - ctx=ctx, - ) - - return tuple( - ty(x, param, ctx) for ty, x in zip(self.types, value, strict=False) - ) - - -def _guess_type( - ty: type[t.Any] | ParamType[t.Any] | None, - default: t.Any | None, -) -> type[t.Any] | tuple[type[t.Any], ...] | ParamType[t.Any] | None: - """Infer a type from *ty* or *default*. - - Returns *ty* unchanged when it is not ``None``. Otherwise inspects - *default* to produce a ``type``, a ``tuple`` of types (for tuple - defaults), or ``None``. - """ - if ty is not None: - return ty - - if default is None: - return None - - if not isinstance(default, (tuple, list)): - return type(default) - - # If the default is empty, return None so convert_type falls - # through to STRING. - if not default: - return None - - item = default[0] - - # A sequence of iterables needs to detect the inner types. - # Can't call convert_type recursively because that would - # incorrectly unwind the tuple to a single type. - if isinstance(item, (tuple, list)): - return tuple(map(type, item)) - - return type(item) - - -@t.overload -def convert_type(ty: None, default: None = None) -> StringParamType: ... -@t.overload -def convert_type( - ty: type | ParamType[t.Any], default: t.Any | None = None -) -> ParamType[t.Any]: ... -@t.overload -def convert_type( - ty: t.Any | None, default: t.Any | None = None -) -> ParamType[t.Any]: ... -def convert_type( - ty: t.Any | None = None, default: t.Any | None = None -) -> ParamType[t.Any]: - """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 = _guess_type(ty, default) - is_guessed = guessed is not ty - - if isinstance(guessed, tuple): - return Tuple(guessed) - - if isinstance(guessed, ParamType): - return guessed - - if guessed is str or guessed is None: - return STRING - - if guessed is int: - return INT - - if guessed is float: - return FLOAT - - if guessed is bool: - return BOOL - - if is_guessed: - return STRING - - if __debug__: - try: - if issubclass(guessed, ParamType): - raise AssertionError( - f"Attempted to use an uninstantiated parameter type ({guessed})." - ) - except TypeError: - # guessed is an instance (correct), so issubclass fails. - pass - - return FuncParamType(guessed) - - -#: 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 provided. -#: -#: .. versionadded:: 4.0 -UNPROCESSED: t.Final[UnprocessedParamType] = UnprocessedParamType() - -#: A unicode string parameter type which is the implicit default. This -#: can also be selected by using ``str`` as type. -STRING: t.Final[StringParamType] = StringParamType() - -#: An integer parameter. This can also be selected by using ``int`` as -#: type. -INT: t.Final[IntParamType] = IntParamType() - -#: A floating point value parameter. This can also be selected by using -#: ``float`` as type. -FLOAT: t.Final[FloatParamType] = FloatParamType() - -#: A boolean parameter. This is the default for boolean flags. This can -#: also be selected by using ``bool`` as a type. -BOOL: t.Final[BoolParamType] = BoolParamType() - -#: A UUID parameter. -UUID: t.Final[UUIDParameterType] = UUIDParameterType() - - -class OptionHelpExtra(t.TypedDict, total=False): - envvars: tuple[str, ...] - default: str - range: str - required: str diff --git a/.venv/lib/python3.12/site-packages/click/utils.py b/.venv/lib/python3.12/site-packages/click/utils.py deleted file mode 100644 index c0cb22d6..00000000 --- a/.venv/lib/python3.12/site-packages/click/utils.py +++ /dev/null @@ -1,653 +0,0 @@ -from __future__ import annotations - -import collections.abc as cabc -import os -import re -import sys -import typing as t -from functools import update_wrapper -from gettext import gettext as _ -from types import ModuleType -from types import TracebackType - -from ._compat import _default_text_stderr -from ._compat import _default_text_stdout -from ._compat import _find_binary_writer -from ._compat import auto_wrap_for_ansi -from ._compat import binary_streams -from ._compat import open_stream -from ._compat import should_strip_ansi -from ._compat import strip_ansi -from ._compat import text_streams -from ._compat import WIN -from .globals import resolve_color_default - -if t.TYPE_CHECKING: - import typing_extensions as te - - P = te.ParamSpec("P") - -R = t.TypeVar("R") - - -def _posixify(name: str) -> str: - return "-".join(name.split()).lower() - - -def safecall(func: t.Callable[P, R]) -> t.Callable[P, R | None]: - """Wraps a function so that it swallows exceptions.""" - - def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | None: - try: - return func(*args, **kwargs) - except Exception: - pass - return None - - return update_wrapper(wrapper, func) - - -def make_str(value: t.Any) -> str: - """Converts a value into a valid string.""" - if isinstance(value, bytes): - try: - return value.decode(sys.getfilesystemencoding()) - except UnicodeError: - return value.decode("utf-8", "replace") - return str(value) - - -def make_default_short_help(help: str, max_length: int = 45) -> str: - """Returns a condensed version of help string. - - :meta private: - """ - # 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. - """ - - name: str - mode: str - encoding: str | None - errors: str | None - atomic: bool - _f: t.IO[t.Any] | None - should_close: bool - - def __init__( - self, - filename: str | os.PathLike[str], - mode: str = "r", - encoding: str | None = None, - errors: str | None = "strict", - atomic: bool = False, - ) -> None: - self.name = os.fspath(filename) - self.mode = mode - self.encoding = encoding - self.errors = errors - self.atomic = atomic - - if self.name == "-": - self._f, self.should_close = open_stream(filename, mode, encoding, errors) - else: - if "r" in mode: - # Open and close the file in case we're opening it for - # reading so that we can catch at least some errors in - # some cases early. - open(filename, mode).close() - self._f = None - self.should_close = True - - def __getattr__(self, name: str) -> t.Any: - return getattr(self.open(), name) - - def __repr__(self) -> str: - if self._f is not None: - return repr(self._f) - return f"" - - def open(self) -> t.IO[t.Any]: - """Opens the file if it's not yet open. This call might fail with - a :exc:`FileError`. Not handling this error will produce an error - that Click shows. - """ - if self._f is not None: - return self._f - try: - rv, self.should_close = open_stream( - self.name, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - except OSError as e: - from .exceptions import FileError - - raise FileError(self.name, hint=e.strerror) from e - self._f = rv - return rv - - def close(self) -> None: - """Closes the underlying file, no matter what.""" - if self._f is not None: - self._f.close() - - def close_intelligently(self) -> None: - """This function only closes the file if it was opened by the lazy - file wrapper. For instance this will never close stdin. - """ - if self.should_close: - self.close() - - def __enter__(self) -> LazyFile: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - tb: TracebackType | None, - ) -> None: - self.close_intelligently() - - def __iter__(self) -> cabc.Iterator[t.AnyStr]: - self.open() - return iter(self._f) # type: ignore - - -class KeepOpenFile: - """Proxy a file object but keep it open across a ``with`` block. - - Wraps a borrowed file (such as ``sys.stdin`` or ``sys.stdout``) so that - leaving a ``with`` block does not close it, as used by :func:`open_file` - for the ``-`` filename. The caller stays responsible for the file: an - explicit :meth:`close` still passes through to the wrapped object. - - Dunder methods are proxied explicitly: implicit special-method lookups - bypass :meth:`__getattr__`, because Python resolves them on the type rather - than the instance. - """ - - _file: t.IO[t.Any] - - def __init__(self, file: t.IO[t.Any]) -> 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: type[BaseException] | None, - exc_value: BaseException | None, - tb: TracebackType | None, - ) -> None: - pass - - def __repr__(self) -> str: - return repr(self._file) - - def __iter__(self) -> cabc.Iterator[t.AnyStr]: - return iter(self._file) - - -def echo( - message: object = None, - file: t.IO[t.Any] | None = None, - nl: bool = True, - err: bool = False, - color: bool | None = None, -) -> None: - """Print a message and newline to stdout or a file. This should be - used instead of :func:`print` because it provides better support - for different data, files, and environments. - - Compared to :func:`print`, this does the following: - - - Ensures that the output encoding is not misconfigured on Linux. - - Supports Unicode in the Windows console. - - Supports writing to binary outputs, and supports writing bytes - to text outputs. - - Supports colors and styles on Windows. - - Removes ANSI color and style codes if the output does not look - like an interactive terminal. - - Always flushes the output. - - :param message: The string or bytes to output. Other objects are - converted to strings. - :param file: The file to write to. Defaults to ``stdout``. - :param err: Write to ``stderr`` instead of ``stdout``. - :param nl: Print a newline after the message. Enabled by default. - :param color: Force showing or hiding colors and other styles. By - default Click will remove color if the output does not look like - an interactive terminal. - - .. versionchanged:: 6.0 - Support Unicode output on the Windows console. Click does not - modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` - will still not support Unicode. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - - .. versionadded:: 3.0 - Added the ``err`` parameter. - - .. versionchanged:: 2.0 - Support colors on Windows if colorama is installed. - """ - if file is None: - if err: - file = _default_text_stderr() - else: - file = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if file is None: - return - - match message: - case str() | bytes() | bytearray(): - out = message - case None: - out = "" - case _: - out = str(message) - - if nl: - if isinstance(out, str): - out += "\n" - else: - out += b"\n" - - if not out: - file.flush() - return - - # If there is a message and the value looks like bytes, we manually - # need to find the binary stream and write the message in there. - # This is done separately so that most stream types will work as you - # would expect. Eg: you can write to StringIO for other cases. - if isinstance(out, (bytes, bytearray)): - binary_file = _find_binary_writer(file) - if binary_file is not None: - file.flush() - binary_file.write(out) - binary_file.flush() - return - - # ANSI style code support. For no message or bytes, nothing happens. - # When outputting to a file instead of a terminal, strip codes. - else: - color = resolve_color_default(color) - - if should_strip_ansi(file, color): - out = strip_ansi(out) - elif WIN: - if auto_wrap_for_ansi is not None: - file = auto_wrap_for_ansi(file, color) # type: ignore - elif not color: - out = strip_ansi(out) - - file.write(out) # type: ignore - file.flush() - - -def get_binary_stream(name: t.Literal["stdin", "stdout", "stderr"]) -> t.BinaryIO: - """Returns a system stream for byte processing. - - :param name: the name of the stream to open. Valid names are ``'stdin'``, - ``'stdout'`` and ``'stderr'`` - """ - opener = binary_streams.get(name) - if opener is None: - raise TypeError(_("Unknown standard stream '{name}'").format(name=name)) - return opener() - - -def get_text_stream( - name: t.Literal["stdin", "stdout", "stderr"], - encoding: str | None = None, - errors: str | None = "strict", -) -> t.TextIO: - """Returns a system stream for text processing. This usually returns - a wrapped stream around a binary stream returned from - :func:`get_binary_stream` but it also can take shortcuts for already - correctly configured streams. - - :param name: the name of the stream to open. Valid names are ``'stdin'``, - ``'stdout'`` and ``'stderr'`` - :param encoding: overrides the detected default encoding. - :param errors: overrides the default error mode. - """ - opener = text_streams.get(name) - if opener is None: - raise TypeError(_("Unknown standard stream '{name}'").format(name=name)) - return opener(encoding, errors) - - -def open_file( - filename: str | os.PathLike[str], - mode: str = "r", - encoding: str | None = None, - errors: str | None = "strict", - lazy: bool = False, - atomic: bool = False, -) -> t.IO[t.Any]: - """Open a file, with extra behavior to handle ``'-'`` to indicate - a standard stream, lazy open on write, and atomic write. Similar to - the behavior of the :class:`~click.File` param type. - - If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is - wrapped so that using it in a context manager will not close it. - This makes it possible to use the function without accidentally - closing a standard stream: - - .. code-block:: python - - with open_file(filename) as f: - ... - - :param filename: The name or Path of the file to open, or ``'-'`` for - ``stdin``/``stdout``. - :param mode: The mode in which to open the file. - :param encoding: The encoding to decode or encode a file opened in - text mode. - :param errors: The error handling mode. - :param lazy: Wait to open the file until it is accessed. For read - mode, the file is temporarily opened to raise access errors - early, then closed until it is read again. - :param atomic: Write to a temporary file and replace the given file - on close. - - .. versionadded:: 3.0 - """ - if lazy: - return t.cast( - "t.IO[t.Any]", LazyFile(filename, mode, encoding, errors, atomic=atomic) - ) - - f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) - - if not should_close: - f = t.cast("t.IO[t.Any]", KeepOpenFile(f)) - - return f - - -def format_filename( - filename: str | bytes | os.PathLike[str] | os.PathLike[bytes], - shorten: bool = False, -) -> str: - """Format a filename as a string for display. Ensures the filename can be - displayed by replacing any invalid bytes or surrogate escapes in the name - with the replacement character ``�``. - - Invalid bytes or surrogate escapes will raise an error when written to a - stream with ``errors="strict"``. This will typically happen with ``stdout`` - when the locale is something like ``en_GB.UTF-8``. - - Many scenarios *are* safe to write surrogates though, due to PEP 538 and - PEP 540, including: - - - Writing to ``stderr``, which uses ``errors="backslashreplace"``. - - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens - stdout and stderr with ``errors="surrogateescape"``. - - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. - - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. - Python opens stdout and stderr with ``errors="surrogateescape"``. - - :param filename: formats a filename for UI display. This will also convert - the filename into unicode without failing. - :param shorten: this optionally shortens the filename to strip of the - path that leads up to it. - """ - if shorten: - filename = os.path.basename(filename) - else: - filename = os.fspath(filename) - - if isinstance(filename, bytes): - filename = filename.decode(sys.getfilesystemencoding(), "replace") - else: - filename = filename.encode("utf-8", "surrogateescape").decode( - "utf-8", "replace" - ) - - return filename - - -def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: - r"""Returns the config folder for the application. The default behavior - is to return whatever is most appropriate for the operating system. - - To give you an idea, for an app called ``"Foo Bar"``, something like - the following folders could be returned: - - Mac OS X: - ``~/Library/Application Support/Foo Bar`` - Mac OS X (POSIX): - ``~/.foo-bar`` - Unix: - ``~/.config/foo-bar`` - Unix (POSIX): - ``~/.foo-bar`` - Windows (roaming): - ``C:\Users\\AppData\Roaming\Foo Bar`` - Windows (not roaming): - ``C:\Users\\AppData\Local\Foo Bar`` - - .. versionadded:: 2.0 - - :param app_name: the application name. This should be properly capitalized - and can contain whitespace. - :param roaming: controls if the folder should be roaming or not on Windows. - Has no effect otherwise. - :param force_posix: if this is set to `True` then on any POSIX system the - folder will be stored in the home folder with a leading - dot instead of the XDG config home or darwin's - application support folder. - """ - if WIN: - key = "APPDATA" if roaming else "LOCALAPPDATA" - folder = os.environ.get(key) - if folder is None: - folder = os.path.expanduser("~") - return os.path.join(folder, app_name) - if force_posix: - return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) - if sys.platform == "darwin": - return os.path.join( - os.path.expanduser("~/Library/Application Support"), app_name - ) - return os.path.join( - os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), - _posixify(app_name), - ) - - -class PacifyFlushWrapper: - """This wrapper is used to catch and suppress BrokenPipeErrors resulting - from ``.flush()`` being called on broken pipe during the shutdown/final-GC - of the Python interpreter. Notably ``.flush()`` is always called on - ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any - other cleanup code, and the case where the underlying file is not a broken - pipe, all calls and attributes are proxied. - """ - - wrapped: t.IO[t.Any] - - def __init__(self, wrapped: t.IO[t.Any]) -> None: - self.wrapped = wrapped - - def flush(self) -> None: - try: - self.wrapped.flush() - except OSError as e: - import errno - - if e.errno != errno.EPIPE: - raise - - def __getattr__(self, attr: str) -> t.Any: - return getattr(self.wrapped, attr) - - -def _detect_program_name( - path: str | None = None, _main: ModuleType | None = None -) -> str: - """Determine the command used to run the program, for use in help - text. If a file or entry point was executed, the file name is - returned. If ``python -m`` was used to execute a module or package, - ``python -m name`` is returned. - - This doesn't try to be too precise, the goal is to give a concise - name for help text. Files are only shown as their name without the - path. ``python`` is only shown for modules, and the full path to - ``sys.executable`` is not shown. - - :param path: The Python file being executed. Python puts this in - ``sys.argv[0]``, which is used by default. - :param _main: The ``__main__`` module. This should only be passed - during internal testing. - - .. versionadded:: 8.0 - Based on command args detection in the Werkzeug reloader. - - :meta private: - """ - if _main is None: - _main = sys.modules["__main__"] - - if not path: - path = sys.argv[0] - - # The value of __package__ indicates how Python was called. It may - # not exist if a setuptools script is installed as an egg. It may be - # set incorrectly for entry points created with pip on Windows. - # It is set to "" inside a Shiv or PEX zipapp. - if getattr(_main, "__package__", None) in {None, ""} or ( - os.name == "nt" - and _main.__package__ == "" - and not os.path.exists(path) - and os.path.exists(f"{path}.exe") - ): - # Executed a file, like "python app.py". - return os.path.basename(path) - - # Executed a module, like "python -m example". - # Rewritten by Python from "-m script" to "/path/to/script.py". - # Need to look at main module to determine how it was executed. - py_module = t.cast(str, _main.__package__) - name = os.path.splitext(os.path.basename(path))[0] - - # A submodule like "example.cli". - if name != "__main__": - py_module = f"{py_module}.{name}" - - return f"python -m {py_module.lstrip('.')}" - - -def _expand_args( - args: cabc.Iterable[str], - *, - user: bool = True, - env: bool = True, - glob_recursive: bool = True, -) -> list[str]: - """Simulate Unix shell expansion with Python functions. - - See :func:`glob.glob`, :func:`os.path.expanduser`, and - :func:`os.path.expandvars`. - - This is intended for use on Windows, where the shell does not do any - expansion. It may not exactly match what a Unix shell would do. - - :param args: List of command line arguments to expand. - :param user: Expand user home directory. - :param env: Expand environment variables. - :param glob_recursive: ``**`` matches directories recursively. - - .. versionchanged:: 8.1 - Invalid glob patterns are treated as empty expansions rather - than raising an error. - - .. versionadded:: 8.0 - - :meta private: - """ - from glob import glob - - out = [] - - for arg in args: - if user: - arg = os.path.expanduser(arg) - - if env: - arg = os.path.expandvars(arg) - - try: - matches = glob(arg, recursive=glob_recursive) - except re.error: - matches = [] - - if not matches: - out.append(arg) - else: - out.extend(matches) - - return out diff --git a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/METADATA deleted file mode 100644 index 208769d4..00000000 --- a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/METADATA +++ /dev/null @@ -1,512 +0,0 @@ -Metadata-Version: 2.4 -Name: courlan -Version: 1.4.0 -Summary: Clean, filter and sample URLs to optimize data collection – includes spam, content type and language filters. -Author-email: Adrien Barbaresi -License-Expression: Apache-2.0 -Project-URL: Homepage, https://github.com/adbar/courlan -Project-URL: Blog, https://adrien.barbaresi.eu/blog/ -Project-URL: Tracker, https://github.com/adbar/courlan/issues -Keywords: cleaner,crawler,uri,url-parsing,url-manipulation,urls,validation,webcrawling -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Console -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: Education -Classifier: Intended Audience :: Information Technology -Classifier: Intended Audience :: Science/Research -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: Microsoft :: Windows -Classifier: Operating System :: POSIX :: Linux -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Topic :: Internet :: WWW/HTTP -Classifier: Topic :: Scientific/Engineering :: Information Analysis -Classifier: Topic :: Security -Classifier: Topic :: Text Processing :: Filters -Classifier: Topic :: Text Processing :: Linguistic -Classifier: Typing :: Typed -Requires-Python: >=3.10 -Description-Content-Type: text/markdown -License-File: LICENSE -Requires-Dist: babel>=2.16.0 -Requires-Dist: tld>=0.13 -Requires-Dist: urllib3<3,>=1.26 -Provides-Extra: dev -Requires-Dist: ruff==0.15.15; extra == "dev" -Requires-Dist: mypy==2.1.0; extra == "dev" -Requires-Dist: pytest==9.0.3; extra == "dev" -Requires-Dist: pytest-cov==7.1.0; extra == "dev" -Requires-Dist: pytest-httpserver==1.1.5; extra == "dev" -Dynamic: license-file - -# coURLan: Clean, filter, normalize, and sample URLs - - -[![Python package](https://img.shields.io/pypi/v/courlan.svg)](https://pypi.python.org/pypi/courlan) -[![Python versions](https://img.shields.io/pypi/pyversions/courlan.svg)](https://pypi.python.org/pypi/courlan) -[![Code Coverage](https://img.shields.io/codecov/c/github/adbar/courlan.svg)](https://codecov.io/gh/adbar/courlan) -[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) - - -## Why coURLan? - -> "It is important for the crawler to visit 'important' pages first, -> so that the fraction of the Web that is visited (and kept up to date) -> is more meaningful." (Cho et al. 1998) -> -> "Given that the bandwidth for conducting crawls is neither infinite -> nor free, it is becoming essential to crawl the Web in not only a -> scalable, but efficient way, if some reasonable measure of quality or -> freshness is to be maintained." (Edwards et al. 2001) - -This library provides an additional "brain" for web crawling, scraping -and document management. It facilitates web navigation through a set of -filters, enhancing the quality of resulting document collections: - -- Save bandwidth and processing time by steering clear of pages deemed - low-value -- Identify specific pages based on language or text content -- Pinpoint pages relevant for efficient link gathering - -Additional utilities needed include URL storage, filtering, and -deduplication. - -## Features - -Separate the wheat from the chaff and optimize document discovery and -retrieval: - - -- URL handling - - Validation - - Normalization - - Sampling -- Heuristics for link filtering - - Spam, trackers, and content-types - - Locales and internationalization - - Web crawling (frontier, scheduling) -- Data store specifically designed for URLs -- Usable with Python or on the command-line - - -**Let the coURLan fish up juicy bits for you!** - -Courlan bird - -Here is a [courlan](https://en.wiktionary.org/wiki/courlan) (source: -[Limpkin at Harn's Marsh by -Russ](https://commons.wikimedia.org/wiki/File:Limpkin,_harns_marsh_(33723700146).jpg), -CC BY 2.0). - - -## Installation - -This package requires Python 3.10 or higher and is tested on Linux, macOS -and Windows systems. - -Courlan is available on the package repository [PyPI](https://pypi.org/) -and can notably be installed with the Python package manager `pip`: - -``` bash -$ pip install courlan -$ pip install --upgrade courlan # to make sure you have the latest version -$ pip install git+https://github.com/adbar/courlan.git # latest available code (see build status above) -``` - -The last version to support Python 3.6 and 3.7 is `courlan==1.2.0`. -The last version to support Python 3.8 and 3.9 is `courlan==1.3.2`. - - -## Python - -Most filters revolve around the `strict` and `language` arguments. - -### check_url() - -All useful operations chained in `check_url(url)`: - -``` python ->>> from courlan import check_url - -# return url and domain name ->>> check_url('https://github.com/adbar/courlan') -('https://github.com/adbar/courlan', 'github.com') - -# filter out bogus domains ->>> check_url('http://666.0.0.1/') ->>> - -# tracker removal ->>> check_url('http://test.net/foo.html?utm_source=twitter#gclid=123') -('http://test.net/foo.html', 'test.net') - -# use strict for further trimming ->>> my_url = 'https://httpbin.org/redirect-to?url=http%3A%2F%2Fexample.org' ->>> check_url(my_url, strict=True) -('https://httpbin.org/redirect-to', 'httpbin.org') - -# check for redirects (HEAD request) ->>> url, domain_name = check_url(my_url, with_redirects=True) - -# include navigation pages instead of discarding them ->>> check_url('http://www.example.org/page/10/', with_nav=True) - -# remove trailing slash ->>> check_url('https://github.com/adbar/courlan/', trailing_slash=False) -``` - -Language-aware heuristics, notably internationalization in URLs, are -available in `lang_filter(url, language)`: - -``` python -# optional language argument ->>> url = 'https://www.un.org/en/about-us' - -# success: returns clean URL and domain name ->>> check_url(url, language='en') -('https://www.un.org/en/about-us', 'un.org') - -# failure: doesn't return anything ->>> check_url(url, language='de') ->>> - -# optional argument: strict ->>> url = 'https://en.wikipedia.org/' ->>> check_url(url, language='de', strict=False) -('https://en.wikipedia.org', 'wikipedia.org') ->>> check_url(url, language='de', strict=True) ->>> -``` - -Define stricter restrictions on the expected content type with -`strict=True`. This also blocks certain platforms and page types -where machines get lost. - -``` python -# strict filtering: blocked as it is a major platform ->>> check_url('https://www.twitch.com/', strict=True) ->>> -``` - -### Sampling by domain name - -``` python ->>> from courlan import sample_urls ->>> my_urls = ['https://example.org/' + str(x) for x in range(100)] ->>> my_sample = sample_urls(my_urls, 10) -# optional: exclude_min=None, exclude_max=None, strict=False, verbose=False -``` - -### Web crawling and URL handling - -Link extraction and preprocessing: - -``` python ->>> from courlan import extract_links ->>> doc = 'Link' ->>> url = "https://example.org" ->>> extract_links(doc, url) -{'https://example.org/test/link.html'} -# other options: external_bool, no_filter, language, strict, redirects, ... -``` - -The `filter_links()` function provides additional filters for crawling -purposes: use of robots.txt rules and link prioritization. It returns two -lists: regular links and priority (navigation) links. - -``` python ->>> from courlan import filter_links ->>> doc = '1Tag' ->>> links, links_priority = filter_links(doc, "https://example.org") ->>> links -['https://example.org/page1.html'] ->>> links_priority -['https://example.org/tag/listing'] -``` - -Determine if a link leads to another host: - -``` python ->>> from courlan import is_external ->>> is_external('https://github.com/', 'https://www.microsoft.com/') -True -# default ->>> is_external('https://google.com/', 'https://www.google.co.uk/', ignore_suffix=True) -False -# taking suffixes into account ->>> is_external('https://google.com/', 'https://www.google.co.uk/', ignore_suffix=False) -True -``` - -Other useful functions dedicated to URL handling: - -- `extract_domain(url, fast=True)`: find domain and subdomain or just - domain with `fast=False` -- `get_base_url(url)`: strip the URL of some of its parts -- `get_host_and_path(url)`: decompose URLs in two parts: protocol + - host/domain and path -- `get_hostinfo(url)`: extract domain and host info (protocol + - host/domain) -- `fix_relative_urls(baseurl, url)`: prepend necessary information to - relative links - -``` python ->>> from courlan import * ->>> url = 'https://www.un.org/en/about-us' - ->>> get_base_url(url) -'https://www.un.org' - ->>> get_host_and_path(url) -('https://www.un.org', '/en/about-us') - ->>> get_hostinfo(url) -('un.org', 'https://www.un.org') - ->>> fix_relative_urls('https://www.un.org', 'en/about-us') -'https://www.un.org/en/about-us' -``` - -Other filters dedicated to crawl frontier management: - -- `is_not_crawlable(url)`: check for deep web or pages generally not - usable in a crawling context -- `is_navigation_page(url)`: check for navigation and overview pages - -``` python ->>> from courlan import is_navigation_page, is_not_crawlable ->>> is_navigation_page('https://www.randomblog.net/category/myposts') -True ->>> is_not_crawlable('https://www.randomblog.net/login') -True -``` - -See also [URL management page](https://trafilatura.readthedocs.io/en/latest/url-management.html) -of the Trafilatura documentation. - - -### Python helpers - -Helper function, scrub and normalize: - -``` python ->>> from courlan import clean_url ->>> clean_url('HTTPS://WWW.DWDS.DE:443/') -'https://www.dwds.de' -``` - -Basic scrubbing only: - -``` python ->>> from courlan import scrub_url -``` - -Basic canonicalization/normalization only, i.e. modifying and -standardizing URLs in a consistent manner: - -``` python ->>> from urllib.parse import urlparse ->>> from courlan import normalize_url ->>> my_url = normalize_url(urlparse(my_url)) -# passing URL strings directly also works ->>> my_url = normalize_url(my_url) -# remove unnecessary components and re-order query elements ->>> normalize_url('http://test.net/foo.html?utm_source=twitter&post=abc&page=2#fragment', strict=True) -'http://test.net/foo.html?page=2&post=abc' -``` - -Basic URL validation only: - -``` python ->>> from courlan import validate_url ->>> validate_url('http://1234') -(False, None) ->>> validate_url('http://www.example.org/') -(True, ParseResult(scheme='http', netloc='www.example.org', path='/', params='', query='', fragment='')) -``` - -### Troubleshooting - -Courlan uses an internal cache to speed up URL parsing. It can be reset -as follows: - -``` python ->>> from courlan.meta import clear_caches ->>> clear_caches() -``` - -## UrlStore class - -The `UrlStore` class allow for storing and retrieving domain-classified -URLs, where a URL like `https://example.org/path/testpage` is stored as -the path `/path/testpage` within the domain `https://example.org`. It -features the following methods: - -- URL management - - `add_urls(urls=None, appendleft=None, visited=False)`: Add a - list of URLs to the (possibly) existing one. Optional: - append certain URLs to the left, specify if the URLs have - already been visited. - - `add_from_html(htmlstring, url, external=False, lang=None, with_nav=True)`: - Extract and filter links in a HTML string. - - `discard(domains)`: Declare domains void and prune the store. - - `dump_urls()`: Return a list of all known URLs. - - `print_urls()`: Print all URLs in store (URL + TAB + visited or not). - - `print_unvisited_urls()`: Print all unvisited URLs in store. - - `get_all_counts()`: Return all download counts for the hosts in store. - - `get_known_domains()`: Return all known domains as a list. - - `get_unvisited_domains()`: Find all domains for which there are unvisited URLs. - - `total_url_number()`: Find number of all URLs in store. - - `is_known(url)`: Check if the given URL has already been stored. - - `has_been_visited(url)`: Check if the given URL has already been visited. - - `filter_unknown_urls(urls)`: Take a list of URLs and return the currently unknown ones. - - `filter_unvisited_urls(urls)`: Take a list of URLs and return the currently unvisited ones. - - `find_known_urls(domain)`: Get all already known URLs for the - given domain (ex. `https://example.org`). - - `find_unvisited_urls(domain)`: Get all unvisited URLs for the given domain. - - `reset()`: Re-initialize the URL store. - -- Crawling and downloads - - `get_url(domain)`: Retrieve a single URL and consider it to - be visited (with corresponding timestamp). - - `get_rules(domain)`: Return the stored crawling rules for the given website. - - `store_rules(website, rules)`: Store crawling rules for a given website. - - `get_crawl_delay()`: Return the delay as extracted from robots.txt, or a given default. - - `get_download_urls(time_limit=10, max_urls=10000)`: Get a list of immediately - downloadable URLs according to the given time limit per domain. - - `establish_download_schedule(max_urls=100, time_limit=10)`: - Get up to the specified number of URLs along with a suitable - backoff schedule (in seconds). - - `download_threshold_reached(threshold)`: Find out if the - download limit (in seconds) has been reached for one of the - websites in store. - - `unvisited_websites_number()`: Return the number of websites - for which there are still URLs to visit. - - `is_exhausted_domain(domain)`: Tell if all known URLs for - the website have been visited. - -- Persistance - - `write(filename)`: Save the store to disk. - - `load_store(filename)`: Read a UrlStore from disk (separate function, not class method). - -- Optional settings: - - `compressed=True`: activate compression of URLs and rules - - `language=XX`: focus on a particular target language (two-letter code) - - `strict=True`: stricter URL filtering - - `verbose=True`: dump URLs if interrupted (requires use of `signal`) - - -## Command-line - -The main fonctions are also available through a command-line utility: - -``` bash -$ courlan --inputfile url-list.txt --outputfile cleaned-urls.txt -$ courlan --help -usage: courlan [-h] -i INPUTFILE -o OUTPUTFILE [-d DISCARDEDFILE] [-v] - [-p PARALLEL] [--strict] [-l LANGUAGE] [-r] [--sample SAMPLE] - [--exclude-max EXCLUDE_MAX] [--exclude-min EXCLUDE_MIN] - -Command-line interface for Courlan - -options: - -h, --help show this help message and exit - -I/O: - Manage input and output - - -i INPUTFILE, --inputfile INPUTFILE - name of input file (required) - -o OUTPUTFILE, --outputfile OUTPUTFILE - name of output file (required) - -d DISCARDEDFILE, --discardedfile DISCARDEDFILE - name of file to store discarded URLs (optional) - -v, --verbose increase output verbosity - -p PARALLEL, --parallel PARALLEL - number of parallel processes (not used for sampling) - -Filtering: - Configure URL filters - - --strict perform more restrictive tests - -l LANGUAGE, --language LANGUAGE - use language filter (ISO 639-1 code) - -r, --redirects check redirects - -Sampling: - Use sampling by host, configure sample size - - --sample SAMPLE size of sample per domain - --exclude-max EXCLUDE_MAX - exclude domains with more than n URLs - --exclude-min EXCLUDE_MIN - exclude domains with less than n URLs -``` - - -## License - -*coURLan* is distributed under the [Apache 2.0 -license](https://www.apache.org/licenses/LICENSE-2.0.html). - -Versions prior to v1 were under GPLv3+ license. - - -## Settings - -`courlan` is optimized for English and German but its generic approach -is also usable in other contexts. - -Details of strict URL filtering can be reviewed and changed in the file -`settings.py`. To override the default settings, clone the repository and -[re-install the package -locally](https://packaging.python.org/tutorials/installing-packages/#installing-from-a-local-src-tree). - - -## Author - -Initially launched to create text databases for research purposes -at the Berlin-Brandenburg Academy of Sciences (DWDS and ZDL units), -this package continues to be maintained but its future development -depends on community support. - -**If you value this software or depend on it for your product, consider -sponsoring it and contributing to its codebase**. Your support -[on GitHub](https://github.com/sponsors/adbar) or [ko-fi.com](https://ko-fi.com/adbarbaresi) -will help maintain and enhance this package. -Visit the [Contributing page](https://github.com/adbar/courlan/blob/master/CONTRIBUTING.md) -for more information. - -Reach out via the software repository or the [contact -page](https://adrien.barbaresi.eu/) for inquiries, collaborations, or -feedback. - -For more on Courlan's' software ecosystem see [this -graphic](https://github.com/adbar/trafilatura/blob/master/docs/software-ecosystem.png). - - -## Similar work - -These Python libraries perform similar handling and normalization tasks -but do not entail language or content filters. They also do not -primarily focus on crawl optimization: - -- [furl](https://github.com/gruns/furl) -- [ural](https://github.com/medialab/ural) -- [yarl](https://github.com/aio-libs/yarl) - - -## References - -- Cho, J., Garcia-Molina, H., & Page, L. (1998). Efficient crawling - through URL ordering. *Computer networks and ISDN systems*, 30(1-7), - 161–172. -- Edwards, J., McCurley, K. S., and Tomlin, J. A. (2001). "An - adaptive model for optimizing performance of an incremental web - crawler". In *Proceedings of the 10th international conference on - World Wide Web - WWW'01*, pp. 106–113. diff --git a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/RECORD deleted file mode 100644 index ea660a57..00000000 --- a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/RECORD +++ /dev/null @@ -1,31 +0,0 @@ -../../../bin/courlan,sha256=u5xoRtnmv2fNJJxeiwDnHKOd5mQTx2cknOJ-CdcQPnA,240 -courlan-1.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -courlan-1.4.0.dist-info/METADATA,sha256=VxhwPuff0skrnoTHH0n4aMQtU_F44nD63wCTmNO0wy0,18023 -courlan-1.4.0.dist-info/RECORD,, -courlan-1.4.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91 -courlan-1.4.0.dist-info/entry_points.txt,sha256=wRo8e-AAGQwxhWNeykVfrvCHt3hngLNI8MkyQD15AHY,45 -courlan-1.4.0.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173 -courlan-1.4.0.dist-info/top_level.txt,sha256=mNIXZYlTWhDv5JMGmkE35PCU3jWgIWOsweoIYzNxNEs,8 -courlan/__init__.py,sha256=-Rb_LTRfhklaFzvKAiW9PoHr7QNC1zXliU2A7fcZOsg,1156 -courlan/__pycache__/__init__.cpython-312.pyc,, -courlan/__pycache__/clean.cpython-312.pyc,, -courlan/__pycache__/cli.cpython-312.pyc,, -courlan/__pycache__/core.cpython-312.pyc,, -courlan/__pycache__/filters.cpython-312.pyc,, -courlan/__pycache__/meta.cpython-312.pyc,, -courlan/__pycache__/network.cpython-312.pyc,, -courlan/__pycache__/sampling.cpython-312.pyc,, -courlan/__pycache__/settings.cpython-312.pyc,, -courlan/__pycache__/urlstore.cpython-312.pyc,, -courlan/__pycache__/urlutils.cpython-312.pyc,, -courlan/clean.py,sha256=eF_49HweekSsyHk05cHiY4EX0WN8ye8X0fnA9Rp01tI,6705 -courlan/cli.py,sha256=eCbAhvGkhwRF0ZOLyfoL1XRiyiqfD4HHz6tEkakO588,5941 -courlan/core.py,sha256=Zs0WovSrXqLuKHJaRs9VzWfKAxcpCk1fBmzNTcVcA5g,8311 -courlan/filters.py,sha256=4k57c7xMk0x4Wi-vuQ37UcS_UTcsAs9_Ahs30B41xGc,8062 -courlan/meta.py,sha256=pJ8PTRY4jPKm2feWQKQU16rWTESp_S_5sDY4lbuetvg,636 -courlan/network.py,sha256=jkcwbesv13nehyLWDVELjgZPBG_OHbde58kTWfTbUxE,1724 -courlan/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -courlan/sampling.py,sha256=KiGvXwAqiM1qIQ93Wz8pswlaiJjk6cuRBY8q_hmDBoM,2045 -courlan/settings.py,sha256=W9Y5qPPZmakA5EJ8UWaKdulzhx1Y7US1770nobrlYQg,1636 -courlan/urlstore.py,sha256=ELiAfe_s9a9sWh0IgRioKcGMdFMBQ1lVpS2tEn2UMU8,20527 -courlan/urlutils.py,sha256=z06d3XKfv8wpcUgYLRdFh3nJ1kIlxBI9IZ-fYmFY5HA,5733 diff --git a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/WHEEL deleted file mode 100644 index 14a883f2..00000000 --- a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (82.0.1) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/entry_points.txt deleted file mode 100644 index d0e3b026..00000000 --- a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[console_scripts] -courlan = courlan.cli:main diff --git a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/licenses/LICENSE deleted file mode 100644 index d9a10c0d..00000000 --- a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - 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 diff --git a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/top_level.txt deleted file mode 100644 index 1c8c1466..00000000 --- a/.venv/lib/python3.12/site-packages/courlan-1.4.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -courlan diff --git a/.venv/lib/python3.12/site-packages/courlan/__init__.py b/.venv/lib/python3.12/site-packages/courlan/__init__.py deleted file mode 100644 index dda4f31d..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/__init__.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -coURLan: Clean, filter, normalize, and sample URLs -""" - -# meta -__title__ = "courlan" -__author__ = "Adrien Barbaresi" -__license__ = "Apache-2.0" -__copyright__ = "Copyright 2020-present, Adrien Barbaresi" -__version__ = "1.4.0" - - -# imports -from .clean import clean_url, normalize_url, scrub_url -from .core import check_url, extract_links, filter_links -from .filters import ( - is_navigation_page, - is_not_crawlable, - is_valid_url, - lang_filter, - validate_url, -) -from .sampling import sample_urls -from .urlstore import UrlStore, load_store -from .urlutils import ( - extract_domain, - filter_urls, - fix_relative_urls, - get_base_url, - get_host_and_path, - get_hostinfo, - is_external, -) - -__all__ = [ - "clean_url", - "normalize_url", - "scrub_url", - "check_url", - "extract_links", - "filter_links", - "is_navigation_page", - "is_not_crawlable", - "is_valid_url", - "lang_filter", - "validate_url", - "sample_urls", - "UrlStore", - "load_store", - "extract_domain", - "filter_urls", - "fix_relative_urls", - "get_base_url", - "get_host_and_path", - "get_hostinfo", - "is_external", -] diff --git a/.venv/lib/python3.12/site-packages/courlan/clean.py b/.venv/lib/python3.12/site-packages/courlan/clean.py deleted file mode 100644 index 74a3829b..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/clean.py +++ /dev/null @@ -1,203 +0,0 @@ -""" -Functions performing URL trimming and cleaning -""" - -import logging -import re -from urllib.parse import SplitResult, parse_qs, quote, urlencode, urlunsplit - -from .filters import is_valid_url -from .settings import ALLOWED_PARAMS, LANG_PARAMS, TARGET_LANGS -from .urlutils import _parse - -LOGGER = logging.getLogger(__name__) - -# parsing -PROTOCOLS = re.compile(r"https?://") -SELECTION = re.compile( - r'(https?://[^">&? ]+?)(?:https?://)|(?:https?://[^/]+?/[^/]+?[&?]u(rl)?=)(https?://[^"> ]+)' -) - -MIDDLE_URL = re.compile(r"https?://.+?(https?://.+?)(?:https?://|$)") -NETLOC_RE = re.compile(r"(?<=\w):(?:80|443)") - -# path -PATH1 = re.compile(r"/+") -PATH2 = re.compile(r"^(?:/\.\.(?![^/]))+") - -# scrub -CONTROL_CHARS = "".join(map(chr, range(0x20))) -REMAINING_MARKUP = re.compile(r"|{.+?}") -TRAILING_AMP = re.compile(r"/\&$") -TRAILING_PARTS = re.compile(r'(.*?)[<>"\s]') - -# https://github.com/AdguardTeam/AdguardFilters/blob/master/TrackParamFilter/sections/general_url.txt -# https://gitlab.com/ClearURLs/rules/-/blob/master/data.min.json -# https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/query-stripping/records -TRACKERS_RE = re.compile( - r"^(?:dc|fbc|gc|twc|yc|ysc)lid|" - r"^(?:click|gbra|msclk|igsh|partner|wbra)id|" - r"^(?:ads?|mc|ga|gs|itm|mc|mkt|ml|mtm|oly|pk|utm|vero)_|" - r"(?:\b|_)(?:aff|affi|affiliate|campaign|cl?id|eid|ga|gl|" - r"kwd|keyword|medium|ref|referr?er|session|source|uid|xtor)" -) - - -def clean_url(url: str, language: str | None = None) -> str | None: - "Helper function: chained scrubbing and normalization" - try: - return normalize_url(scrub_url(url), False, language) - except (AttributeError, ValueError): - return None - - -def scrub_url(url: str) -> str: - "Strip unnecessary parts and make sure only one URL is considered" - # remove leading/trailing space and unescaped control chars - # strip space in input string - url = "".join(url.split()).strip(CONTROL_CHARS) - - # - if url.startswith("", "") - - # markup rests - url = REMAINING_MARKUP.sub("", url) - - # & and & - url = TRAILING_AMP.sub("", url.replace("&", "&")) - - # if '"' in link: - # link = link.split('"')[0] - - # double/faulty URLs - protocols = PROTOCOLS.findall(url) - if len(protocols) > 1 and "web.archive.org" not in url: - LOGGER.debug("double url: %s %s", len(protocols), url) - match = SELECTION.match(url) - if match and is_valid_url(match[1]): - url = match[1] - LOGGER.debug("taking url: %s", url) - else: - match = MIDDLE_URL.match(url) - if match and is_valid_url(match[1]): - url = match[1] - LOGGER.debug("taking url: %s", url) - - # too long and garbled URLs e.g. due to quotes URLs - match = TRAILING_PARTS.match(url) - if match: - url = match[1] - if len(url) > 500: # arbitrary choice - LOGGER.debug("invalid-looking link %s of length %d", url[:50] + "…", len(url)) - - # trailing slashes in URLs without path or in embedded URLs - if url.count("/") == 3 or url.count("://") > 1: - url = url.rstrip("/") - - return url - - -def clean_query( - querystring: str, strict: bool = False, language: str | None = None -) -> str: - "Strip unwanted query elements" - if not querystring: - return "" - - qdict = parse_qs(querystring) - newqdict = {} - - for qelem in sorted(qdict): - teststr = qelem.lower() - # control param - if strict: - if teststr not in ALLOWED_PARAMS and teststr not in LANG_PARAMS: - continue - # get rid of trackers - elif TRACKERS_RE.search(teststr): - continue - # control language - if ( - language in TARGET_LANGS - and teststr in LANG_PARAMS - and str(qdict[qelem][0]) not in TARGET_LANGS[language] - ): - LOGGER.debug("bad lang: %s %s", language, qelem) - raise ValueError - # insert - newqdict[qelem] = qdict[qelem] - - return urlencode(newqdict, doseq=True) - - -def decode_punycode(string: str) -> str: - "Probe for punycode in lower-cased hostname and try to decode it." - if "xn--" not in string: - return string - - parts = [] - - for part in string.split("."): - if part.lower().startswith("xn--"): - try: - part = part.encode("utf8").decode("idna") - except UnicodeError: - LOGGER.debug("invalid utf/idna string: %s", part) - parts.append(part) - - return ".".join(parts) - - -def normalize_part(url_part: str) -> str: - """Normalize URLs parts (specifically path and fragment) while - accounting for certain characters.""" - return quote(url_part, safe="/%!=:,-") - - -def normalize_fragment(fragment: str, language: str | None = None) -> str: - "Look for trackers in URL fragments using query analysis, normalize the output." - if "=" in fragment: - if "&" in fragment: - fragment = clean_query(fragment, False, language) - elif TRACKERS_RE.search(fragment): - fragment = "" - return normalize_part(fragment) - - -def normalize_url( - parsed_url: SplitResult | str, - strict: bool = False, - language: str | None = None, - trailing_slash: bool = True, -) -> str: - "Takes a URL string or a parsed URL and returns a normalized URL string" - parsed_url = _parse(parsed_url) - # lowercase + remove fragments + normalize punycode - scheme = parsed_url.scheme.lower() - netloc = decode_punycode(parsed_url.netloc.lower()) - # port: strip only the scheme's default port (80 for http, 443 for https) - try: - port = parsed_url.port - except ValueError: - port = None # port could not be cast to integer value - if (scheme == "http" and port == 80) or (scheme == "https" and port == 443): - netloc = NETLOC_RE.sub("", netloc) - # path: https://github.com/saintamh/alcazar/blob/master/alcazar/utils/urls.py - # leading /../'s in the path are removed - newpath = normalize_part(PATH2.sub("", PATH1.sub("/", parsed_url.path))) - # strip unwanted query elements - newquery = clean_query(parsed_url.query, strict, language) - if newquery and not newpath: - newpath = "/" - elif ( - not trailing_slash - and not newquery - and len(newpath) > 1 - and newpath.endswith("/") - ): - newpath = newpath.rstrip("/") - # fragment - newfragment = "" if strict else normalize_fragment(parsed_url.fragment, language) - # rebuild - return urlunsplit((scheme, netloc, newpath, newquery, newfragment)) diff --git a/.venv/lib/python3.12/site-packages/courlan/cli.py b/.venv/lib/python3.12/site-packages/courlan/cli.py deleted file mode 100644 index 0e2d4902..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/cli.py +++ /dev/null @@ -1,194 +0,0 @@ -""" -Implements a basic command-line interface. -""" - -import argparse -import logging -import sys -from collections.abc import Iterator, Sequence -from concurrent.futures import ProcessPoolExecutor, as_completed -from contextlib import ExitStack -from itertools import islice - -from .core import check_url -from .sampling import _make_sample -from .urlstore import UrlStore - -LOGGER = logging.getLogger(__name__) - - -def parse_args(args: Sequence[str] | None) -> argparse.Namespace: - """Define parser for command-line arguments""" - argsparser = argparse.ArgumentParser( - description="Command-line interface for Courlan" - ) - group1 = argsparser.add_argument_group("I/O", "Manage input and output") - group1.add_argument( - "-i", - "--inputfile", - help="name of input file (required)", - type=str, - required=True, - ) - group1.add_argument( - "-o", - "--outputfile", - help="name of output file (required)", - type=str, - required=True, - ) - group1.add_argument( - "-d", - "--discardedfile", - help="name of file to store discarded URLs (optional)", - type=str, - ) - group1.add_argument( - "-v", "--verbose", help="increase output verbosity", action="store_true" - ) - group1.add_argument( - "-p", - "--parallel", - help="number of parallel processes (not used for sampling)", - type=int, - ) - group2 = argsparser.add_argument_group("Filtering", "Configure URL filters") - group2.add_argument( - "--strict", help="perform more restrictive tests", action="store_true" - ) - group2.add_argument( - "-l", "--language", help="use language filter (ISO 639-1 code)", type=str - ) - group2.add_argument( - "-r", "--redirects", help="check redirects", action="store_true" - ) - group3 = argsparser.add_argument_group( - "Sampling", "Use sampling by host, configure sample size" - ) - group3.add_argument("--sample", help="size of sample per domain", type=int) - group3.add_argument( - "--exclude-max", help="exclude domains with more than n URLs", type=int - ) - group3.add_argument( - "--exclude-min", help="exclude domains with less than n URLs", type=int - ) - return argsparser.parse_args(args) - - -def _cli_check_urls( - urls: list[str], - strict: bool = False, - with_redirects: bool = False, - language: str | None = None, - with_nav: bool = False, -) -> list[tuple[bool, str]]: - "Internal function to be used with CLI multiprocessing." - results = [] - for url in urls: - result = check_url( - url, - strict=strict, - with_redirects=with_redirects, - language=language, - with_nav=with_nav, - ) - if result is not None: - results.append((True, result[0])) - else: - results.append((False, url)) - return results - - -def _batch_lines(inputfile: str) -> Iterator[list[str]]: - "Read input line in batches" - with open(inputfile, encoding="utf-8", errors="ignore") as inputfh: - while True: - batch = [line.strip() for line in islice(inputfh, 10**5)] - if not batch: - return - yield batch - - -def _cli_sample(args: argparse.Namespace) -> None: - "Sample URLs on the CLI." - if args.verbose: - LOGGER.setLevel(logging.DEBUG) - else: - LOGGER.setLevel(logging.ERROR) - - urlstore = UrlStore( - compressed=True, language=None, strict=args.strict, verbose=args.verbose - ) - for batch in _batch_lines(args.inputfile): - urlstore.add_urls(batch) - - with open(args.outputfile, "w", encoding="utf-8") as outputfh: - for url in _make_sample( - urlstore, - args.sample, - exclude_min=args.exclude_min, - exclude_max=args.exclude_max, - ): - outputfh.write(url + "\n") - - -def _cli_process(args: argparse.Namespace) -> None: - "Read input file bit by bit and process URLs in batches." - with ExitStack() as stack: - # open the input first so the outputs are not truncated if it is unreadable - inputfh = stack.enter_context( - open(args.inputfile, encoding="utf-8", errors="ignore") - ) - executor = stack.enter_context(ProcessPoolExecutor(max_workers=args.parallel)) - outputfh = stack.enter_context(open(args.outputfile, "w", encoding="utf-8")) - discardfh = ( - stack.enter_context(open(args.discardedfile, "w", encoding="utf-8")) - if args.discardedfile is not None - else None - ) - while True: - batches: list[list[str]] = [] - while len(batches) < 1000: # pragma: no branch - line_batch = [line.strip() for line in islice(inputfh, 1000)] - if not line_batch: - break - batches.append(line_batch) - - if not batches: - break - - futures = ( - executor.submit( - _cli_check_urls, - batch, - strict=args.strict, - with_redirects=args.redirects, - language=args.language, - ) - for batch in batches - ) - - for future in as_completed(futures): - for valid, url in future.result(): - if valid: - outputfh.write(url + "\n") - elif discardfh is not None: - discardfh.write(url + "\n") - - -def process_args(args: argparse.Namespace) -> None: - """Start processing according to the arguments""" - if args.sample: - _cli_sample(args) - else: - _cli_process(args) - - -def main() -> None: - """Run as a command-line utility.""" - args = parse_args(sys.argv[1:]) - process_args(args) - - -if __name__ == "__main__": - main() diff --git a/.venv/lib/python3.12/site-packages/courlan/core.py b/.venv/lib/python3.12/site-packages/courlan/core.py deleted file mode 100644 index b1059a36..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/core.py +++ /dev/null @@ -1,266 +0,0 @@ -""" -Core functions needed to make the module work. -""" - -# import locale -import logging -import re -from urllib.robotparser import RobotFileParser - -from .clean import normalize_url, scrub_url -from .filters import ( - basic_filter, - domain_filter, - extension_filter, - is_navigation_page, - is_not_crawlable, - lang_filter, - path_filter, - type_filter, - validate_url, -) -from .network import redirection_test -from .settings import BLACKLIST -from .urlutils import ( - extract_domain, - fix_relative_urls, - get_base_url, - is_external, - is_known_link, -) - -LOGGER = logging.getLogger(__name__) - -FIND_LINKS_REGEX = re.compile(r"]+?>", re.I) -HREFLANG_REGEX = re.compile(r'hreflang=["\']?([a-z-]+)', re.I) -LINK_REGEX = re.compile(r'href=["\']?([^ ]+?)(["\' >])', re.I) - - -def check_url( - url: str, - strict: bool = False, - with_redirects: bool = False, - language: str | None = None, - with_nav: bool = False, - trailing_slash: bool = True, -) -> tuple[str, str] | None: - """Check links for appropriateness and sanity - Args: - url: url to check - strict: set to True for stricter filtering - with_redirects: set to True for redirection test (per HTTP HEAD request) - language: set target language (ISO 639-1 codes) - with_nav: set to True to include navigation pages instead of discarding them - trailing_slash: set to False to trim trailing slashes - - Returns: - A tuple consisting of canonical URL and extracted domain - - Raises: - Nothing: invalid URLs are caught internally and None is returned. - """ - - # first sanity check - # use standard parsing library, validate and strip fragments, then normalize - try: - # length test - if basic_filter(url) is False: - LOGGER.debug("rejected, basic filter: %s", url) - raise ValueError - - # clean - url = scrub_url(url) - - # get potential redirect, can raise ValueError - if with_redirects: - url = redirection_test(url) - - # spam & structural elements - if type_filter(url, strict=strict, with_nav=with_nav) is False: - LOGGER.debug("rejected, type filter: %s", url) - raise ValueError - - # internationalization and language heuristics in URL - if ( - language is not None - and lang_filter(url, language, strict, trailing_slash) is False - ): - LOGGER.debug("rejected, lang filter: %s", url) - raise ValueError - - # split and validate - validation_test, parsed_url = validate_url(url) - if validation_test is False or parsed_url is None: - LOGGER.debug("rejected, validation test: %s", url) - raise ValueError - - # content filter based on extensions - if extension_filter(parsed_url.path) is False: - LOGGER.debug("rejected, extension filter: %s", url) - raise ValueError - - # unsuitable domain/host name - if domain_filter(parsed_url.netloc) is False: - LOGGER.debug("rejected, domain name: %s", url) - raise ValueError - - # strict content filtering - if strict and path_filter(parsed_url.path, parsed_url.query) is False: - LOGGER.debug("rejected, path filter: %s", url) - raise ValueError - - # normalize - url = normalize_url(parsed_url, strict, language, trailing_slash) - - # domain info: use blacklist in strict mode only - if strict: - domain = extract_domain(url, blacklist=BLACKLIST, fast=True) - else: - domain = extract_domain(url, fast=True) - if domain is None: - LOGGER.debug("rejected, domain name: %s", url) - return None - - # handle exceptions - except (AttributeError, ValueError): - LOGGER.debug("discarded URL: %s", url) - return None - - return url, domain - - -def extract_links( - pagecontent: str, - url: str | None = None, - external_bool: bool = False, - *, - no_filter: bool = False, - language: str | None = None, - strict: bool = True, - trailing_slash: bool = True, - with_nav: bool = False, - redirects: bool = False, - reference: str | None = None, - base_url: str | None = None, -) -> set[str]: - """Filter links in a HTML document using a series of heuristics - Args: - pagecontent: whole page as a string - url: full URL of the original page - external_bool: set to True for external links only, False for - internal links only - no_filter: override settings and bypass checks to return all possible URLs - language: set target language (ISO 639-1 codes) - strict: set to True for stricter filtering - trailing_slash: set to False to trim trailing slashes - with_nav: set to True to include navigation pages instead of discarding them - redirects: set to True for redirection test (per HTTP HEAD request) - reference: provide a host reference for external/internal evaluation - - Returns: - A set containing filtered HTTP links checked for sanity and consistency. - - Raises: - ValueError: if the deprecated 'base_url' argument is provided. - """ - if base_url: - raise ValueError("'base_url' is deprecated, use 'url' instead.") - - base_url = get_base_url(url or "") - url = url or base_url - candidates: set[str] = set() - validlinks: set[str] = set() - if not pagecontent: - return validlinks - - # define host reference - reference = reference or base_url - - # extract links - for link in (m[0] for m in FIND_LINKS_REGEX.finditer(pagecontent)): - if "rel" in link and "nofollow" in link: - continue - # https://en.wikipedia.org/wiki/Hreflang - if no_filter is False and language is not None and "hreflang" in link: - langmatch = HREFLANG_REGEX.search(link) - if langmatch and ( - langmatch[1].startswith(language) or langmatch[1] == "x-default" - ): - linkmatch = LINK_REGEX.search(link) - if linkmatch: - candidates.add(linkmatch[1]) - # default - else: - linkmatch = LINK_REGEX.search(link) - if linkmatch: - candidates.add(linkmatch[1]) - - # filter candidates - for link in candidates: - # repair using base - if not link.startswith("http"): - link = fix_relative_urls(url, link) - # check - if no_filter is False: - checked = check_url( - link, - strict=strict, - trailing_slash=trailing_slash, - with_nav=with_nav, - with_redirects=redirects, - language=language, - ) - if checked is None: - continue - link = checked[0] - # external/internal links - if external_bool != is_external( - url=link, reference=reference, ignore_suffix=True - ): - continue - if is_known_link(link, validlinks): - continue - validlinks.add(link) - - LOGGER.info("%s links found – %s valid links", len(candidates), len(validlinks)) - return validlinks - - -def filter_links( - htmlstring: str, - url: str | None, - *, - lang: str | None = None, - rules: RobotFileParser | None = None, - external: bool = False, - strict: bool = False, - with_nav: bool = True, - base_url: str | None = None, -) -> tuple[list[str], list[str]]: - "Find links in a HTML document, filter and prioritize them for crawling purposes." - - if base_url: - raise ValueError("'base_url' is deprecated, use 'url' instead.") - - links, links_priority = [], [] - - for link in extract_links( - pagecontent=htmlstring, - url=url, - external_bool=external, - language=lang, - strict=strict, - with_nav=with_nav, - ): - # sanity check - if is_not_crawlable(link) or ( - rules is not None and not rules.can_fetch("*", link) - ): - continue - # store - if is_navigation_page(link): - links_priority.append(link) - else: - links.append(link) - - return links, links_priority diff --git a/.venv/lib/python3.12/site-packages/courlan/filters.py b/.venv/lib/python3.12/site-packages/courlan/filters.py deleted file mode 100644 index df909d3e..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/filters.py +++ /dev/null @@ -1,282 +0,0 @@ -""" -Bundles functions needed to target text content and validate the input. -""" - -import logging -import re -from functools import lru_cache -from ipaddress import ip_address -from urllib.parse import SplitResult, urlsplit - -from babel import Locale, UnknownLocaleError - -LOGGER = logging.getLogger(__name__) - - -PROTOCOLS = {"http", "https"} - -# domain/host names -IP_SET = { - ".", - ":", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "a", - "b", - "c", - "d", - "e", - "f", -} - -# https://github.com/python-validators/validators/blob/master/src/validators/domain.py -VALID_DOMAIN_PORT = re.compile( - # First character of the domain - r"^(?:[a-zA-Z0-9]" - # Sub domain + hostname - + r"(?:[a-zA-Z0-9-_]{0,61}[A-Za-z0-9])?\.)" - # First 61 characters of the gTLD - + r"+[A-Za-z0-9][A-Za-z0-9-_]{0,61}" - # Last character of the gTLD - + r"[A-Za-z]" - # Port number - + r"(\:(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|" - + r"6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}))?$", - re.IGNORECASE, -) - -# content filters -SITE_STRUCTURE = re.compile( - # wordpress - r"/(?:wp-(?:admin|content|includes|json|themes)|" - r"paged?|seite|search|suche|gall?er[a-z]{1,2}|labels|" - r"archives|uploads|modules|attachment|oembed)/|" - # wordpress + short URL - r"[/_-](?:tags?|schlagwort|[ck]ategor[a-z]{1,2}|[ck]at|auth?or|user)/[^/]+/?$|" - # mixed/blogspot - r"[^0-9]/[0-9]+/[0-9]+/$|[^0-9]/[0-9]{4}/$", - re.IGNORECASE, -) -FILE_TYPE = re.compile( - r"\.(atom|json|css|xml|js|jpg|jpeg|png|svg|gif|tiff|pdf|ogg|mp3|m4a|aac|" - r"avi|mp4|mov|web[mp]|flv|ico|pls|zip|tar|gz|iso|swf|woff|eot|ttf)\b|" - r"[/-](img|jpg|png)(\b|_)", - re.IGNORECASE, -) # (?=[&?]) -ADULT_AND_VIDEOS = re.compile( - r"[/_-](?:bild-?kontakte|fick|gangbang|incest|live-?cams?|live-?chat|" - r"porno?|sexcam|sexyeroti[ck]|swinger|x{3})\b", - re.IGNORECASE, -) - -# language filter -PATH_LANG_FILTER = re.compile( - r"(?:https?://[^/]+/)([a-z]{2})([_-][a-z]{2,3})?(?:/|$)", re.IGNORECASE -) -ALL_PATH_LANGS = re.compile(r"(?:/)([a-z]{2})([_-][a-z]{2})?(?:/)", re.IGNORECASE) -ALL_PATH_LANGS_NO_TRAILING = re.compile( - r"(?:/)([a-z]{2})([_-][a-z]{2})?(?:/|$)", re.IGNORECASE -) -HOST_LANG_FILTER = re.compile( - r"https?://([a-z]{2})\.(?:[^.]{4,})\.(?:[^.]+)(?:\.[^.]+)?/", re.IGNORECASE -) - -# navigation/crawls -NAVIGATION_FILTER = re.compile( - r"[/_-](archives|auth?or|[ck]at|category|kategorie|paged?|schlagwort|seite|tags?|topics?|user)/|\?p=[0-9]+", - re.IGNORECASE, -) -NOTCRAWLABLE = re.compile( - r"/([ck]onta[ck]t|datenschutzerkl.{1,2}rung|login|impressum|imprint)(\.[a-z]{3,4})?/?$|/login\?|" - r"/(javascript:|mailto:|tel\.?:|whatsapp:)", - re.IGNORECASE, -) -# |/(www\.)?(facebook\.com|google\.com|instagram\.com|twitter\.com)/ -INDEX_PAGE_FILTER = re.compile( - r".{0,5}/(default|home|index)(\.[a-z]{3,5})?/?$", re.IGNORECASE -) - -# document types -EXTENSION_REGEX = re.compile(r"\.[a-z]{2,5}$") -# https://en.wikipedia.org/wiki/List_of_file_formats#Web_page -WHITELISTED_EXTENSIONS = { - ".adp", - ".amp", - ".asp", - ".aspx", - ".cfm", - ".cgi", - ".do", - ".htm", - ".html", - ".htx", - ".jsp", - ".mht", - ".mhtml", - ".php", - ".php3", - ".php4", - ".php5", - ".phtml", - ".pl", - ".shtml", - ".stm", - ".txt", - ".xhtml", - ".xml", -} - - -def basic_filter(url: str) -> bool: - "Filter URLs based on basic formal characteristics." - return bool(url.startswith("http") and 10 <= len(url) < 500) - - -def domain_filter(domain: str) -> bool: - "Find invalid domain/host names." - # IPv4 or IPv6 - if set(domain) <= IP_SET: - try: - ip_address(domain) - except ValueError: - return False - return True - - # malformed domains - if not VALID_DOMAIN_PORT.match(domain): - try: - if not VALID_DOMAIN_PORT.match(domain.encode("idna").decode("utf-8")): - return False - except UnicodeError: - return False - - # unsuitable content - if domain.split(".")[0].isdigit() or FILE_TYPE.search(domain): - return False - - # extensions - extension_match = EXTENSION_REGEX.search(domain) - return not extension_match or extension_match[0] not in WHITELISTED_EXTENSIONS - - -def extension_filter(urlpath: str) -> bool: - "Filter based on file extension." - extension_match = EXTENSION_REGEX.search(urlpath) - return not extension_match or extension_match[0] in WHITELISTED_EXTENSIONS - - -@lru_cache(maxsize=1024) -def langcodes_score(language: str, segment: str, score: int) -> int: - "Use locale parser to assess the plausibility of the chosen URL segment." - delimiter = "_" if "_" in segment else "-" - try: - if Locale.parse(segment, sep=delimiter).language == language: - score += 1 - else: - score -= 1 - except (TypeError, UnknownLocaleError): - pass - return score - - -def lang_filter( - url: str, - language: str | None = None, - strict: bool = False, - trailing_slash: bool = True, -) -> bool: - "Heuristics targeting internationalization and linguistic elements based on a score." - # sanity check - if language is None: - return True - # init score - score = 0 - # first test: internationalization in URL path - match = PATH_LANG_FILTER.match(url) - if match: - # look for other occurrences - if trailing_slash: - occurrences = ALL_PATH_LANGS.findall(url) - else: - occurrences = ALL_PATH_LANGS_NO_TRAILING.findall(url) - if len(occurrences) == 1: - score = langcodes_score(language, match[1], score) - elif len(occurrences) == 2: - for occurrence in occurrences: - score = langcodes_score(language, occurrence, score) - # don't perform the test if there are too many candidates: > 2 - # second test: prepended language cues - if strict: - match = HOST_LANG_FILTER.match(url) - if match: - score += 1 if match[1].lower() == language else -1 - # determine test result - return score >= 0 - - -def path_filter(urlpath: str, query: str) -> bool: - "Filters based on URL path: index page, imprint, etc." - if NOTCRAWLABLE.search(urlpath): - return False - return bool(not INDEX_PAGE_FILTER.match(urlpath) or query) - - -def type_filter(url: str, strict: bool = False, with_nav: bool = False) -> bool: - """Make sure the target URL is from a suitable type (HTML page with primarily text). - Strict: Try to filter out other document types, spam, video and adult websites.""" - if ( - # feeds + blogspot - url.endswith(("/feed", "/rss", "_archive.html")) - or - # website structure - (SITE_STRUCTURE.search(url) and (not with_nav or not is_navigation_page(url))) - or - # type (also hidden in parameters), videos, adult content - (strict and (FILE_TYPE.search(url) or ADULT_AND_VIDEOS.search(url))) - ): - return False - # default - return True - - -def validate_url(url: str | None) -> tuple[bool, SplitResult | None]: - "Parse and validate the input." - try: - parsed_url = urlsplit(url) - except ValueError: - return False, None - - if not parsed_url.scheme or parsed_url.scheme not in PROTOCOLS: - return False, None - - if len(parsed_url.netloc) < 5 or ( - parsed_url.netloc.startswith("www.") and len(parsed_url.netloc) < 8 - ): - return False, None - - return True, parsed_url - - -def is_valid_url(url: str | None) -> bool: - "Determine if a given string is a valid URL." - return validate_url(url)[0] - - -def is_navigation_page(url: str) -> bool: - """Determine if the URL is related to navigation and overview pages - rather than content pages, e.g. /page/1 vs. article page.""" - return bool(NAVIGATION_FILTER.search(url)) - - -def is_not_crawlable(url: str) -> bool: - """Run tests to check if the URL may lead to deep web or pages - generally not usable in a crawling context.""" - return bool(NOTCRAWLABLE.search(url)) diff --git a/.venv/lib/python3.12/site-packages/courlan/meta.py b/.venv/lib/python3.12/site-packages/courlan/meta.py deleted file mode 100644 index 3fb86156..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/meta.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Meta-functions to be applied module-wide. -""" - -import logging - -from .filters import langcodes_score - -LOGGER = logging.getLogger(__name__) - -try: - from urllib.parse import clear_cache as urllib_clear_cache # type: ignore -except ImportError: # pragma: no cover - - def urllib_clear_cache() -> None: - "Fallback when urllib.parse.clear_cache is unavailable." - LOGGER.warning("urllib.parse.clear_cache is unavailable, skipping") - - -def clear_caches() -> None: - """Reset all known LRU caches used to speed up processing. - This may release some memory.""" - urllib_clear_cache() - langcodes_score.cache_clear() diff --git a/.venv/lib/python3.12/site-packages/courlan/network.py b/.venv/lib/python3.12/site-packages/courlan/network.py deleted file mode 100644 index 55735dfa..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/network.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -Functions devoted to requests over the WWW. -""" - -import logging - -import urllib3 - -LOGGER = logging.getLogger(__name__) -urllib3.disable_warnings() - - -RETRY_STRATEGY = urllib3.util.Retry( - total=2, - redirect=2, - raise_on_redirect=False, - status_forcelist=[ - 429, - 499, - 500, - 502, - 503, - 504, - 509, - 520, - 521, - 522, - 523, - 524, - 525, - 526, - 527, - 530, - 598, - ], # unofficial: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#Unofficial_codes - backoff_factor=1, -) -HTTP_POOL = urllib3.PoolManager( - cert_reqs="CERT_NONE", num_pools=100, retries=RETRY_STRATEGY, timeout=10 -) - -ACCEPTABLE_CODES = {200, 300, 301, 302, 303, 304, 305, 306, 307, 308} - - -# Test redirects -def redirection_test(url: str) -> str: - """Test final URL to handle redirects - Args: - url: url to check - - Returns: - The final URL seen. - - Raises: - ValueError: if the URL cannot be reached or returns an unacceptable status. - """ - # headers.update({ - # "User-Agent" : str(sample(settings.USER_AGENTS, 1)), # select a random user agent - # }) - try: - rhead = HTTP_POOL.request("HEAD", url) - except Exception as err: - LOGGER.warning("cannot reach URL: %s %s", url, err) - raise ValueError(f"cannot reach URL: {url}") from err - # response - if rhead.status in ACCEPTABLE_CODES: - # geturl() works across urllib3 1.26+/2.x; in 2.x it is Optional[str] - final_url = rhead.geturl() or url - LOGGER.debug("result found: %s %s", final_url, rhead.status) - return final_url - raise ValueError(f"cannot reach URL: {url}") diff --git a/.venv/lib/python3.12/site-packages/courlan/py.typed b/.venv/lib/python3.12/site-packages/courlan/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/courlan/sampling.py b/.venv/lib/python3.12/site-packages/courlan/sampling.py deleted file mode 100644 index 189d9ccf..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/sampling.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Utilities dedicated to URL sampling -""" - -import logging - -# from functools import cmp_to_key -from random import sample - -from .urlstore import UrlStore - -LOGGER = logging.getLogger(__name__) - - -def _make_sample( - urlstore: UrlStore, - samplesize: int, - exclude_min: int | None, - exclude_max: int | None, -) -> list[str]: - "Iterate through the hosts in store and draw samples." - output_urls = [] - for domain in urlstore.urldict: # key=cmp_to_key(locale.strcoll) - urlpaths = [ - p.path() - for p in urlstore._load_urls(domain) - if p.urlpath not in (b"/", None) - ] - # too few or too many URLs - if ( - not urlpaths - or exclude_min is not None - and len(urlpaths) < exclude_min - or exclude_max is not None - and len(urlpaths) > exclude_max - ): - LOGGER.warning("discarded (size): %s\t\turls: %s", domain, len(urlpaths)) - continue - # sample - if len(urlpaths) > samplesize: - mysample = sorted(sample(urlpaths, k=samplesize)) - else: - mysample = urlpaths - output_urls.extend([domain + p for p in mysample]) - LOGGER.debug( - "%s\t\turls: %s\tprop.: %s", - domain, - len(mysample), - len(mysample) / len(urlpaths), - ) - return output_urls - - -def sample_urls( - input_urls: list[str], - samplesize: int, - exclude_min: int | None = None, - exclude_max: int | None = None, - strict: bool = False, - verbose: bool = False, -) -> list[str]: - """Sample a list of URLs by domain name, optionally using constraints on their number""" - # logging - if verbose: - LOGGER.setLevel(logging.DEBUG) - else: - LOGGER.setLevel(logging.ERROR) - # store - urlstore = UrlStore(compressed=True, language=None, strict=strict, verbose=verbose) - urlstore.add_urls(input_urls) - # return gathered URLs - return _make_sample(urlstore, samplesize, exclude_min, exclude_max) diff --git a/.venv/lib/python3.12/site-packages/courlan/settings.py b/.venv/lib/python3.12/site-packages/courlan/settings.py deleted file mode 100644 index 95f6a235..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/settings.py +++ /dev/null @@ -1,109 +0,0 @@ -""" -General settings for package execution. -""" - -# https://www.alexa.com/topsites/countries/DE -# https://www.alexa.com/topsites/countries/US -BLACKLIST = { - "360", - "akamai", - "aliexpress", - "amzn", - "amazon", - "amazonaws", - "baidu", - "bit", - "bongacams", - "chaturbate", - "cloudfront", - "daftsex", - "delicious", - "digg", - "ebay", - "ebay-kleinanzeigen", - "facebook", - "feedburner", - "flickr", - "gettyimages", - "gmx", - "google", - "gravatar", - "http", - "imgur", - "immobilienscout24", - "instagr", - "instagram", - "jd", - "last", - "linkedin", - "live", - "livejasmin", - "localhost", - "mail", - "naver", - "netflix", - "office", - "ok", - "onlyfans", - "otto", - "paypal", - "pinterest", - "pornhub", - "postbank", - "qq", - "reddit", - "redtube", - "sina", - "sohu", - "soundcloud", - "spankbang", - "taobao", - "telegram", - "tiktok", - "tmall", - "tnaflix", - "twitch", - "twitter", - "twitpic", - "txxx", - "vk", - "vkontakte", - "vimeo", - "web", - "weibo", - "whatsapp", - "xhamster", - "xnxx", - "xvideos", - "yahoo", - "yandex", - "youjizz", - "youporn", - "youtube", - "youtu", - "zoom", -} - -ALLOWED_PARAMS = { - "aid", - "article_id", - "artnr", - "id", - "itemid", - "objectid", - "p", - "page", - "pagenum", - "page_id", - "pid", - "post", - "postid", - "product_id", -} - -LANG_PARAMS = {"lang", "language"} - -TARGET_LANGS = { - "de": {"de", "deutsch", "ger", "german"}, - "en": {"en", "english", "eng"}, # 'en_US' -} diff --git a/.venv/lib/python3.12/site-packages/courlan/urlstore.py b/.venv/lib/python3.12/site-packages/courlan/urlstore.py deleted file mode 100644 index d28bc9cb..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/urlstore.py +++ /dev/null @@ -1,574 +0,0 @@ -""" -Defines a URL store which holds URLs along with relevant information and entails crawling helpers. -""" - -import gc -import logging -import pickle -import signal -import sys - -try: - import bz2 - - HAS_BZ2 = True -except ImportError: - HAS_BZ2 = False - -try: - import zlib - - HAS_ZLIB = True -except ImportError: - HAS_ZLIB = False - - -from collections import defaultdict, deque -from collections.abc import Callable -from datetime import datetime, timedelta -from enum import Enum -from operator import itemgetter -from threading import Lock -from typing import Any -from urllib.robotparser import RobotFileParser - -from .clean import normalize_url -from .core import filter_links -from .filters import lang_filter, validate_url -from .meta import clear_caches -from .urlutils import get_base_url, get_host_and_path, is_known_link - -LOGGER = logging.getLogger(__name__) - - -class Compressor: - "Use system information on available compression modules and define corresponding methods." - - __slots__ = ("compressor", "decompressor") - - def __init__(self, compression: bool = True) -> None: - self.compressor: Callable[[bytes], bytes] - self.decompressor: Callable[[bytes], bytes] - if compression and HAS_BZ2: - self.compressor, self.decompressor = bz2.compress, bz2.decompress - elif compression and HAS_ZLIB: - self.compressor, self.decompressor = zlib.compress, zlib.decompress - else: - self.compressor = self.decompressor = self._identical - - @staticmethod - def _identical(data: Any) -> Any: - "Return unchanged data." - return data - - def compress(self, data: Any) -> Any: - "Pickle the data and compress it if a method is available." - return self.compressor(pickle.dumps(data, protocol=5)) - - def decompress(self, data: bytes) -> Any: - "Decompress the data if a method is available and load the object." - return pickle.loads(self.decompressor(data)) - - -COMPRESSOR = Compressor() - - -class State(Enum): - "Record state information about a domain or host." - - OPEN = 1 - ALL_VISITED = 2 - BUSTED = 3 - - -class DomainEntry: - "Class to record host-related information and URL paths." - - __slots__ = ("count", "rules", "state", "timestamp", "total", "tuples") - - def __init__(self, state: State = State.OPEN) -> None: - self.count: int = 0 - self.rules: RobotFileParser | None = None - self.state: State = state - self.timestamp: datetime | None = None - self.total: int = 0 - self.tuples: deque[UrlPathTuple] = deque() - - -class UrlPathTuple: - "Class storing information for URL paths relative to a domain/host." - - __slots__ = ("urlpath", "visited") - - def __init__(self, urlpath: str, visited: bool) -> None: - self.urlpath: bytes = urlpath.encode("utf-8") - self.visited: bool = visited - - def path(self) -> str: - "Get the URL path as string." - return self.urlpath.decode("utf-8") - - -class UrlStore: - "Defines a class to store domain-classified URLs and perform checks against it." - - __slots__ = ( - "compressed", - "done", - "language", - "strict", - "trailing_slash", - "urldict", - "_lock", - ) - - def __init__( - self, - compressed: bool = False, - language: str | None = None, - strict: bool = False, - trailing_slash: bool = True, - verbose: bool = False, - ) -> None: - self.compressed: bool = compressed - self.done: bool = False - self.language: str | None = language - self.strict: bool = strict - self.trailing_slash: bool = trailing_slash - self.urldict: defaultdict[str, DomainEntry] = defaultdict(DomainEntry) - self._lock: Lock = Lock() - - def dump_unvisited_urls(num: Any, frame: Any) -> None: - LOGGER.debug( - "Processing interrupted, dumping unvisited URLs from %s hosts", - len(self.urldict), - ) - self.print_unvisited_urls() - sys.exit(1) - - # don't use the following on Windows - if verbose and not sys.platform.startswith("win"): - try: - signal.signal(signal.SIGINT, dump_unvisited_urls) - signal.signal(signal.SIGTERM, dump_unvisited_urls) - except ValueError: - # signal handlers can only be registered in the main thread - LOGGER.warning("Cannot set signal handlers outside the main thread") - - def __getstate__(self) -> dict[str, Any]: - "Return the picklable state, excluding the unpicklable lock." - return {slot: getattr(self, slot) for slot in self.__slots__ if slot != "_lock"} - - def __setstate__(self, state: dict[str, Any]) -> None: - "Restore state after unpickling and re-create the lock." - for slot, value in state.items(): - setattr(self, slot, value) - self._lock = Lock() - - def _buffer_urls( - self, data: list[str], visited: bool = False - ) -> defaultdict[str, deque[UrlPathTuple]]: - inputdict: defaultdict[str, deque[UrlPathTuple]] = defaultdict(deque) - for url in dict.fromkeys(data): - # segment URL and add to domain dictionary - try: - # validate - validation_result, parsed_url = validate_url(url) - if validation_result is False or parsed_url is None: - LOGGER.debug("Invalid URL: %s", url) - raise ValueError - # filter - if ( - self.language is not None - and lang_filter( - url, self.language, self.strict, self.trailing_slash - ) - is False - ): - LOGGER.debug("Wrong language: %s", url) - raise ValueError - normalized = normalize_url( - parsed_url, - strict=self.strict, - language=self.language, - trailing_slash=self.trailing_slash, - ) - hostinfo, urlpath = get_host_and_path(normalized) - inputdict[hostinfo].append(UrlPathTuple(urlpath, visited)) - except (TypeError, ValueError): - LOGGER.warning("Discarding URL: %s", url) - return inputdict - - def _load_urls(self, domain: str) -> deque[UrlPathTuple]: - if domain in self.urldict: - if self.compressed: - return COMPRESSOR.decompress(self.urldict[domain].tuples) # type: ignore - return self.urldict[domain].tuples - return deque() - - def _set_done(self) -> None: - if not self.done and all(v.state != State.OPEN for v in self.urldict.values()): - with self._lock: - self.done = True - - def _store_urls( - self, - domain: str, - to_right: deque[UrlPathTuple] | None = None, - timestamp: datetime | None = None, - to_left: deque[UrlPathTuple] | None = None, - ) -> None: - # http/https switch - if domain.startswith("http://"): - candidate = "https" + domain[4:] - # switch - if candidate in self.urldict: - domain = candidate - elif domain.startswith("https://"): - candidate = "http" + domain[5:] - # replace entry - if candidate in self.urldict: - self.urldict[domain] = self.urldict[candidate] - del self.urldict[candidate] - - # load URLs or create entry - if domain in self.urldict: - # discard if busted - if self.urldict[domain].state is State.BUSTED: - return - urls = self._load_urls(domain) - known = {u.path() for u in urls} - else: - urls = deque() - known = set() - - # check if the link or its variants are known - if to_right is not None: - urls.extend(t for t in to_right if not is_known_link(t.path(), known)) - if to_left is not None: - urls.extendleft(t for t in to_left if not is_known_link(t.path(), known)) - - with self._lock: - if self.compressed: - self.urldict[domain].tuples = COMPRESSOR.compress(urls) - else: - self.urldict[domain].tuples = urls - self.urldict[domain].total = len(urls) - - if timestamp is not None: - self.urldict[domain].timestamp = timestamp - - if all(u.visited for u in urls): - self.urldict[domain].state = State.ALL_VISITED - else: - self.urldict[domain].state = State.OPEN - if self.done: - self.done = False - - def _search_urls(self, urls: list[str], switch: int | None = None) -> list[str]: - # init - last_domain: str | None = None - known_paths: dict[str, bool | None] = {} - remaining_urls = dict.fromkeys(urls) - # iterate - for url in sorted(remaining_urls): - hostinfo, urlpath = get_host_and_path(url) - # examine domain - if hostinfo != last_domain: - last_domain = hostinfo - known_paths = {u.path(): u.visited for u in self._load_urls(hostinfo)} - # run checks: case 1: the path matches, case 2: visited URL - if urlpath in known_paths and ( - switch == 1 or (switch == 2 and known_paths[urlpath]) - ): - del remaining_urls[url] - # preserve input order - return list(remaining_urls) - - # ADDITIONS AND DELETIONS - - def add_urls( - self, - urls: list[str] | None = None, - appendleft: list[str] | None = None, - visited: bool = False, - ) -> None: - """Add a list of URLs to the (possibly) existing one. - Optional: append certain URLs to the left, - specify if the URLs have already been visited.""" - if urls: - for host, urltuples in self._buffer_urls(urls, visited).items(): - self._store_urls(host, to_right=urltuples) - if appendleft: - for host, urltuples in self._buffer_urls(appendleft, visited).items(): - self._store_urls(host, to_left=urltuples) - - def add_from_html( - self, - htmlstring: str, - url: str, - external: bool = False, - lang: str | None = None, - with_nav: bool = True, - ) -> None: - "Find links in a HTML document, filter them and add them to the data store." - # lang = lang or self.language - base_url = get_base_url(url) - rules = self.get_rules(base_url) - links, links_priority = filter_links( - htmlstring=htmlstring, - url=url, - external=external, - lang=lang or self.language, - rules=rules, - strict=self.strict, - with_nav=with_nav, - ) - self.add_urls(urls=links, appendleft=links_priority) - - def discard(self, domains: list[str]) -> None: - "Declare domains void and prune the store." - with self._lock: - for d in domains: - self.urldict[d] = DomainEntry(state=State.BUSTED) - self._set_done() - num = gc.collect() - LOGGER.debug("%s objects in GC after UrlStore.discard", num) - - def reset(self) -> None: - "Re-initialize the URL store." - with self._lock: - self.urldict = defaultdict(DomainEntry) - clear_caches() - num = gc.collect() - LOGGER.debug("UrlStore reset, %s objects in GC", num) - - # DOMAINS / HOSTNAMES - - def get_known_domains(self) -> list[str]: - "Return all known domains as a list." - return list(self.urldict.keys()) - - def get_unvisited_domains(self) -> list[str]: - """Find all domains for which there are unvisited URLs - and potentially adjust done meta-information.""" - return [d for d, v in self.urldict.items() if v.state == State.OPEN] - - def is_exhausted_domain(self, domain: str) -> bool: - "Tell if all known URLs for the website have been visited." - if domain in self.urldict: - return self.urldict[domain].state != State.OPEN - return False - # raise KeyError("website not in store") - - def unvisited_websites_number(self) -> int: - "Return the number of websites for which there are still URLs to visit." - return len(self.get_unvisited_domains()) - - # URL-BASED QUERIES - - def find_known_urls(self, domain: str) -> list[str]: - """Get all already known URLs for the given domain (ex. "https://example.org").""" - return [domain + u.path() for u in self._load_urls(domain)] - - def find_unvisited_urls(self, domain: str) -> list[str]: - "Get all unvisited URLs for the given domain." - if not self.is_exhausted_domain(domain): - return [domain + u.path() for u in self._load_urls(domain) if not u.visited] - return [] - - def filter_unknown_urls(self, urls: list[str]) -> list[str]: - "Take a list of URLs and return the currently unknown ones." - return self._search_urls(urls, switch=1) - - def filter_unvisited_urls(self, urls: list[str]) -> list[str]: - "Take a list of URLs and return the currently unvisited ones." - return self._search_urls(urls, switch=2) - - def has_been_visited(self, url: str) -> bool: - "Check if the given URL has already been visited." - return not bool(self.filter_unvisited_urls([url])) - - def is_known(self, url: str) -> bool: - "Check if the given URL has already been stored." - hostinfo, urlpath = get_host_and_path(url) - # returns False if domain or URL is new - return urlpath in {u.path() for u in self._load_urls(hostinfo)} - - # DOWNLOADS - - def get_url(self, domain: str, as_visited: bool = True) -> str | None: - "Retrieve a single URL and consider it to be visited (with corresponding timestamp)." - # not fully used - if not self.is_exhausted_domain(domain): - url_tuples = self._load_urls(domain) - # get first non-seen url - for url in url_tuples: - if not url.visited: - # store information - if as_visited: - url.visited = True - with self._lock: - self.urldict[domain].count += 1 - self._store_urls(domain, url_tuples, timestamp=datetime.now()) - return domain + url.path() - # nothing to draw from - with self._lock: - self.urldict[domain].state = State.ALL_VISITED - self._set_done() - return None - - def get_download_urls( - self, - time_limit: float = 10.0, - max_urls: int = 10000, - ) -> list[str]: - """Get a list of immediately downloadable URLs according to the given - time limit per domain.""" - urls = [] - for website, entry in self.urldict.items(): - if entry.state != State.OPEN: - continue - if ( - not entry.timestamp - or (datetime.now() - entry.timestamp).total_seconds() > time_limit - ): - url = self.get_url(website) - if url is not None: - urls.append(url) - if len(urls) >= max_urls: - break - self._set_done() - return urls - - def establish_download_schedule( - self, max_urls: int = 100, time_limit: int = 10 - ) -> list[str]: - """Get up to the specified number of URLs along with a suitable - backoff schedule (in seconds).""" - # see which domains are free - potential = self.get_unvisited_domains() - if not potential: - return [] - # variables init - per_domain = max_urls // len(potential) or 1 - targets: list[tuple[float, str]] = [] - # iterate potential domains - for domain in potential: - # load urls - url_tuples = self._load_urls(domain) - urlpaths: list[str] = [] - # get first non-seen urls - for url in url_tuples: - if ( - len(urlpaths) >= per_domain - or (len(targets) + len(urlpaths)) >= max_urls - ): - break - if not url.visited: - urlpaths.append(url.path()) - url.visited = True - with self._lock: - self.urldict[domain].count += 1 - # determine timestamps - now = datetime.now() - original_timestamp = self.urldict[domain].timestamp - if ( - not original_timestamp - or (now - original_timestamp).total_seconds() > time_limit - ): - schedule_secs = 0.0 - else: - schedule_secs = time_limit - float( - f"{(now - original_timestamp).total_seconds():.2f}" - ) - for urlpath in urlpaths: - targets.append((schedule_secs, domain + urlpath)) - schedule_secs += time_limit - # calculate difference and offset last addition - total_diff = now + timedelta(0, schedule_secs - time_limit) - # store new info - self._store_urls(domain, url_tuples, timestamp=total_diff) - # sort by first tuple element (time in secs) - self._set_done() - return sorted(targets, key=itemgetter(1)) # type: ignore[arg-type] - - # CRAWLING - - def store_rules(self, website: str, rules: RobotFileParser | None) -> None: - "Store crawling rules for a given website." - if self.compressed: - rules = COMPRESSOR.compress(rules) - self.urldict[website].rules = rules - - def get_rules(self, website: str) -> RobotFileParser | None: - "Return the stored crawling rules for the given website." - if website in self.urldict: - if self.compressed: - return COMPRESSOR.decompress(self.urldict[website].rules) # type: ignore - return self.urldict[website].rules - return None - - def get_crawl_delay(self, website: str, default: float = 5) -> float: - "Return the delay as extracted from robots.txt, or a given default." - delay = None - rules = self.get_rules(website) - try: - delay = rules.crawl_delay("*") # type: ignore[union-attr] - except AttributeError: # no rules or no crawl delay - pass - # backup - return delay or default # type: ignore[return-value] - - # GENERAL INFO - - def get_all_counts(self) -> list[int]: - "Return all download counts for the hosts in store." - return [v.count for v in self.urldict.values()] - - def total_url_number(self) -> int: - "Find number of all URLs in store." - return sum(v.total for v in self.urldict.values()) - - def download_threshold_reached(self, threshold: float) -> bool: - "Find out if the download limit (in seconds) has been reached for one of the websites in store." - return any(v.count >= threshold for v in self.urldict.values()) - - def dump_urls(self) -> list[str]: - "Return a list of all known URLs." - urls = [] - for domain in self.urldict: - urls.extend(self.find_known_urls(domain)) - return urls - - def print_unvisited_urls(self) -> None: - "Print all unvisited URLs in store." - for domain in self.urldict: - print("\n".join(self.find_unvisited_urls(domain)), flush=True) - - def print_urls(self) -> None: - "Print all URLs in store (URL + TAB + visited or not)." - for domain in self.urldict: - print( - "\n".join( - [ - f"{domain}{u.path()}\t{str(u.visited)}" - for u in self._load_urls(domain) - ] - ), - flush=True, - ) - - # PERSISTANCE - - def write(self, filename: str) -> None: - "Write the URL store to disk." - with open(filename, "wb") as output: - pickle.dump(self, output) - - -def load_store(filename: str) -> UrlStore: - "Load a URL store from disk." - with open(filename, "rb") as output: - url_store = pickle.load(output) - return url_store diff --git a/.venv/lib/python3.12/site-packages/courlan/urlutils.py b/.venv/lib/python3.12/site-packages/courlan/urlutils.py deleted file mode 100644 index 95a5029a..00000000 --- a/.venv/lib/python3.12/site-packages/courlan/urlutils.py +++ /dev/null @@ -1,172 +0,0 @@ -""" -Functions related to URL manipulation and extraction of URL parts. -""" - -import re -from html import unescape -from urllib.parse import SplitResult, urljoin, urlsplit, urlunsplit - -from tld import get_tld - -DOMAIN_REGEX = re.compile( - r"(?:(?:f|ht)tp)s?://" # protocols - r"(?:[^/?#]{,63}\.)?" # subdomain, www, etc. - r"([^/?#.]{4,63}\.[^/?#]{2,63}|" # domain and extension - r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|" # IPv4 - r"[0-9a-f:]{16,})" # IPv6 - r"(?:/|$)" # slash or end of string -) -STRIP_PORT_REGEX = re.compile(r"(?<=\D):\d+") -CLEAN_FLD_REGEX = re.compile(r"^www[0-9]*\.") -FEED_WHITELIST_REGEX = re.compile(r"(?:feed(?:burner|proxy))", re.I) - - -def get_tldinfo(url: str, fast: bool = False) -> tuple[str | None, str | None]: - """Cached function to extract top-level domain info""" - if not url or not isinstance(url, str): - return None, None - if fast: - # try with regexes - domain_match = DOMAIN_REGEX.match(url) - if domain_match: - full_domain = STRIP_PORT_REGEX.sub("", domain_match[1].split("@")[-1]) - clean_match = full_domain.split(".")[0] - if clean_match: - return clean_match, full_domain - # fallback - tldinfo = get_tld(url, as_object=True, fail_silently=True) - if tldinfo is None: - return None, None - # this step is necessary to standardize output - return tldinfo.domain, CLEAN_FLD_REGEX.sub("", tldinfo.fld) # type: ignore[union-attr] - - -def extract_domain( - url: str, blacklist: set[str] | None = None, fast: bool = False -) -> str | None: - """Extract domain name information using top-level domain info""" - if blacklist is None: - blacklist = set() - # new code: Python >= 3.6 with tld module - domain, full_domain = get_tldinfo(url, fast=fast) - - return ( - full_domain - if full_domain and domain not in blacklist and full_domain not in blacklist - else None - ) - - -def _parse(url: str | SplitResult) -> SplitResult: - "Parse a string or use urllib.parse object directly." - if isinstance(url, str): - parsed_url = urlsplit(unescape(url)) - elif isinstance(url, SplitResult): - parsed_url = url - else: - raise TypeError("wrong input type:", type(url)) - return parsed_url - - -def get_base_url(url: str | SplitResult) -> str: - """Strip URL of some of its parts to get base URL. - Accepts strings and urllib.parse ParseResult objects.""" - parsed_url = _parse(url) - if parsed_url.scheme: - scheme = parsed_url.scheme + "://" - else: - scheme = "" - return scheme + parsed_url.netloc - - -def get_host_and_path(url: str | SplitResult) -> tuple[str, str]: - """Decompose URL in two parts: protocol + host/domain and path. - Accepts strings and urllib.parse ParseResult objects.""" - parsed_url = _parse(url) - hostname = get_base_url(parsed_url) - pathval = urlunsplit( - ["", "", parsed_url.path, parsed_url.query, parsed_url.fragment] - ) - # correction for root/homepage - if pathval == "": - pathval = "/" - if not hostname or not pathval: - raise ValueError(f"incomplete URL: {url}") - return hostname, pathval - - -def get_hostinfo(url: str) -> tuple[str | None, str]: - "Convenience function returning domain and host info (protocol + host/domain) from a URL." - domainname = extract_domain(url, fast=True) - base_url = get_base_url(url) - return domainname, base_url - - -def fix_relative_urls(baseurl: str, url: str) -> str: - "Prepend protocol and host information to relative links." - if url.startswith("{"): - return url - - base_netloc = urlsplit(baseurl).netloc - split_url = urlsplit(url) - - if split_url.netloc not in (base_netloc, ""): - if split_url.scheme: - return url - return urlunsplit(split_url._replace(scheme="http")) - - return urljoin(baseurl, url) - - -def filter_urls(link_list: list[str], urlfilter: str | None) -> list[str]: - "Return a list of links corresponding to the given substring pattern." - if urlfilter is None: - return sorted(set(link_list)) - # filter links - filtered_list = [link for link in link_list if urlfilter in link] - # feedburner option: filter and wildcards for feeds - if not filtered_list: - filtered_list = [ - link for link in link_list if FEED_WHITELIST_REGEX.search(link) - ] - return sorted(set(filtered_list)) - - -def is_external(url: str, reference: str, ignore_suffix: bool = True) -> bool: - """Determine if a link leads to another host, takes a reference URL and - a URL as input, returns a boolean""" - stripped_ref, ref = get_tldinfo(reference, fast=True) - stripped_domain, domain = get_tldinfo(url, fast=True) - # comparison - if ignore_suffix: - return stripped_domain != stripped_ref - return domain != ref - - -def is_known_link(link: str, known_links: set[str]) -> bool: - "Compare the link and its possible variants to the existing URL base." - if not link: - return False - # check exact link - if link in known_links: - return True - - # check link and variants with trailing slashes - slash_test = link.rstrip("/") if link[-1] == "/" else link + "/" - if slash_test in known_links: - return True - - # check link and variants with modified protocol - if link.startswith("http"): - protocol_test = ( - "http" + link[5:] if link.startswith("https") else "https" + link[4:] - ) - slash_test = ( - protocol_test.rstrip("/") - if protocol_test[-1] == "/" - else protocol_test + "/" - ) - if protocol_test in known_links or slash_test in known_links: - return True - - return False diff --git a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/METADATA deleted file mode 100644 index 408da483..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/METADATA +++ /dev/null @@ -1,110 +0,0 @@ -Metadata-Version: 2.4 -Name: cryptography -Version: 49.0.0 -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Natural Language :: English -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: POSIX -Classifier: Operating System :: POSIX :: BSD -Classifier: Operating System :: POSIX :: Linux -Classifier: Operating System :: Microsoft :: Windows -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable -Classifier: Topic :: Security :: Cryptography -Requires-Dist: cffi>=2.0.0 ; platform_python_implementation != 'PyPy' -Requires-Dist: typing-extensions>=4.13.2 ; python_full_version < '3.11' -Requires-Dist: bcrypt>=3.1.5 ; extra == 'ssh' -Provides-Extra: ssh -License-File: LICENSE -License-File: LICENSE.APACHE -License-File: LICENSE.BSD -Summary: cryptography is a package which provides cryptographic recipes and primitives to Python developers. -Author-email: The Python Cryptographic Authority and individual contributors -License-Expression: Apache-2.0 OR BSD-3-Clause -Requires-Python: >=3.9, !=3.9.0, !=3.9.1 -Description-Content-Type: text/x-rst; charset=UTF-8 -Project-URL: changelog, https://cryptography.io/en/latest/changelog/ -Project-URL: documentation, https://cryptography.io/ -Project-URL: homepage, https://github.com/pyca/cryptography -Project-URL: issues, https://github.com/pyca/cryptography/issues -Project-URL: source, https://github.com/pyca/cryptography/ - -pyca/cryptography -================= - -.. image:: https://img.shields.io/pypi/v/cryptography.svg - :target: https://pypi.org/project/cryptography/ - :alt: Latest Version - -.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest - :target: https://cryptography.io - :alt: Latest Docs - -.. image:: https://github.com/pyca/cryptography/actions/workflows/ci.yml/badge.svg - :target: https://github.com/pyca/cryptography/actions/workflows/ci.yml?query=branch%3Amain - -``cryptography`` is a package which provides cryptographic recipes and -primitives to Python developers. Our goal is for it to be your "cryptographic -standard library". It supports Python 3.9+ and PyPy3 7.3.11+. - -``cryptography`` includes both high level recipes and low level interfaces to -common cryptographic algorithms such as symmetric ciphers, message digests, and -key derivation functions. For example, to encrypt something with -``cryptography``'s high level symmetric encryption recipe: - -.. code-block:: pycon - - >>> from cryptography.fernet import Fernet - >>> # Put this somewhere safe! - >>> key = Fernet.generate_key() - >>> f = Fernet(key) - >>> token = f.encrypt(b"A really secret message. Not for prying eyes.") - >>> token - b'...' - >>> f.decrypt(token) - b'A really secret message. Not for prying eyes.' - -You can find more information in the `documentation`_. - -You can install ``cryptography`` with: - -.. code-block:: console - - $ pip install cryptography - -For full details see `the installation documentation`_. - -Discussion -~~~~~~~~~~ - -If you run into bugs, you can file them in our `issue tracker`_. - -We maintain a `cryptography-dev`_ mailing list for development discussion. - -You can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get -involved. - -Security -~~~~~~~~ - -Need to report a security issue? Please consult our `security reporting`_ -documentation. - - -.. _`documentation`: https://cryptography.io/ -.. _`the installation documentation`: https://cryptography.io/en/latest/installation/ -.. _`issue tracker`: https://github.com/pyca/cryptography/issues -.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev -.. _`security reporting`: https://cryptography.io/en/latest/security/ - diff --git a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/RECORD deleted file mode 100644 index 25dce70c..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/RECORD +++ /dev/null @@ -1,195 +0,0 @@ -cryptography-49.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -cryptography-49.0.0.dist-info/METADATA,sha256=H8g50k78NV8OXFB6qAeCK8pDcecyYDttPpKyQDje3tI,4343 -cryptography-49.0.0.dist-info/RECORD,, -cryptography-49.0.0.dist-info/WHEEL,sha256=RsJGtPiXI7P0nuCAlmEGFCNmDfEAgrdnSkm5qyHnkcM,104 -cryptography-49.0.0.dist-info/licenses/LICENSE,sha256=Pgx8CRqUi4JTO6mP18u0BDLW8amsv4X1ki0vmak65rs,197 -cryptography-49.0.0.dist-info/licenses/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360 -cryptography-49.0.0.dist-info/licenses/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532 -cryptography-49.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json,sha256=6ItEJ6a3AJe5_q1qqykkVrKaQASVZ8TFAaJb5QajcNc,45956 -cryptography-49.0.0.dist-info/sboms/sbom.json,sha256=lQIiB--GYQwT12j6xooh-_Lt2NzvwKFDFU6ExTWbfJw,1210 -cryptography/__about__.py,sha256=p2cyvtrX_xcEkq3BVREzUdakMwPxE5Npl9xNKBvay-k,445 -cryptography/__init__.py,sha256=mthuUrTd4FROCpUYrTIqhjz6s6T9djAZrV7nZ1oMm2o,364 -cryptography/__pycache__/__about__.cpython-312.pyc,, -cryptography/__pycache__/__init__.cpython-312.pyc,, -cryptography/__pycache__/exceptions.cpython-312.pyc,, -cryptography/__pycache__/fernet.cpython-312.pyc,, -cryptography/__pycache__/utils.cpython-312.pyc,, -cryptography/exceptions.py,sha256=835EWILc2fwxw-gyFMriciC2SqhViETB10LBSytnDIc,1087 -cryptography/fernet.py,sha256=3Cvxkh0KJSbX8HbnCHu4wfCW7U0GgfUA3v_qQ8a8iWc,6963 -cryptography/hazmat/__init__.py,sha256=5IwrLWrVp0AjEr_4FdWG_V057NSJGY_W4egNNsuct0g,455 -cryptography/hazmat/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/__pycache__/_oid.cpython-312.pyc,, -cryptography/hazmat/_oid.py,sha256=mHJL2m4LnXNqetu_yX8QnNb8UaOuX4y59uKZEGukT1o,17879 -cryptography/hazmat/asn1/__init__.py,sha256=30QNQSTZyAfiM8l9SStLnV20d6efznwm4e43MkTHAVY,776 -cryptography/hazmat/asn1/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/asn1/__pycache__/asn1.cpython-312.pyc,, -cryptography/hazmat/asn1/asn1.py,sha256=9mcfrmdvK7KtjhRYofMzAjW0u7nvqbfsvPfbhNQK1BQ,19152 -cryptography/hazmat/backends/__init__.py,sha256=O5jvKFQdZnXhKeqJ-HtulaEL9Ni7mr1mDzZY5kHlYhI,361 -cryptography/hazmat/backends/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/backends/openssl/__init__.py,sha256=p3jmJfnCag9iE5sdMrN6VvVEu55u46xaS_IjoI0SrmA,305 -cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-312.pyc,, -cryptography/hazmat/backends/openssl/backend.py,sha256=3ReyMe3ee_A8-3fSroGu7xu6pPevXQLt9Wd5O9xxecY,10591 -cryptography/hazmat/bindings/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/bindings/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/bindings/_rust.abi3.so,sha256=nm-X7kTeX1V0tpgGm-uyjxCAZixV-rggveB_uPnFzUw,11393824 -cryptography/hazmat/bindings/_rust/__init__.pyi,sha256=uUnnvOq_uameYnBNpmsv3SRAVO0WQGpjI58aR1lL4Kw,2285 -cryptography/hazmat/bindings/_rust/_openssl.pyi,sha256=T_5TpYO8bghIPjAQ3BtAABqw-CcYskHp92wm5PNY6dc,228 -cryptography/hazmat/bindings/_rust/asn1.pyi,sha256=BrGjC8J6nwuS-r3EVcdXJB8ndotfY9mbQYOfpbPG0HA,354 -cryptography/hazmat/bindings/_rust/declarative_asn1.pyi,sha256=MBmOZzLTdmPJ6-1K6Aq3PTuHJ1MGgRfIu0hIgag7ocE,3866 -cryptography/hazmat/bindings/_rust/exceptions.pyi,sha256=exXr2xw_0pB1kk93cYbM3MohbzoUkjOms1ZMUi0uQZE,640 -cryptography/hazmat/bindings/_rust/ocsp.pyi,sha256=VPVWuKHI9EMs09ZLRYAGvR0Iz0mCMmEzXAkgJHovpoM,4020 -cryptography/hazmat/bindings/_rust/openssl/__init__.pyi,sha256=JDCef29vXnrEMvF13JH9z1-W9ahiTZQAk5xhu_KH6po,1550 -cryptography/hazmat/bindings/_rust/openssl/aead.pyi,sha256=7d--xdc0vuzRe4S2uuQaGm-9GGKv4NcSLQcLyWcaD6s,4582 -cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi,sha256=LhPzHWSXJq4grAJXn6zSvSSdV-aYIIscHDwIPlJGGPs,1315 -cryptography/hazmat/bindings/_rust/openssl/cmac.pyi,sha256=nPH0X57RYpsAkRowVpjQiHE566ThUTx7YXrsadmrmHk,564 -cryptography/hazmat/bindings/_rust/openssl/dh.pyi,sha256=Z3TC-G04-THtSdAOPLM1h2G7ml5bda1ElZUcn5wpuhk,1564 -cryptography/hazmat/bindings/_rust/openssl/dsa.pyi,sha256=qBtkgj2albt2qFcnZ9UDrhzoNhCVO7HTby5VSf1EXMI,1299 -cryptography/hazmat/bindings/_rust/openssl/ec.pyi,sha256=zJy0pRa5n-_p2dm45PxECB_-B6SVZyNKfjxFDpPqT38,1691 -cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi,sha256=VXfXd5G6hUivg399R1DYdmW3eTb0EebzDTqjRC2gaRw,532 -cryptography/hazmat/bindings/_rust/openssl/ed448.pyi,sha256=Yx49lqdnjsD7bxiDV1kcaMrDktug5evi5a6zerMiy2s,514 -cryptography/hazmat/bindings/_rust/openssl/hashes.pyi,sha256=bFe3T13UeyRInUJMD-R5ArGTGLqWDiWo2Lv2KGzUbf4,1076 -cryptography/hazmat/bindings/_rust/openssl/hmac.pyi,sha256=BXZn7NDjL3JAbYW0SQ8pg1iyC5DbQXVhUAiwsi8DFR8,702 -cryptography/hazmat/bindings/_rust/openssl/hpke.pyi,sha256=p_hGLn6YH-EarJF1V5bUsUtjipviEApcR5O0sUHJLtc,2914 -cryptography/hazmat/bindings/_rust/openssl/kdf.pyi,sha256=HiiLdEB9nqMsTvMwiFppQpfPz6fHUz9_gpjEB_XAOXA,6619 -cryptography/hazmat/bindings/_rust/openssl/keys.pyi,sha256=teIt8M6ZEMJrn4s3W0UnW0DZ-30Jd68WnSsKKG124l0,912 -cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi,sha256=HChNsjW0C_vO9OVD4FhEZrXO3_FiEq2ZTF59hlfcEdM,1073 -cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi,sha256=byoeus4-lIOlWlEiRQdCc00qCkXpOB5peBY6yo9ik8s,835 -cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi,sha256=_SW9NtQ5FDlAbdclFtWpT4lGmxKIKHpN-4j8J2BzYfQ,585 -cryptography/hazmat/bindings/_rust/openssl/rsa.pyi,sha256=2OQCNSXkxgc-3uw1xiCCloIQTV6p9_kK79Yu0rhZgPc,1364 -cryptography/hazmat/bindings/_rust/openssl/x25519.pyi,sha256=ewn4GpQyb7zPwE-ni7GtyQgMC0A1mLuqYsSyqv6nI_s,523 -cryptography/hazmat/bindings/_rust/openssl/x448.pyi,sha256=juTZTmli8jO_5Vcufg-vHvx_tCyezmSLIh_9PU3TczI,505 -cryptography/hazmat/bindings/_rust/pkcs12.pyi,sha256=vEEd5wDiZvb8ZGFaziLCaWLzAwoG_tvPUxLQw5_uOl8,1605 -cryptography/hazmat/bindings/_rust/pkcs7.pyi,sha256=txGBJijqZshEcqra6byPNbnisIdlxzOSIHP2hl9arPs,1601 -cryptography/hazmat/bindings/_rust/test_support.pyi,sha256=PPhld-WkO743iXFPebeG0LtgK0aTzGdjcIsay1Gm5GE,757 -cryptography/hazmat/bindings/_rust/x509.pyi,sha256=AjnoHl5C31c5AWSAmw2XvGc2kosUpzeV8ALtMlBxYOg,10204 -cryptography/hazmat/bindings/openssl/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-312.pyc,, -cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-312.pyc,, -cryptography/hazmat/bindings/openssl/_conditional.py,sha256=vfzi-xHdStAcJQjLqqOlSos94L8D_tqFtBe7Ovw-KtY,5611 -cryptography/hazmat/bindings/openssl/binding.py,sha256=FsuLxbTnrqW60-dAWtakgufeq7TnqaQai1Pc7Dowrjo,3706 -cryptography/hazmat/decrepit/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 -cryptography/hazmat/decrepit/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/decrepit/ciphers/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 -cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-312.pyc,, -cryptography/hazmat/decrepit/ciphers/__pycache__/modes.cpython-312.pyc,, -cryptography/hazmat/decrepit/ciphers/algorithms.py,sha256=yqHt7k5OzF_KuvO8M9vTlNLtG7EotbvShyYKIaUJqhg,3566 -cryptography/hazmat/decrepit/ciphers/modes.py,sha256=Oq_PEwCke5OLczOfr_vzAOJ6wPx-rlsvHAXPBuh5b9o,1649 -cryptography/hazmat/primitives/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/primitives/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/_modes.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/_serialization.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/cmac.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/constant_time.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/hashes.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/hmac.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/hpke.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/keywrap.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/padding.cpython-312.pyc,, -cryptography/hazmat/primitives/__pycache__/poly1305.cpython-312.pyc,, -cryptography/hazmat/primitives/_asymmetric.py,sha256=RhgcouUB6HTiFDBrR1LxqkMjpUxIiNvQ1r_zJjRG6qQ,532 -cryptography/hazmat/primitives/_cipheralgorithm.py,sha256=Eh3i7lwedHfi0eLSsH93PZxQKzY9I6lkK67vL4V5tOc,1522 -cryptography/hazmat/primitives/_modes.py,sha256=_EiAOD8Jb6WpFXluwkcUT3ECz_TH8xjGvbQlGkNm59Q,3075 -cryptography/hazmat/primitives/_serialization.py,sha256=hi0xJblBAZ8pmZx2lWa-lruphxvvvG3g9DNK0G8Odfs,4440 -cryptography/hazmat/primitives/asymmetric/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/mldsa.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/mlkem.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-312.pyc,, -cryptography/hazmat/primitives/asymmetric/dh.py,sha256=klvHVaxAFkUyaWXLytqYjBTh81Zx1ByCJNZVRcSDYX8,3912 -cryptography/hazmat/primitives/asymmetric/dsa.py,sha256=kQzTViqAI9PiELNOBco_n6MfP3N9ehIPkSduOpupt0M,4482 -cryptography/hazmat/primitives/asymmetric/ec.py,sha256=LklVCLLxbbQzqiE6HRaYTRauLMphYUw3tE6c8oBIxYQ,10183 -cryptography/hazmat/primitives/asymmetric/ed25519.py,sha256=mbffLvs-3PgCDeg1rQXVXh7GLntlVL4ATjkIEc4pt5M,2998 -cryptography/hazmat/primitives/asymmetric/ed448.py,sha256=n0dK6y-VLxuw-zvrDxuPT4598hymgX-1Ry_jq9aUQhE,4002 -cryptography/hazmat/primitives/asymmetric/mldsa.py,sha256=ih4Z__s7bRAuZKs9qKcuNQ7z9W6q3xrVcw5Oc81_9X0,13750 -cryptography/hazmat/primitives/asymmetric/mlkem.py,sha256=wvVkeFT-4of-VcUAER2-zI_5hXT6rwTJPMCQ8gF9GTA,7901 -cryptography/hazmat/primitives/asymmetric/padding.py,sha256=vQ6l6gOg9HqcbOsvHrSiJRVLdEj9L4m4HkRGYziTyFA,2854 -cryptography/hazmat/primitives/asymmetric/rsa.py,sha256=lIE7lGe449W5URLTKCpvFGerITouKoZV9GwuTzScsSo,8492 -cryptography/hazmat/primitives/asymmetric/types.py,sha256=s8-WqjOntaN1oAOV5bHczLFM2mpSi0JJuyED_9FoF5w,2409 -cryptography/hazmat/primitives/asymmetric/utils.py,sha256=Qs8Re9GFPjW_tNp_73IeJEjPCf0slOIsWOxo6qymT6k,821 -cryptography/hazmat/primitives/asymmetric/x25519.py,sha256=lQcgUk-Piubj8ynFNWQeYme3tYZMgHQqkRKkIDOHLzo,3888 -cryptography/hazmat/primitives/asymmetric/x448.py,sha256=b37ig7k7poG6SJDry42j-ElCj5q4eQ0X5CFmN0Sn8z8,3913 -cryptography/hazmat/primitives/ciphers/__init__.py,sha256=eyEXmjk6_CZXaOPYDr7vAYGXr29QvzgWL2-4CSolLFs,680 -cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-312.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-312.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-312.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-312.pyc,, -cryptography/hazmat/primitives/ciphers/aead.py,sha256=Fzlyx7w8KYQakzDp1zWgJnIr62zgZrgVh1u2h4exB54,634 -cryptography/hazmat/primitives/ciphers/algorithms.py,sha256=IGfCycYJOh1TvoI34rfw8KfJ_xmt1p_DpBht2Fghceo,3496 -cryptography/hazmat/primitives/ciphers/base.py,sha256=aBC7HHBBoixebmparVr0UlODs3VD0A7B6oz_AaRjDv8,4253 -cryptography/hazmat/primitives/ciphers/modes.py,sha256=EIDdFACuIPhwOaDeUouSroxtdFi7vOrXwJbB4ZPxRLs,5999 -cryptography/hazmat/primitives/cmac.py,sha256=sz_s6H_cYnOvx-VNWdIKhRhe3Ymp8z8J0D3CBqOX3gg,338 -cryptography/hazmat/primitives/constant_time.py,sha256=xdunWT0nf8OvKdcqUhhlFKayGp4_PgVJRU2W1wLSr_A,422 -cryptography/hazmat/primitives/hashes.py,sha256=M8BrlKB3U6DEtHvWTV5VRjpteHv1kS3Zxm_Bsk04cr8,5184 -cryptography/hazmat/primitives/hmac.py,sha256=RpB3z9z5skirCQrm7zQbtnp9pLMnAjrlTUvKqF5aDDc,423 -cryptography/hazmat/primitives/hpke.py,sha256=RsHissC5l-dTPn1p2JbcrIrAnDBrLqM5ugBFSGpmKu4,865 -cryptography/hazmat/primitives/kdf/__init__.py,sha256=v3yiYBGU272EojNXbwfYZdbbfI9cVOCCG3nXhTDda3k,1037 -cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/argon2.cpython-312.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-312.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-312.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-312.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-312.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-312.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-312.pyc,, -cryptography/hazmat/primitives/kdf/argon2.py,sha256=ZJx-enUlAA4o7b46C1wlzG_XAQQW0wIkFgUKkrM_wA8,632 -cryptography/hazmat/primitives/kdf/concatkdf.py,sha256=BFnOKS72txKF0yQvASr0Na7uhzvLHpUCckK5iNAC_aQ,591 -cryptography/hazmat/primitives/kdf/hkdf.py,sha256=M0lAEfRoc4kpp4-nwDj9yB-vNZukIOYEQrUlWsBNn9o,543 -cryptography/hazmat/primitives/kdf/kbkdf.py,sha256=C3w99vjFqhqzAxzFOH4zzvVoHkb5AC6Lz6AEQGV7d_g,737 -cryptography/hazmat/primitives/kdf/pbkdf2.py,sha256=Xchkk99s-Mk6b6KSCouqdk8FVyiNVOSFIenmnW3tLgQ,468 -cryptography/hazmat/primitives/kdf/scrypt.py,sha256=XyWUdUUmhuI9V6TqAPOvujCSMGv1XQdg0a21IWCmO-U,590 -cryptography/hazmat/primitives/kdf/x963kdf.py,sha256=QKjhRehuTsAstL384bKfvjuv1yfdamAHVhsBRWen2Vw,456 -cryptography/hazmat/primitives/keywrap.py,sha256=UI-0UESQxBXTKU1HrrRoUwDiFsrWif81qyXlV1e7kyY,5776 -cryptography/hazmat/primitives/padding.py,sha256=QT-U-NvV2eQGO1wVPbDiNGNSc9keRDS-ig5cQOrLz0E,1865 -cryptography/hazmat/primitives/poly1305.py,sha256=P5EPQV-RB_FJPahpg01u0Ts4S_PnAmsroxIGXbGeRRo,355 -cryptography/hazmat/primitives/serialization/__init__.py,sha256=Q7uTgDlt7n3WfsMT6jYwutC6DIg_7SEeoAm1GHZ5B5E,1705 -cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-312.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-312.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-312.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-312.pyc,, -cryptography/hazmat/primitives/serialization/base.py,sha256=ikq5MJIwp_oUnjiaBco_PmQwOTYuGi-XkYUYHKy8Vo0,615 -cryptography/hazmat/primitives/serialization/pkcs12.py,sha256=mS9cFNG4afzvseoc5e1MWoY2VskfL8N8Y_OFjl67luY,5104 -cryptography/hazmat/primitives/serialization/pkcs7.py,sha256=mFM7OFuZ8cCxUGUoo4Cof6BnCHc_Ibx1AQjjfxnPlxo,13998 -cryptography/hazmat/primitives/serialization/ssh.py,sha256=HV6ZqIjqNNaNUL6M4gQyNJJtZ75EsiSZs6yxddqBrWI,53789 -cryptography/hazmat/primitives/twofactor/__init__.py,sha256=tmMZGB-g4IU1r7lIFqASU019zr0uPp_wEBYcwdDCKCA,258 -cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-312.pyc,, -cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-312.pyc,, -cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-312.pyc,, -cryptography/hazmat/primitives/twofactor/hotp.py,sha256=ivZo5BrcCGWLsqql4nZV0XXCjyGPi_iHfDFltGlOJwk,3256 -cryptography/hazmat/primitives/twofactor/totp.py,sha256=m5LPpRL00kp4zY8gTjr55Hfz9aMlPS53kHmVkSQCmdY,1652 -cryptography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -cryptography/utils.py,sha256=wCnykfmWmp6L5zUFou715snYL9_YLLHjpj4_NHmlhwU,4281 -cryptography/x509/__init__.py,sha256=zciyadRgnCp2DNPPm6ujEKd6EX-8_zOnwtCjJbZBatg,8273 -cryptography/x509/__pycache__/__init__.cpython-312.pyc,, -cryptography/x509/__pycache__/base.cpython-312.pyc,, -cryptography/x509/__pycache__/certificate_transparency.cpython-312.pyc,, -cryptography/x509/__pycache__/extensions.cpython-312.pyc,, -cryptography/x509/__pycache__/general_name.cpython-312.pyc,, -cryptography/x509/__pycache__/name.cpython-312.pyc,, -cryptography/x509/__pycache__/ocsp.cpython-312.pyc,, -cryptography/x509/__pycache__/oid.cpython-312.pyc,, -cryptography/x509/__pycache__/verification.cpython-312.pyc,, -cryptography/x509/base.py,sha256=k9zEFC7H7Gys58AmJNeEfpTeJCvog8p8oDEa8Dg0CO0,27056 -cryptography/x509/certificate_transparency.py,sha256=JqoOIDhlwInrYMFW6IFn77WJ0viF-PB_rlZV3vs9MYc,797 -cryptography/x509/extensions.py,sha256=0AUgutLe26SLg1KaxSeo0fG7fUBAyK2tEgJl9c_AQRM,77968 -cryptography/x509/general_name.py,sha256=sP_rV11Qlpsk4x3XXGJY_Mv0Q_s9dtjeLckHsjpLQoQ,7836 -cryptography/x509/name.py,sha256=oI2w6VdY8zgw7qpNhlxEx512W0AhWDXQrDYf5EsM2PY,15566 -cryptography/x509/ocsp.py,sha256=Yey6NdFV1MPjop24Mj_VenjEpg3kUaMopSWOK0AbeBs,12699 -cryptography/x509/oid.py,sha256=BUzgXXGVWilkBkdKPTm9R4qElE9gAGHgdYPMZAp7PJo,931 -cryptography/x509/verification.py,sha256=gR2C2c-XZQtblZhT5T5vjSKOtCb74ef2alPVmEcwFlM,958 diff --git a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/WHEEL deleted file mode 100644 index f7447571..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: maturin (1.14.0) -Root-Is-Purelib: false -Tag: cp311-abi3-macosx_11_0_arm64 diff --git a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE deleted file mode 100644 index b11f379e..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,3 +0,0 @@ -This software is made available under the terms of *either* of the licenses -found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made -under the terms of *both* these licenses. diff --git a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.APACHE b/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.APACHE deleted file mode 100644 index 62589edd..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.APACHE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://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 [yyyy] [name of copyright owner] - - 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 - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.BSD b/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.BSD deleted file mode 100644 index ec1a29d3..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/licenses/LICENSE.BSD +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) Individual contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of PyCA Cryptography 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.12/site-packages/cryptography-49.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json b/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json deleted file mode 100644 index 65533b53..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/sboms/cryptography-rust.cyclonedx.json +++ /dev/null @@ -1,1365 +0,0 @@ -{ - "bomFormat": "CycloneDX", - "specVersion": "1.5", - "version": 1, - "serialNumber": "urn:uuid:c2591875-4354-4f3c-bed8-acec184448d4", - "metadata": { - "timestamp": "2026-06-12T19:53:35.426003000Z", - "tools": [ - { - "vendor": "CycloneDX", - "name": "cargo-cyclonedx", - "version": "0.5.9" - } - ], - "authors": [ - { - "name": "The cryptography developers", - "email": "cryptography-dev@python.org" - } - ], - "component": { - "type": "library", - "bom-ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust#cryptography-rust@0.49.0", - "author": "The cryptography developers ", - "name": "cryptography-rust", - "version": "0.49.0", - "scope": "required", - "licenses": [ - { - "expression": "Apache-2.0 OR BSD-3-Clause" - } - ], - "purl": "pkg:cargo/cryptography-rust@0.49.0?download_url=file://.", - "components": [ - { - "type": "library", - "bom-ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust#cryptography-rust@0.49.0 bin-target-0", - "name": "cryptography_rust", - "version": "0.49.0", - "purl": "pkg:cargo/cryptography-rust@0.49.0?download_url=file://.#src/lib.rs" - } - ] - }, - "properties": [ - { - "name": "cdx:rustc:sbom:target:all_targets", - "value": "true" - } - ] - }, - "components": [ - { - "type": "library", - "bom-ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-cffi#0.49.0", - "author": "The cryptography developers ", - "name": "cryptography-cffi", - "version": "0.49.0", - "scope": "required", - "licenses": [ - { - "expression": "Apache-2.0 OR BSD-3-Clause" - } - ], - "purl": "pkg:cargo/cryptography-cffi@0.49.0?download_url=file://cryptography-cffi" - }, - { - "type": "library", - "bom-ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-crypto#0.49.0", - "author": "The cryptography developers ", - "name": "cryptography-crypto", - "version": "0.49.0", - "scope": "required", - "licenses": [ - { - "expression": "Apache-2.0 OR BSD-3-Clause" - } - ], - "purl": "pkg:cargo/cryptography-crypto@0.49.0?download_url=file://cryptography-crypto" - }, - { - "type": "library", - "bom-ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-keepalive#0.49.0", - "author": "The cryptography developers ", - "name": "cryptography-keepalive", - "version": "0.49.0", - "scope": "required", - "licenses": [ - { - "expression": "Apache-2.0 OR BSD-3-Clause" - } - ], - "purl": "pkg:cargo/cryptography-keepalive@0.49.0?download_url=file://cryptography-keepalive" - }, - { - "type": "library", - "bom-ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-key-parsing#0.49.0", - "author": "The cryptography developers ", - "name": "cryptography-key-parsing", - "version": "0.49.0", - "scope": "required", - "licenses": [ - { - "expression": "Apache-2.0 OR BSD-3-Clause" - } - ], - "purl": "pkg:cargo/cryptography-key-parsing@0.49.0?download_url=file://cryptography-key-parsing" - }, - { - "type": "library", - "bom-ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-openssl#0.49.0", - "author": "The cryptography developers ", - "name": "cryptography-openssl", - "version": "0.49.0", - "scope": "required", - "licenses": [ - { - "expression": "Apache-2.0 OR BSD-3-Clause" - } - ], - "purl": "pkg:cargo/cryptography-openssl@0.49.0?download_url=file://cryptography-openssl" - }, - { - "type": "library", - "bom-ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-x509#0.49.0", - "author": "The cryptography developers ", - "name": "cryptography-x509", - "version": "0.49.0", - "scope": "required", - "licenses": [ - { - "expression": "Apache-2.0 OR BSD-3-Clause" - } - ], - "purl": "pkg:cargo/cryptography-x509@0.49.0?download_url=file://cryptography-x509" - }, - { - "type": "library", - "bom-ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-x509-verification#0.49.0", - "author": "The cryptography developers ", - "name": "cryptography-x509-verification", - "version": "0.49.0", - "scope": "required", - "licenses": [ - { - "expression": "Apache-2.0 OR BSD-3-Clause" - } - ], - "purl": "pkg:cargo/cryptography-x509-verification@0.49.0?download_url=file://cryptography-x509-verification" - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", - "author": "Alex Gaynor ", - "name": "asn1", - "version": "0.24.1", - "description": "ASN.1 (DER) parser and writer for Rust.", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1" - } - ], - "licenses": [ - { - "expression": "BSD-3-Clause" - } - ], - "purl": "pkg:cargo/asn1@0.24.1", - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/alex/rust-asn1" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1", - "author": "Alex Gaynor ", - "name": "asn1_derive", - "version": "0.24.1", - "description": "#[derive] support for asn1", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "909e307f1cc32bb8bccbd98f446e6d1bf03fa30f7b53a4337da7181ad30fa11a" - } - ], - "licenses": [ - { - "expression": "BSD-3-Clause" - } - ], - "purl": "pkg:cargo/asn1_derive@0.24.1", - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/alex/rust-asn1" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1", - "author": "Marshall Pierce ", - "name": "base64", - "version": "0.22.1", - "description": "encodes and decodes base64 as bytes or utf8", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/base64@0.22.1", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/base64" - }, - { - "type": "vcs", - "url": "https://github.com/marshallpierce/rust-base64" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0", - "author": "The Rust Project Developers", - "name": "bitflags", - "version": "2.13.0", - "description": "A macro to generate structures which behave like bitflags. ", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/bitflags@2.13.0", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/bitflags" - }, - { - "type": "website", - "url": "https://github.com/bitflags/bitflags" - }, - { - "type": "vcs", - "url": "https://github.com/bitflags/bitflags" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64", - "author": "Alex Crichton ", - "name": "cc", - "version": "1.2.64", - "description": "A build-time dependency for Cargo build scripts to assist in invoking the native C compiler to compile native C code into a static archive to be linked into Rust code. ", - "scope": "excluded", - "hashes": [ - { - "alg": "SHA-256", - "content": "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/cc@1.2.64", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/cc" - }, - { - "type": "website", - "url": "https://github.com/rust-lang/cc-rs" - }, - { - "type": "vcs", - "url": "https://github.com/rust-lang/cc-rs" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", - "author": "Alex Crichton ", - "name": "cfg-if", - "version": "1.0.4", - "description": "A macro to ergonomically define an item depending on a large number of #[cfg] parameters. Structured like an if-else chain, the first matching branch is the item that gets emitted. ", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/cfg-if@1.0.4", - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/rust-lang/cfg-if" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9", - "name": "find-msvc-tools", - "version": "0.1.9", - "description": "Find windows-specific tools, read MSVC versions from the registry and from COM interfaces", - "scope": "excluded", - "hashes": [ - { - "alg": "SHA-256", - "content": "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/find-msvc-tools@0.1.9", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/find-msvc-tools" - }, - { - "type": "vcs", - "url": "https://github.com/rust-lang/cc-rs" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1", - "author": "Steven Fackler ", - "name": "foreign-types-shared", - "version": "0.1.1", - "description": "An internal crate used by foreign-types", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/foreign-types-shared@0.1.1", - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/sfackler/foreign-types" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", - "author": "Steven Fackler ", - "name": "foreign-types", - "version": "0.3.2", - "description": "A framework for Rust wrappers over C APIs", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/foreign-types@0.3.2", - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/sfackler/foreign-types" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0", - "name": "heck", - "version": "0.5.0", - "description": "heck is a case conversion library.", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/heck@0.5.0", - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/withoutboats/heck" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18", - "author": "David Tolnay ", - "name": "itoa", - "version": "1.0.18", - "description": "Fast integer primitive to string conversion", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/itoa@1.0.18", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/itoa" - }, - { - "type": "vcs", - "url": "https://github.com/dtolnay/itoa" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186", - "author": "The Rust Project Developers", - "name": "libc", - "version": "0.2.186", - "description": "Raw FFI bindings to platform libraries like libc.", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/libc@0.2.186", - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/rust-lang/libc" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4", - "author": "Aleksey Kladov ", - "name": "once_cell", - "version": "1.21.4", - "description": "Single assignment cells and lazy values.", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/once_cell@1.21.4", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/once_cell" - }, - { - "type": "vcs", - "url": "https://github.com/matklad/once_cell" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1", - "name": "openssl-macros", - "version": "0.1.1", - "description": "Internal macros used by the openssl crate.", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/openssl-macros@0.1.1" - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117", - "author": "Alex Crichton , Steven Fackler ", - "name": "openssl-sys", - "version": "0.9.117", - "description": "FFI bindings to OpenSSL", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" - } - ], - "licenses": [ - { - "expression": "MIT" - } - ], - "purl": "pkg:cargo/openssl-sys@0.9.117", - "externalReferences": [ - { - "type": "other", - "url": "openssl" - }, - { - "type": "vcs", - "url": "https://github.com/rust-openssl/rust-openssl" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81", - "author": "Steven Fackler ", - "name": "openssl", - "version": "0.10.81", - "description": "OpenSSL bindings", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" - } - ], - "licenses": [ - { - "expression": "Apache-2.0" - } - ], - "purl": "pkg:cargo/openssl@0.10.81", - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/rust-openssl/rust-openssl" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6", - "author": "Jonathan Creekmore ", - "name": "pem", - "version": "3.0.6", - "description": "Parse and encode PEM-encoded data.", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" - } - ], - "licenses": [ - { - "expression": "MIT" - } - ], - "purl": "pkg:cargo/pem@3.0.6", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/pem/" - }, - { - "type": "website", - "url": "https://github.com/jcreekmore/pem-rs.git" - }, - { - "type": "vcs", - "url": "https://github.com/jcreekmore/pem-rs.git" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33", - "author": "Alex Crichton ", - "name": "pkg-config", - "version": "0.3.33", - "description": "A library to run the pkg-config system tool at build time in order to be used in Cargo build scripts. ", - "scope": "excluded", - "hashes": [ - { - "alg": "SHA-256", - "content": "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/pkg-config@0.3.33", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/pkg-config" - }, - { - "type": "vcs", - "url": "https://github.com/rust-lang/pkg-config-rs" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.13.1", - "name": "portable-atomic", - "version": "1.13.1", - "description": "Portable atomic types including support for 128-bit atomics, atomic float, etc. ", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - } - ], - "licenses": [ - { - "expression": "Apache-2.0 OR MIT" - } - ], - "purl": "pkg:cargo/portable-atomic@1.13.1", - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/taiki-e/portable-atomic" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", - "author": "David Tolnay , Alex Crichton ", - "name": "proc-macro2", - "version": "1.0.106", - "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/proc-macro2@1.0.106", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/proc-macro2" - }, - { - "type": "vcs", - "url": "https://github.com/dtolnay/proc-macro2" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0", - "author": "PyO3 Project and Contributors ", - "name": "pyo3-build-config", - "version": "0.29.0", - "description": "Build configuration for the PyO3 ecosystem", - "scope": "excluded", - "hashes": [ - { - "alg": "SHA-256", - "content": "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/pyo3-build-config@0.29.0", - "externalReferences": [ - { - "type": "website", - "url": "https://github.com/pyo3/pyo3" - }, - { - "type": "vcs", - "url": "https://github.com/pyo3/pyo3" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0", - "author": "PyO3 Project and Contributors ", - "name": "pyo3-ffi", - "version": "0.29.0", - "description": "Python-API bindings for the PyO3 ecosystem", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/pyo3-ffi@0.29.0", - "externalReferences": [ - { - "type": "website", - "url": "https://github.com/pyo3/pyo3" - }, - { - "type": "other", - "url": "python" - }, - { - "type": "vcs", - "url": "https://github.com/pyo3/pyo3" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0", - "author": "PyO3 Project and Contributors ", - "name": "pyo3-macros-backend", - "version": "0.29.0", - "description": "Code generation for PyO3 package", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/pyo3-macros-backend@0.29.0", - "externalReferences": [ - { - "type": "website", - "url": "https://github.com/pyo3/pyo3" - }, - { - "type": "vcs", - "url": "https://github.com/pyo3/pyo3" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0", - "author": "PyO3 Project and Contributors ", - "name": "pyo3-macros", - "version": "0.29.0", - "description": "Proc macros for PyO3 package", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/pyo3-macros@0.29.0", - "externalReferences": [ - { - "type": "website", - "url": "https://github.com/pyo3/pyo3" - }, - { - "type": "vcs", - "url": "https://github.com/pyo3/pyo3" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0", - "author": "PyO3 Project and Contributors ", - "name": "pyo3", - "version": "0.29.0", - "description": "Bindings to Python interpreter", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/pyo3@0.29.0", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/crate/pyo3/" - }, - { - "type": "website", - "url": "https://github.com/pyo3/pyo3" - }, - { - "type": "other", - "url": "pyo3-python" - }, - { - "type": "vcs", - "url": "https://github.com/pyo3/pyo3" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", - "author": "David Tolnay ", - "name": "quote", - "version": "1.0.45", - "description": "Quasi-quoting macro quote!(...)", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/quote@1.0.45", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/quote/" - }, - { - "type": "vcs", - "url": "https://github.com/dtolnay/quote" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.2.2", - "author": "Lukas Bergdoll ", - "name": "self_cell", - "version": "1.2.2", - "description": "Safe-to-use proc-macro-free self-referential structs in stable Rust.", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" - } - ], - "licenses": [ - { - "expression": "Apache-2.0 OR GPL-2.0-only" - } - ], - "purl": "pkg:cargo/self_cell@1.2.2", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/self_cell" - }, - { - "type": "vcs", - "url": "https://github.com/Voultapher/self_cell" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1", - "author": "comex , Fenhl , Adrian Taylor , Alex Touchet , Daniel Parks , Garrett Berg ", - "name": "shlex", - "version": "2.0.1", - "description": "Split a string into shell words, like Python's shlex.", - "scope": "excluded", - "hashes": [ - { - "alg": "SHA-256", - "content": "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/shlex@2.0.1", - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/comex/rust-shlex" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117", - "author": "David Tolnay ", - "name": "syn", - "version": "2.0.117", - "description": "Parser for Rust source code", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/syn@2.0.117", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/syn" - }, - { - "type": "vcs", - "url": "https://github.com/dtolnay/syn" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5", - "author": "Dan Gohman ", - "name": "target-lexicon", - "version": "0.13.5", - "description": "LLVM target triple types", - "scope": "excluded", - "hashes": [ - { - "alg": "SHA-256", - "content": "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" - } - ], - "licenses": [ - { - "expression": "Apache-2.0 WITH LLVM-exception" - } - ], - "purl": "pkg:cargo/target-lexicon@0.13.5", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/target-lexicon/" - }, - { - "type": "vcs", - "url": "https://github.com/bytecodealliance/target-lexicon" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24", - "author": "David Tolnay ", - "name": "unicode-ident", - "version": "1.0.24", - "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", - "scope": "required", - "hashes": [ - { - "alg": "SHA-256", - "content": "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - } - ], - "licenses": [ - { - "expression": "(MIT OR Apache-2.0) AND Unicode-3.0" - } - ], - "purl": "pkg:cargo/unicode-ident@1.0.24", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/unicode-ident" - }, - { - "type": "vcs", - "url": "https://github.com/dtolnay/unicode-ident" - } - ] - }, - { - "type": "library", - "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15", - "author": "Jim McGrath ", - "name": "vcpkg", - "version": "0.2.15", - "description": "A library to find native dependencies in a vcpkg tree at build time in order to be used in Cargo build scripts. ", - "scope": "excluded", - "hashes": [ - { - "alg": "SHA-256", - "content": "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - } - ], - "licenses": [ - { - "expression": "MIT OR Apache-2.0" - } - ], - "purl": "pkg:cargo/vcpkg@0.2.15", - "externalReferences": [ - { - "type": "documentation", - "url": "https://docs.rs/vcpkg" - }, - { - "type": "vcs", - "url": "https://github.com/mcgoo/vcpkg-rs" - } - ] - } - ], - "dependencies": [ - { - "ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust#cryptography-rust@0.49.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", - "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1", - "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-cffi#0.49.0", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-crypto#0.49.0", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-keepalive#0.49.0", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-key-parsing#0.49.0", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-openssl#0.49.0", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-x509#0.49.0", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-x509-verification#0.49.0", - "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1", - "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81", - "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117", - "registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6", - "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0", - "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0", - "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.2.2" - ] - }, - { - "ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-cffi#0.49.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64", - "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117", - "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0" - ] - }, - { - "ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-crypto#0.49.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81" - ] - }, - { - "ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-keepalive#0.49.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0" - ] - }, - { - "ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-key-parsing#0.49.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", - "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-crypto#0.49.0", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-openssl#0.49.0", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-x509#0.49.0", - "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81", - "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117", - "registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6" - ] - }, - { - "ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-openssl#0.49.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", - "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", - "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1", - "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81", - "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117" - ] - }, - { - "ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-x509#0.49.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1" - ] - }, - { - "ref": "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-x509-verification#0.49.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", - "path+file:///Users/runner/work/cryptography/cryptography/wheelhouse/.tmpU2EH91/cryptography-49.0.0/src/rust/cryptography-x509#0.49.0" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#asn1@0.24.1", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1", - "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#asn1_derive@0.24.1", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", - "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", - "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9", - "registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#find-msvc-tools@0.1.9" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.18" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", - "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", - "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#cc@1.2.64", - "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186", - "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33", - "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.81", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.13.0", - "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.4", - "registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2", - "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186", - "registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1", - "registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.117" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#pem@3.0.6", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.33" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.13.1" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186", - "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0", - "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", - "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", - "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", - "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.29.0", - "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", - "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.29.0", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.186", - "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.4", - "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.13.1", - "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.29.0", - "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.29.0", - "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.29.0" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#self_cell@1.2.2" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#shlex@2.0.1" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.117", - "dependsOn": [ - "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.106", - "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.45", - "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" - ] - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.5" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.24" - }, - { - "ref": "registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15" - } - ] -} \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/sboms/sbom.json b/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/sboms/sbom.json deleted file mode 100644 index e5234b7d..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography-49.0.0.dist-info/sboms/sbom.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "bomFormat": "CycloneDX", - "specVersion": "1.5", - "version": 1, - "serialNumber": "urn:uuid:08696954-ed2a-4e04-b1dc-5ec4bc19db28", - "metadata": { - "timestamp": "2026-06-12T00:57:36Z" - }, - "components": [ - { - "type": "library", - "name": "openssl", - "version": "4.0.1", - "purl": "pkg:generic/openssl@4.0.1?download_url=https://github.com/openssl/openssl/releases/download/openssl-4.0.1/openssl-4.0.1.tar.gz", - "hashes": [ - { - "alg": "SHA-256", - "content": "2db3f3a0d6ea4b59e1f094ace2c8cd536dffb87cdc39084c5afa1e6f7f37dd09" - } - ], - "externalReferences": [ - { - "type": "distribution", - "url": "https://github.com/openssl/openssl/releases/download/openssl-4.0.1/openssl-4.0.1.tar.gz" - } - ], - "properties": [ - { - "name": "build:operating-system", - "value": "macos" - }, - { - "name": "build:architecture", - "value": "universal2" - }, - { - "name": "build:flags", - "value": "no-zlib no-shared no-module no-comp no-apps no-docs no-sm2-precomp no-atexit enable-ec_nistp_64_gcc_128" - } - ] - } - ] -} diff --git a/.venv/lib/python3.12/site-packages/cryptography/__about__.py b/.venv/lib/python3.12/site-packages/cryptography/__about__.py deleted file mode 100644 index 2ae9fc7b..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/__about__.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -__all__ = [ - "__author__", - "__copyright__", - "__version__", -] - -__version__ = "49.0.0" - - -__author__ = "The Python Cryptographic Authority and individual contributors" -__copyright__ = f"Copyright 2013-2026 {__author__}" diff --git a/.venv/lib/python3.12/site-packages/cryptography/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/__init__.py deleted file mode 100644 index d374f752..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.__about__ import __author__, __copyright__, __version__ - -__all__ = [ - "__author__", - "__copyright__", - "__version__", -] diff --git a/.venv/lib/python3.12/site-packages/cryptography/exceptions.py b/.venv/lib/python3.12/site-packages/cryptography/exceptions.py deleted file mode 100644 index fe125ea9..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/exceptions.py +++ /dev/null @@ -1,52 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions - -if typing.TYPE_CHECKING: - from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -_Reasons = rust_exceptions._Reasons - - -class UnsupportedAlgorithm(Exception): - def __init__(self, message: str, reason: _Reasons | None = None) -> None: - super().__init__(message) - self._reason = reason - - -class AlreadyFinalized(Exception): - pass - - -class AlreadyUpdated(Exception): - pass - - -class NotYetFinalized(Exception): - pass - - -class InvalidTag(Exception): - pass - - -class InvalidSignature(Exception): - pass - - -class InternalError(Exception): - def __init__( - self, msg: str, err_code: list[rust_openssl.OpenSSLError] - ) -> None: - super().__init__(msg) - self.err_code = err_code - - -class InvalidKey(Exception): - pass diff --git a/.venv/lib/python3.12/site-packages/cryptography/fernet.py b/.venv/lib/python3.12/site-packages/cryptography/fernet.py deleted file mode 100644 index c6744ae3..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/fernet.py +++ /dev/null @@ -1,224 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import base64 -import binascii -import os -import time -import typing -from collections.abc import Iterable - -from cryptography import utils -from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.primitives import hashes, padding -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.primitives.hmac import HMAC - - -class InvalidToken(Exception): - pass - - -_MAX_CLOCK_SKEW = 60 - - -class Fernet: - def __init__( - self, - key: bytes | str, - backend: typing.Any = None, - ) -> None: - try: - key = base64.urlsafe_b64decode(key) - except binascii.Error as exc: - raise ValueError( - "Fernet key must be 32 url-safe base64-encoded bytes." - ) from exc - if len(key) != 32: - raise ValueError( - "Fernet key must be 32 url-safe base64-encoded bytes." - ) - - self._signing_key = key[:16] - self._encryption_key = key[16:] - - @classmethod - def generate_key(cls) -> bytes: - return base64.urlsafe_b64encode(os.urandom(32)) - - def encrypt(self, data: bytes) -> bytes: - return self.encrypt_at_time(data, int(time.time())) - - def encrypt_at_time(self, data: bytes, current_time: int) -> bytes: - iv = os.urandom(16) - return self._encrypt_from_parts(data, current_time, iv) - - def _encrypt_from_parts( - self, data: bytes, current_time: int, iv: bytes - ) -> bytes: - utils._check_bytes("data", data) - - padder = padding.PKCS7(algorithms.AES.block_size).padder() - padded_data = padder.update(data) + padder.finalize() - encryptor = Cipher( - algorithms.AES(self._encryption_key), - modes.CBC(iv), - ).encryptor() - ciphertext = encryptor.update(padded_data) + encryptor.finalize() - - basic_parts = ( - b"\x80" - + current_time.to_bytes(length=8, byteorder="big") - + iv - + ciphertext - ) - - h = HMAC(self._signing_key, hashes.SHA256()) - h.update(basic_parts) - hmac = h.finalize() - return base64.urlsafe_b64encode(basic_parts + hmac) - - def decrypt(self, token: bytes | str, ttl: int | None = None) -> bytes: - timestamp, data = Fernet._get_unverified_token_data(token) - if ttl is None: - time_info = None - else: - time_info = (ttl, int(time.time())) - return self._decrypt_data(data, timestamp, time_info) - - def decrypt_at_time( - self, token: bytes | str, ttl: int, current_time: int - ) -> bytes: - if ttl is None: - raise ValueError( - "decrypt_at_time() can only be used with a non-None ttl" - ) - timestamp, data = Fernet._get_unverified_token_data(token) - return self._decrypt_data(data, timestamp, (ttl, current_time)) - - def extract_timestamp(self, token: bytes | str) -> int: - timestamp, data = Fernet._get_unverified_token_data(token) - # Verify the token was not tampered with. - self._verify_signature(data) - return timestamp - - @staticmethod - def _get_unverified_token_data(token: bytes | str) -> tuple[int, bytes]: - if not isinstance(token, (str, bytes)): - raise TypeError("token must be bytes or str") - - try: - data = base64.urlsafe_b64decode(token) - except (TypeError, binascii.Error): - raise InvalidToken - - if not data or data[0] != 0x80: - raise InvalidToken - - if len(data) < 9: - raise InvalidToken - - timestamp = int.from_bytes(data[1:9], byteorder="big") - return timestamp, data - - def _verify_signature(self, data: bytes) -> None: - h = HMAC(self._signing_key, hashes.SHA256()) - h.update(data[:-32]) - try: - h.verify(data[-32:]) - except InvalidSignature: - raise InvalidToken - - def _decrypt_data( - self, - data: bytes, - timestamp: int, - time_info: tuple[int, int] | None, - ) -> bytes: - if time_info is not None: - ttl, current_time = time_info - if timestamp + ttl < current_time: - raise InvalidToken - - if current_time + _MAX_CLOCK_SKEW < timestamp: - raise InvalidToken - - self._verify_signature(data) - - iv = data[9:25] - ciphertext = data[25:-32] - decryptor = Cipher( - algorithms.AES(self._encryption_key), modes.CBC(iv) - ).decryptor() - plaintext_padded = decryptor.update(ciphertext) - try: - plaintext_padded += decryptor.finalize() - except ValueError: - raise InvalidToken - unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() - - unpadded = unpadder.update(plaintext_padded) - try: - unpadded += unpadder.finalize() - except ValueError: - raise InvalidToken - return unpadded - - -class MultiFernet: - def __init__(self, fernets: Iterable[Fernet]): - fernets = list(fernets) - if not fernets: - raise ValueError( - "MultiFernet requires at least one Fernet instance" - ) - self._fernets = fernets - - def encrypt(self, msg: bytes) -> bytes: - return self.encrypt_at_time(msg, int(time.time())) - - def encrypt_at_time(self, msg: bytes, current_time: int) -> bytes: - return self._fernets[0].encrypt_at_time(msg, current_time) - - def rotate(self, msg: bytes | str) -> bytes: - timestamp, data = Fernet._get_unverified_token_data(msg) - for f in self._fernets: - try: - p = f._decrypt_data(data, timestamp, None) - break - except InvalidToken: - pass - else: - raise InvalidToken - - iv = os.urandom(16) - return self._fernets[0]._encrypt_from_parts(p, timestamp, iv) - - def decrypt(self, msg: bytes | str, ttl: int | None = None) -> bytes: - for f in self._fernets: - try: - return f.decrypt(msg, ttl) - except InvalidToken: - pass - raise InvalidToken - - def decrypt_at_time( - self, msg: bytes | str, ttl: int, current_time: int - ) -> bytes: - for f in self._fernets: - try: - return f.decrypt_at_time(msg, ttl, current_time) - except InvalidToken: - pass - raise InvalidToken - - def extract_timestamp(self, msg: bytes | str) -> int: - for f in self._fernets: - try: - return f.extract_timestamp(msg) - except InvalidToken: - pass - raise InvalidToken diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py deleted file mode 100644 index b9f11870..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -""" -Hazardous Materials - -This is a "Hazardous Materials" module. You should ONLY use it if you're -100% absolutely sure that you know what you're doing because this module -is full of land mines, dragons, and dinosaurs with laser guns. -""" diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py deleted file mode 100644 index 6849215f..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/_oid.py +++ /dev/null @@ -1,368 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import ( - ObjectIdentifier as ObjectIdentifier, -) -from cryptography.hazmat.primitives import hashes - - -class ExtensionOID: - SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9") - SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14") - KEY_USAGE = ObjectIdentifier("2.5.29.15") - PRIVATE_KEY_USAGE_PERIOD = ObjectIdentifier("2.5.29.16") - SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17") - ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18") - BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19") - NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30") - CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31") - CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32") - POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33") - AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35") - POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36") - EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37") - FRESHEST_CRL = ObjectIdentifier("2.5.29.46") - INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54") - ISSUING_DISTRIBUTION_POINT = ObjectIdentifier("2.5.29.28") - AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1") - SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11") - OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") - TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24") - CRL_NUMBER = ObjectIdentifier("2.5.29.20") - DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27") - PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier( - "1.3.6.1.4.1.11129.2.4.2" - ) - PRECERT_POISON = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3") - SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5") - MS_CERTIFICATE_TEMPLATE = ObjectIdentifier("1.3.6.1.4.1.311.21.7") - ADMISSIONS = ObjectIdentifier("1.3.36.8.3.3") - - -class OCSPExtensionOID: - NONCE = ObjectIdentifier("1.3.6.1.5.5.7.48.1.2") - ACCEPTABLE_RESPONSES = ObjectIdentifier("1.3.6.1.5.5.7.48.1.4") - - -class CRLEntryExtensionOID: - CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") - CRL_REASON = ObjectIdentifier("2.5.29.21") - INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") - - -class NameOID: - COMMON_NAME = ObjectIdentifier("2.5.4.3") - COUNTRY_NAME = ObjectIdentifier("2.5.4.6") - LOCALITY_NAME = ObjectIdentifier("2.5.4.7") - STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8") - STREET_ADDRESS = ObjectIdentifier("2.5.4.9") - ORGANIZATION_IDENTIFIER = ObjectIdentifier("2.5.4.97") - ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10") - ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11") - SERIAL_NUMBER = ObjectIdentifier("2.5.4.5") - SURNAME = ObjectIdentifier("2.5.4.4") - GIVEN_NAME = ObjectIdentifier("2.5.4.42") - TITLE = ObjectIdentifier("2.5.4.12") - INITIALS = ObjectIdentifier("2.5.4.43") - GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44") - X500_UNIQUE_IDENTIFIER = ObjectIdentifier("2.5.4.45") - DN_QUALIFIER = ObjectIdentifier("2.5.4.46") - PSEUDONYM = ObjectIdentifier("2.5.4.65") - USER_ID = ObjectIdentifier("0.9.2342.19200300.100.1.1") - DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25") - EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1") - JURISDICTION_COUNTRY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.3") - JURISDICTION_LOCALITY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.1") - JURISDICTION_STATE_OR_PROVINCE_NAME = ObjectIdentifier( - "1.3.6.1.4.1.311.60.2.1.2" - ) - BUSINESS_CATEGORY = ObjectIdentifier("2.5.4.15") - POSTAL_ADDRESS = ObjectIdentifier("2.5.4.16") - POSTAL_CODE = ObjectIdentifier("2.5.4.17") - INN = ObjectIdentifier("1.2.643.3.131.1.1") - OGRN = ObjectIdentifier("1.2.643.100.1") - SNILS = ObjectIdentifier("1.2.643.100.3") - UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") - - -class SignatureAlgorithmOID: - RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4") - RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5") - # This is an alternate OID for RSA with SHA1 that is occasionally seen - _RSA_WITH_SHA1 = ObjectIdentifier("1.3.14.3.2.29") - RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14") - RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11") - RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12") - RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13") - RSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.13") - RSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.14") - RSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.15") - RSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.16") - RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") - ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1") - ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1") - ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2") - ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3") - ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4") - ECDSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.9") - ECDSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.10") - ECDSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.11") - ECDSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.12") - DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3") - DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1") - DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2") - DSA_WITH_SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.3.3") - DSA_WITH_SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.3.4") - ED25519 = ObjectIdentifier("1.3.101.112") - ED448 = ObjectIdentifier("1.3.101.113") - ML_DSA_44 = ObjectIdentifier("2.16.840.1.101.3.4.3.17") - ML_DSA_65 = ObjectIdentifier("2.16.840.1.101.3.4.3.18") - ML_DSA_87 = ObjectIdentifier("2.16.840.1.101.3.4.3.19") - GOSTR3411_94_WITH_3410_2001 = ObjectIdentifier("1.2.643.2.2.3") - GOSTR3410_2012_WITH_3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2") - GOSTR3410_2012_WITH_3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3") - - -_SIG_OIDS_TO_HASH: dict[ObjectIdentifier, hashes.HashAlgorithm | None] = { - SignatureAlgorithmOID.RSA_WITH_MD5: hashes.MD5(), - SignatureAlgorithmOID.RSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID._RSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID.RSA_WITH_SHA224: hashes.SHA224(), - SignatureAlgorithmOID.RSA_WITH_SHA256: hashes.SHA256(), - SignatureAlgorithmOID.RSA_WITH_SHA384: hashes.SHA384(), - SignatureAlgorithmOID.RSA_WITH_SHA512: hashes.SHA512(), - SignatureAlgorithmOID.RSA_WITH_SHA3_224: hashes.SHA3_224(), - SignatureAlgorithmOID.RSA_WITH_SHA3_256: hashes.SHA3_256(), - SignatureAlgorithmOID.RSA_WITH_SHA3_384: hashes.SHA3_384(), - SignatureAlgorithmOID.RSA_WITH_SHA3_512: hashes.SHA3_512(), - SignatureAlgorithmOID.ECDSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID.ECDSA_WITH_SHA224: hashes.SHA224(), - SignatureAlgorithmOID.ECDSA_WITH_SHA256: hashes.SHA256(), - SignatureAlgorithmOID.ECDSA_WITH_SHA384: hashes.SHA384(), - SignatureAlgorithmOID.ECDSA_WITH_SHA512: hashes.SHA512(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_224: hashes.SHA3_224(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_256: hashes.SHA3_256(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_384: hashes.SHA3_384(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_512: hashes.SHA3_512(), - SignatureAlgorithmOID.DSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID.DSA_WITH_SHA224: hashes.SHA224(), - SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(), - SignatureAlgorithmOID.ED25519: None, - SignatureAlgorithmOID.ED448: None, - SignatureAlgorithmOID.ML_DSA_44: None, - SignatureAlgorithmOID.ML_DSA_65: None, - SignatureAlgorithmOID.ML_DSA_87: None, - SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None, - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None, - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None, -} - - -class HashAlgorithmOID: - SHA1 = ObjectIdentifier("1.3.14.3.2.26") - SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.2.4") - SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.2.1") - SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.2.2") - SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.2.3") - SHA3_224 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.224") - SHA3_256 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.256") - SHA3_384 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.384") - SHA3_512 = ObjectIdentifier("1.3.6.1.4.1.37476.3.2.1.99.7.512") - SHA3_224_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.7") - SHA3_256_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.8") - SHA3_384_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.9") - SHA3_512_NIST = ObjectIdentifier("2.16.840.1.101.3.4.2.10") - - -class PublicKeyAlgorithmOID: - DSA = ObjectIdentifier("1.2.840.10040.4.1") - EC_PUBLIC_KEY = ObjectIdentifier("1.2.840.10045.2.1") - RSAES_PKCS1_v1_5 = ObjectIdentifier("1.2.840.113549.1.1.1") - RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") - X25519 = ObjectIdentifier("1.3.101.110") - X448 = ObjectIdentifier("1.3.101.111") - ED25519 = ObjectIdentifier("1.3.101.112") - ED448 = ObjectIdentifier("1.3.101.113") - ML_DSA_44 = ObjectIdentifier("2.16.840.1.101.3.4.3.17") - ML_DSA_65 = ObjectIdentifier("2.16.840.1.101.3.4.3.18") - ML_DSA_87 = ObjectIdentifier("2.16.840.1.101.3.4.3.19") - - -class ExtendedKeyUsageOID: - SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") - CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") - CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") - EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") - TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") - OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") - ANY_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37.0") - SMARTCARD_LOGON = ObjectIdentifier("1.3.6.1.4.1.311.20.2.2") - KERBEROS_PKINIT_KDC = ObjectIdentifier("1.3.6.1.5.2.3.5") - IPSEC_IKE = ObjectIdentifier("1.3.6.1.5.5.7.3.17") - BUNDLE_SECURITY = ObjectIdentifier("1.3.6.1.5.5.7.3.35") - CERTIFICATE_TRANSPARENCY = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.4") - - -class OtherNameFormOID: - PERMANENT_IDENTIFIER = ObjectIdentifier("1.3.6.1.5.5.7.8.3") - HW_MODULE_NAME = ObjectIdentifier("1.3.6.1.5.5.7.8.4") - DNS_SRV = ObjectIdentifier("1.3.6.1.5.5.7.8.7") - NAI_REALM = ObjectIdentifier("1.3.6.1.5.5.7.8.8") - SMTP_UTF8_MAILBOX = ObjectIdentifier("1.3.6.1.5.5.7.8.9") - ACP_NODE_NAME = ObjectIdentifier("1.3.6.1.5.5.7.8.10") - BUNDLE_EID = ObjectIdentifier("1.3.6.1.5.5.7.8.11") - - -class AuthorityInformationAccessOID: - CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2") - OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1") - - -class SubjectInformationAccessOID: - CA_REPOSITORY = ObjectIdentifier("1.3.6.1.5.5.7.48.5") - - -class CertificatePoliciesOID: - CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1") - CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2") - ANY_POLICY = ObjectIdentifier("2.5.29.32.0") - - -class AttributeOID: - CHALLENGE_PASSWORD = ObjectIdentifier("1.2.840.113549.1.9.7") - UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") - - -_OID_NAMES = { - NameOID.COMMON_NAME: "commonName", - NameOID.COUNTRY_NAME: "countryName", - NameOID.LOCALITY_NAME: "localityName", - NameOID.STATE_OR_PROVINCE_NAME: "stateOrProvinceName", - NameOID.STREET_ADDRESS: "streetAddress", - NameOID.ORGANIZATION_NAME: "organizationName", - NameOID.ORGANIZATIONAL_UNIT_NAME: "organizationalUnitName", - NameOID.SERIAL_NUMBER: "serialNumber", - NameOID.SURNAME: "surname", - NameOID.GIVEN_NAME: "givenName", - NameOID.TITLE: "title", - NameOID.GENERATION_QUALIFIER: "generationQualifier", - NameOID.X500_UNIQUE_IDENTIFIER: "x500UniqueIdentifier", - NameOID.DN_QUALIFIER: "dnQualifier", - NameOID.PSEUDONYM: "pseudonym", - NameOID.USER_ID: "userID", - NameOID.DOMAIN_COMPONENT: "domainComponent", - NameOID.EMAIL_ADDRESS: "emailAddress", - NameOID.JURISDICTION_COUNTRY_NAME: "jurisdictionCountryName", - NameOID.JURISDICTION_LOCALITY_NAME: "jurisdictionLocalityName", - NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: ( - "jurisdictionStateOrProvinceName" - ), - NameOID.BUSINESS_CATEGORY: "businessCategory", - NameOID.POSTAL_ADDRESS: "postalAddress", - NameOID.POSTAL_CODE: "postalCode", - NameOID.INN: "INN", - NameOID.OGRN: "OGRN", - NameOID.SNILS: "SNILS", - NameOID.UNSTRUCTURED_NAME: "unstructuredName", - SignatureAlgorithmOID.RSA_WITH_MD5: "md5WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA1: "sha1WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA224: "sha224WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA256: "sha256WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA384: "sha384WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA512: "sha512WithRSAEncryption", - SignatureAlgorithmOID.RSASSA_PSS: "rsassaPss", - SignatureAlgorithmOID.ECDSA_WITH_SHA1: "ecdsa-with-SHA1", - SignatureAlgorithmOID.ECDSA_WITH_SHA224: "ecdsa-with-SHA224", - SignatureAlgorithmOID.ECDSA_WITH_SHA256: "ecdsa-with-SHA256", - SignatureAlgorithmOID.ECDSA_WITH_SHA384: "ecdsa-with-SHA384", - SignatureAlgorithmOID.ECDSA_WITH_SHA512: "ecdsa-with-SHA512", - SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1", - SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224", - SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256", - SignatureAlgorithmOID.ED25519: "ed25519", - SignatureAlgorithmOID.ED448: "ed448", - SignatureAlgorithmOID.ML_DSA_44: "ML-DSA-44", - SignatureAlgorithmOID.ML_DSA_65: "ML-DSA-65", - SignatureAlgorithmOID.ML_DSA_87: "ML-DSA-87", - SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: ( - "GOST R 34.11-94 with GOST R 34.10-2001" - ), - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: ( - "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)" - ), - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: ( - "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)" - ), - HashAlgorithmOID.SHA1: "sha1", - HashAlgorithmOID.SHA224: "sha224", - HashAlgorithmOID.SHA256: "sha256", - HashAlgorithmOID.SHA384: "sha384", - HashAlgorithmOID.SHA512: "sha512", - HashAlgorithmOID.SHA3_224: "sha3_224", - HashAlgorithmOID.SHA3_256: "sha3_256", - HashAlgorithmOID.SHA3_384: "sha3_384", - HashAlgorithmOID.SHA3_512: "sha3_512", - HashAlgorithmOID.SHA3_224_NIST: "sha3_224", - HashAlgorithmOID.SHA3_256_NIST: "sha3_256", - HashAlgorithmOID.SHA3_384_NIST: "sha3_384", - HashAlgorithmOID.SHA3_512_NIST: "sha3_512", - PublicKeyAlgorithmOID.DSA: "dsaEncryption", - PublicKeyAlgorithmOID.EC_PUBLIC_KEY: "id-ecPublicKey", - PublicKeyAlgorithmOID.RSAES_PKCS1_v1_5: "rsaEncryption", - PublicKeyAlgorithmOID.X25519: "X25519", - PublicKeyAlgorithmOID.X448: "X448", - ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth", - ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth", - ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning", - ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection", - ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping", - ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning", - ExtendedKeyUsageOID.SMARTCARD_LOGON: "msSmartcardLogin", - ExtendedKeyUsageOID.KERBEROS_PKINIT_KDC: "pkInitKDC", - ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes", - ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier", - ExtensionOID.KEY_USAGE: "keyUsage", - ExtensionOID.PRIVATE_KEY_USAGE_PERIOD: "privateKeyUsagePeriod", - ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName", - ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName", - ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints", - ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: ( - "signedCertificateTimestampList" - ), - ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: ( - "signedCertificateTimestampList" - ), - ExtensionOID.PRECERT_POISON: "ctPoison", - ExtensionOID.MS_CERTIFICATE_TEMPLATE: "msCertificateTemplate", - ExtensionOID.ADMISSIONS: "Admissions", - CRLEntryExtensionOID.CRL_REASON: "cRLReason", - CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate", - CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", - ExtensionOID.NAME_CONSTRAINTS: "nameConstraints", - ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints", - ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies", - ExtensionOID.POLICY_MAPPINGS: "policyMappings", - ExtensionOID.AUTHORITY_KEY_IDENTIFIER: "authorityKeyIdentifier", - ExtensionOID.POLICY_CONSTRAINTS: "policyConstraints", - ExtensionOID.EXTENDED_KEY_USAGE: "extendedKeyUsage", - ExtensionOID.FRESHEST_CRL: "freshestCRL", - ExtensionOID.INHIBIT_ANY_POLICY: "inhibitAnyPolicy", - ExtensionOID.ISSUING_DISTRIBUTION_POINT: "issuingDistributionPoint", - ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess", - ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess", - ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck", - ExtensionOID.CRL_NUMBER: "cRLNumber", - ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator", - ExtensionOID.TLS_FEATURE: "TLSFeature", - AuthorityInformationAccessOID.OCSP: "OCSP", - AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers", - SubjectInformationAccessOID.CA_REPOSITORY: "caRepository", - CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps", - CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice", - OCSPExtensionOID.NONCE: "OCSPNonce", - AttributeOID.CHALLENGE_PASSWORD: "challengePassword", -} diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__init__.py deleted file mode 100644 index 7fb0fb4a..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.asn1.asn1 import ( - TLV, - BitString, - Default, - Explicit, - GeneralizedTime, - IA5String, - Implicit, - Null, - PrintableString, - SetOf, - Size, - UTCTime, - Variant, - decode_der, - encode_der, - sequence, - set, - value_set, -) - -__all__ = [ - "TLV", - "BitString", - "Default", - "Explicit", - "GeneralizedTime", - "IA5String", - "Implicit", - "Null", - "PrintableString", - "SetOf", - "Size", - "UTCTime", - "Variant", - "decode_der", - "encode_der", - "sequence", - "set", - "value_set", -] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/asn1.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/asn1.py deleted file mode 100644 index 62d38110..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/asn1/asn1.py +++ /dev/null @@ -1,533 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import builtins -import dataclasses -import enum -import sys -import types -import typing - -if sys.version_info < (3, 11): - import typing_extensions - - LiteralString = typing_extensions.LiteralString -else: - LiteralString = typing.LiteralString - -from cryptography.hazmat.bindings._rust import declarative_asn1 -from cryptography.hazmat.bindings._rust import x509 as rust_x509 - -if sys.version_info < (3, 10): - NoneType = type(None) -else: - NoneType = types.NoneType # type: ignore[nonetype-type] - -T = typing.TypeVar("T", covariant=True) -U = typing.TypeVar("U") -Tag = typing.TypeVar("Tag", bound=LiteralString) - - -@dataclasses.dataclass(frozen=True) -class Variant(typing.Generic[U, Tag]): - """ - A tagged variant for CHOICE fields with the same underlying type. - - Use this when you have multiple CHOICE alternatives with the same type - and need to distinguish between them: - - foo: ( - Annotated[Variant[int, typing.Literal["IntA"]], Implicit(0)] - | Annotated[Variant[int, typing.Literal["IntB"]], Implicit(1)] - ) - - Usage: - example = Example(foo=Variant(5, "IntA")) - decoded.foo.value # The int value - decoded.foo.tag # "IntA" or "IntB" - """ - - value: U - tag: str - - -decode_der = declarative_asn1.decode_der -encode_der = declarative_asn1.encode_der - - -_X509_TYPES = ( - rust_x509.Certificate, - rust_x509.CertificateSigningRequest, - rust_x509.CertificateRevocationList, -) - - -def _check_x509_field_annotations( - field_type: typing.Any, - annotation: declarative_asn1.Annotation, - field_name: str, -) -> None: - if field_type in _X509_TYPES and isinstance(annotation.encoding, Implicit): - raise TypeError( - f"field '{field_name}' has an IMPLICIT annotation, but " - "IMPLICIT annotations are not supported for X.509 types." - ) - - -def _is_union(field_type: type) -> bool: - # NOTE: types.UnionType for `T | U`, typing.Union for `Union[T, U]`. - # TODO: Drop the `hasattr()` once the minimum supported Python version - # is >= 3.10. - union_types = ( - (types.UnionType, typing.Union) - if hasattr(types, "UnionType") - else (typing.Union,) - ) - return typing.get_origin(field_type) in union_types - - -def _resolve_type_aliases(field_type: typing.Any) -> typing.Any: - # Recursively resolve PEP 695 (`type X = ...`) type aliases (Python - # 3.12+) to their underlying value, so that the rest of the - # normalization logic never encounters an alias. Aliases can refer - # to other aliases and can appear at any level of nesting (e.g. - # inside `Annotated[...]`, unions, or `list[...]`). - if sys.version_info < (3, 12): - return field_type - - while isinstance(field_type, typing.TypeAliasType): - field_type = field_type.__value__ - - args = typing.get_args(field_type) - resolved_args = tuple(_resolve_type_aliases(arg) for arg in args) - if resolved_args == args: - # No aliases anywhere inside: return the type unchanged. - return field_type - - if _is_union(field_type): - # `X | Y` unions can't be rebuilt through their origin like - # other generics below: `typing.get_origin` returns - # `types.UnionType` for them, which is only subscriptable on - # Python 3.14+. Rebuilding through `typing.Union` also - # flattens any nested union introduced by an alias of a union - # (e.g. `Time | int` where `type Time = UTCTime | - # GeneralizedTime`), just like `typing.Union` would have done - # if the alias had been written inline. - return typing.Union[resolved_args] - - # An alias appeared inside a generic (e.g. `Annotated[Time, ...]`, - # `list[MyInt]`, or `SetOf[MyInt]`): re-parameterize the generic - # with the resolved arguments. Subscripting with a tuple is - # equivalent to subscripting with multiple arguments. - return typing.get_origin(field_type)[resolved_args] - - -def _extract_annotation( - metadata: tuple, field_name: str -) -> declarative_asn1.Annotation: - default = None - encoding = None - size = None - for raw_annotation in metadata: - if isinstance(raw_annotation, Default): - if default is not None: - raise TypeError( - f"multiple DEFAULT annotations found in field " - f"'{field_name}'" - ) - default = raw_annotation.value - elif isinstance(raw_annotation, declarative_asn1.Encoding): - if encoding is not None: - raise TypeError( - f"multiple IMPLICIT/EXPLICIT annotations found in field " - f"'{field_name}'" - ) - encoding = raw_annotation - elif isinstance(raw_annotation, declarative_asn1.Size): - if size is not None: - raise TypeError( - f"multiple SIZE annotations found in field '{field_name}'" - ) - size = raw_annotation - else: - raise TypeError(f"unsupported annotation: {raw_annotation}") - - return declarative_asn1.Annotation( - default=default, encoding=encoding, size=size - ) - - -def _normalize_field_type( - field_type: typing.Any, field_name: str -) -> declarative_asn1.AnnotatedType: - field_type = _resolve_type_aliases(field_type) - - # Strip the `Annotated[...]` off, and populate the annotation - # from it if it exists. - if typing.get_origin(field_type) is typing.Annotated: - annotation = _extract_annotation(field_type.__metadata__, field_name) - field_type, *_ = typing.get_args(field_type) - else: - annotation = declarative_asn1.Annotation() - - if annotation.size is not None and ( - typing.get_origin(field_type) not in (builtins.list, SetOf) - and field_type - not in ( - builtins.bytes, - builtins.str, - BitString, - IA5String, - PrintableString, - ) - ): - raise TypeError( - f"field '{field_name}' has a SIZE annotation, but SIZE " - "annotations are only supported for fields of types: " - "[SEQUENCE OF, SET OF, BIT STRING, OCTET STRING, UTF8String, " - "PrintableString, IA5String]" - ) - - if field_type is TLV: - if isinstance(annotation.encoding, Implicit): - raise TypeError( - f"field '{field_name}' has an IMPLICIT annotation, but " - "IMPLICIT annotations are not supported for TLV types." - ) - elif annotation.default is not None: - raise TypeError( - f"field '{field_name}' has a DEFAULT annotation, but " - "DEFAULT annotations are not supported for TLV types." - ) - - _check_x509_field_annotations(field_type, annotation, field_name) - - if hasattr(field_type, "__asn1_root__"): - root_type = field_type.__asn1_root__ - if not isinstance( - root_type, - ( - declarative_asn1.Type.Sequence, - declarative_asn1.Type.Set, - declarative_asn1.Type.ValueSet, - ), - ): - raise TypeError(f"unsupported root type: {root_type}") - return declarative_asn1.AnnotatedType( - typing.cast(declarative_asn1.Type, root_type), annotation - ) - elif _is_union(field_type): - union_args = typing.get_args(field_type) - if len(union_args) == 2 and NoneType in union_args: - # A Union between a type and None is an OPTIONAL - optional_type = ( - union_args[0] if union_args[1] is type(None) else union_args[1] - ) - if optional_type is TLV: - raise TypeError( - "optional TLV types (`TLV | None`) are not " - "currently supported" - ) - # For optional types, the annotation is associated with the - # union, so we check it against the inner type here. - _check_x509_field_annotations( - optional_type, annotation, field_name - ) - annotated_type = _normalize_field_type(optional_type, field_name) - - if not annotated_type.annotation.is_empty(): - raise TypeError( - "optional (`X | None`) types cannot have `X` " - "annotated: annotations must apply to the union " - "(i.e: `Annotated[X | None, annotation]`)" - ) - - if annotation.default is not None: - raise TypeError( - "optional (`X | None`) types should not have a DEFAULT " - "annotation" - ) - - rust_field_type = declarative_asn1.Type.Option(annotated_type) - - else: - # Otherwise, the Union is a CHOICE - if isinstance(annotation.encoding, Implicit): - # CHOICEs cannot be IMPLICIT. See X.680 section 31.2.9. - raise TypeError( - "CHOICE (`X | Y | ...`) types should not have an IMPLICIT " - "annotation" - ) - variants = [ - _type_to_variant(arg, field_name) - for arg in union_args - if arg is not type(None) - ] - - # Union types should either be all Variants - # (`Variant[..] | Variant[..] | etc`) or all non Variants - are_union_types_tagged = variants[0].tag_name is not None - if any( - (v.tag_name is not None) != are_union_types_tagged - for v in variants - ): - raise TypeError( - "When using `asn1.Variant` in a union, all the other " - "types in the union must also be `asn1.Variant`" - ) - - if are_union_types_tagged: - tags = {v.tag_name for v in variants} - if len(variants) != len(tags): - raise TypeError( - "When using `asn1.Variant` in a union, the tags used " - "must be unique" - ) - - rust_choice_type = declarative_asn1.Type.Choice(variants) - # If None is part of the union types, this is an OPTIONAL CHOICE - rust_field_type = ( - declarative_asn1.Type.Option( - declarative_asn1.AnnotatedType( - rust_choice_type, declarative_asn1.Annotation() - ) - ) - if NoneType in union_args - else rust_choice_type - ) - - elif typing.get_origin(field_type) is builtins.list: - inner_type = _normalize_field_type( - typing.get_args(field_type)[0], field_name - ) - rust_field_type = declarative_asn1.Type.SequenceOf(inner_type) - elif typing.get_origin(field_type) is SetOf: - inner_type = _normalize_field_type( - typing.get_args(field_type)[0], field_name - ) - rust_field_type = declarative_asn1.Type.SetOf(inner_type) - else: - rust_field_type = declarative_asn1.non_root_python_to_rust(field_type) - - return declarative_asn1.AnnotatedType(rust_field_type, annotation) - - -# Convert a type to a Variant. Used with types inside Union -# annotations (T1, T2, etc in `Union[T1, T2, ...]`). -def _type_to_variant( - t: typing.Any, field_name: str -) -> declarative_asn1.Variant: - is_annotated = typing.get_origin(t) is typing.Annotated - inner_type = typing.get_args(t)[0] if is_annotated else t - - # Check if this is a Variant[T, Tag] type - if typing.get_origin(inner_type) is Variant: - value_type, tag_literal = typing.get_args(inner_type) - if typing.get_origin(tag_literal) is not typing.Literal: - raise TypeError( - "When using `asn1.Variant` in a type annotation, the second " - "type parameter must be a `typing.Literal` type. E.g: " - '`Variant[int, typing.Literal["MyInt"]]`.' - ) - tag_name = typing.get_args(tag_literal)[0] - - if hasattr(value_type, "__asn1_root__"): - rust_type = value_type.__asn1_root__ - else: - rust_type = declarative_asn1.non_root_python_to_rust(value_type) - - if is_annotated: - ann_type = declarative_asn1.AnnotatedType( - rust_type, - _extract_annotation(t.__metadata__, field_name), - ) - else: - ann_type = declarative_asn1.AnnotatedType( - rust_type, - declarative_asn1.Annotation(), - ) - - return declarative_asn1.Variant(Variant, ann_type, tag_name) - else: - # Plain type (not a tagged Variant) - return declarative_asn1.Variant( - inner_type, - _normalize_field_type(t, field_name), - None, - ) - - -def _annotate_fields( - raw_fields: dict[str, type], -) -> dict[str, declarative_asn1.AnnotatedType]: - fields = {} - for field_name, field_type in raw_fields.items(): - # Recursively normalize the field type into something that the - # Rust code can understand. - annotated_field_type = _normalize_field_type(field_type, field_name) - fields[field_name] = annotated_field_type - - return fields - - -def _register_asn1_sequence(cls: type[U]) -> None: - raw_fields = typing.get_type_hints(cls, include_extras=True) - root = declarative_asn1.Type.Sequence(cls, _annotate_fields(raw_fields)) - - setattr(cls, "__asn1_root__", root) - - -def _register_asn1_set(cls: type[U]) -> None: - raw_fields = typing.get_type_hints(cls, include_extras=True) - root = declarative_asn1.Type.Set(cls, _annotate_fields(raw_fields)) - - setattr(cls, "__asn1_root__", root) - - -# Due to https://github.com/python/mypy/issues/19731, we can't define an alias -# for `dataclass_transform` that conditionally points to `typing` or -# `typing_extensions` depending on the Python version. We work around it by -# making the whole decorated class conditional on the Python version. -if sys.version_info < (3, 11): - - @typing_extensions.dataclass_transform(kw_only_default=True) - def sequence(cls: type[U]) -> type[U]: - # We use `dataclasses.dataclass` to add an __init__ method - # to the class with keyword-only parameters. - if sys.version_info >= (3, 10): - dataclass_cls = dataclasses.dataclass( - repr=False, - eq=False, - # `match_args` was added in Python 3.10 and defaults - # to True - match_args=False, - # `kw_only` was added in Python 3.10 and defaults to - # False - kw_only=True, - )(cls) - else: - dataclass_cls = dataclasses.dataclass( - repr=False, - eq=False, - )(cls) - _register_asn1_sequence(dataclass_cls) - return dataclass_cls - - @typing_extensions.dataclass_transform(kw_only_default=True) - def set(cls: type[U]) -> type[U]: - # We use `dataclasses.dataclass` to add an __init__ method - # to the class with keyword-only parameters. - if sys.version_info >= (3, 10): - dataclass_cls = dataclasses.dataclass( - repr=False, - eq=False, - # `match_args` was added in Python 3.10 and defaults - # to True - match_args=False, - # `kw_only` was added in Python 3.10 and defaults to - # False - kw_only=True, - )(cls) - else: - dataclass_cls = dataclasses.dataclass( - repr=False, - eq=False, - )(cls) - _register_asn1_set(dataclass_cls) - return dataclass_cls - -else: - - @typing.dataclass_transform(kw_only_default=True) - def sequence(cls: type[U]) -> type[U]: - # Only add an __init__ method, with keyword-only - # parameters. - dataclass_cls = dataclasses.dataclass( - repr=False, - eq=False, - match_args=False, - kw_only=True, - )(cls) - _register_asn1_sequence(dataclass_cls) - return dataclass_cls - - @typing.dataclass_transform(kw_only_default=True) - def set(cls: type[U]) -> type[U]: - # Only add an __init__ method, with keyword-only - # parameters. - dataclass_cls = dataclasses.dataclass( - repr=False, - eq=False, - match_args=False, - kw_only=True, - )(cls) - _register_asn1_set(dataclass_cls) - return dataclass_cls - - -def value_set( - value_type: type, -) -> typing.Callable[[type[U]], type[U]]: - """ - A class decorator that registers an `enum.Enum` subclass as an - ASN.1 value set of the given underlying type. All the member - values must be instances of `value_type`. Members are encoded as - their value; decoding fails if the decoded value does not match - any member. - """ - rust_type = declarative_asn1.non_root_python_to_rust(value_type) - - def decorator(cls: type[U]) -> type[U]: - if not issubclass(cls, enum.Enum): - raise TypeError( - "value sets can only be defined from enum.Enum subclasses" - ) - members = list(cls) - if not members: - raise TypeError( - f"value set '{cls.__name__}' must have at least one member" - ) - for member in members: - if not isinstance(member.value, value_type): - raise TypeError( - f"member '{member.name}' of value set '{cls.__name__}' " - f"must have a value of type " - f"'{value_type.__name__}', got: " - f"'{type(member.value).__name__}'" - ) - inner = declarative_asn1.AnnotatedType( - rust_type, declarative_asn1.Annotation() - ) - # Map from member value to member, used for O(1) lookups when - # decoding. This requires the member values to be hashable. - value_map = {member.value: member for member in members} - root = declarative_asn1.Type.ValueSet(cls, inner, value_map) - - setattr(cls, "__asn1_root__", root) - return cls - - return decorator - - -# TODO: replace with `Default[U]` once the min Python version is >= 3.12 -@dataclasses.dataclass(frozen=True) -class Default(typing.Generic[U]): - value: U - - -SetOf = declarative_asn1.SetOf - -Explicit = declarative_asn1.Encoding.Explicit -Implicit = declarative_asn1.Encoding.Implicit -Size = declarative_asn1.Size - -PrintableString = declarative_asn1.PrintableString -IA5String = declarative_asn1.IA5String -UTCTime = declarative_asn1.UTCTime -GeneralizedTime = declarative_asn1.GeneralizedTime -BitString = declarative_asn1.BitString -TLV = declarative_asn1.Tlv -Null = declarative_asn1.Null diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py deleted file mode 100644 index b4400aa0..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from typing import Any - - -def default_backend() -> Any: - from cryptography.hazmat.backends.openssl.backend import backend - - return backend diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py deleted file mode 100644 index 51b04476..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.backends.openssl.backend import backend - -__all__ = ["backend"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py deleted file mode 100644 index f37b0cf7..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/backends/openssl/backend.py +++ /dev/null @@ -1,314 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.bindings.openssl import binding -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding -from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils -from cryptography.hazmat.primitives.asymmetric.padding import ( - MGF1, - OAEP, - PSS, - PKCS1v15, -) -from cryptography.hazmat.primitives.ciphers import ( - CipherAlgorithm, -) -from cryptography.hazmat.primitives.ciphers.algorithms import ( - AES, -) -from cryptography.hazmat.primitives.ciphers.modes import ( - CBC, - Mode, -) - - -class Backend: - """ - OpenSSL API binding interfaces. - """ - - name = "openssl" - - # TripleDES encryption is disallowed/deprecated throughout 2023 in - # FIPS 140-3. To keep it simple we denylist any use of TripleDES (TDEA). - _fips_ciphers = (AES,) - # Sometimes SHA1 is still permissible. That logic is contained - # within the various *_supported methods. - _fips_hashes = ( - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - hashes.SHA512_224, - hashes.SHA512_256, - hashes.SHA3_224, - hashes.SHA3_256, - hashes.SHA3_384, - hashes.SHA3_512, - hashes.SHAKE128, - hashes.SHAKE256, - ) - _fips_ecdh_curves = ( - ec.SECP224R1, - ec.SECP256R1, - ec.SECP384R1, - ec.SECP521R1, - ) - _fips_rsa_min_key_size = 2048 - _fips_rsa_min_public_exponent = 65537 - _fips_dsa_min_modulus = 1 << 2048 - _fips_dh_min_key_size = 2048 - _fips_dh_min_modulus = 1 << _fips_dh_min_key_size - - def __init__(self) -> None: - self._binding = binding.Binding() - self._ffi = self._binding.ffi - self._lib = self._binding.lib - self._fips_enabled = rust_openssl.is_fips_enabled() - - def __repr__(self) -> str: - return ( - f"" - ) - - def openssl_assert(self, ok: bool) -> None: - return binding._openssl_assert(ok) - - def _enable_fips(self) -> None: - # This function enables FIPS mode for OpenSSL 3.0.0 on installs that - # have the FIPS provider installed properly. - rust_openssl.enable_fips(rust_openssl._providers) - assert rust_openssl.is_fips_enabled() - self._fips_enabled = rust_openssl.is_fips_enabled() - - def openssl_version_text(self) -> str: - """ - Friendly string name of the loaded OpenSSL library. This is not - necessarily the same version as it was compiled against. - - Example: OpenSSL 3.2.1 30 Jan 2024 - """ - return rust_openssl.openssl_version_text() - - def openssl_version_number(self) -> int: - return rust_openssl.openssl_version() - - def hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - if self._fips_enabled and not isinstance(algorithm, self._fips_hashes): - return False - - return rust_openssl.hashes.hash_supported(algorithm) - - def signature_hash_supported( - self, algorithm: hashes.HashAlgorithm - ) -> bool: - # Dedicated check for hashing algorithm use in message digest for - # signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption). - if self._fips_enabled and isinstance(algorithm, hashes.SHA1): - return False - return self.hash_supported(algorithm) - - def scrypt_supported(self) -> bool: - if self._fips_enabled: - return False - else: - return hasattr(rust_openssl.kdf.Scrypt, "derive") - - def argon2_supported(self) -> bool: - if self._fips_enabled: - return False - else: - return hasattr(rust_openssl.kdf.Argon2id, "derive") - - def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - # FIPS mode still allows SHA1 for HMAC - if self._fips_enabled and isinstance(algorithm, hashes.SHA1): - return True - if rust_openssl.CRYPTOGRAPHY_IS_AWSLC: - return isinstance( - algorithm, - ( - hashes.MD5, - hashes.SHA1, - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - hashes.SHA512_224, - hashes.SHA512_256, - ), - ) - return self.hash_supported(algorithm) - - def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool: - if self._fips_enabled: - # FIPS mode requires AES. TripleDES is disallowed/deprecated in - # FIPS 140-3. - if not isinstance(cipher, self._fips_ciphers): - return False - - return rust_openssl.ciphers.cipher_supported(cipher, mode) - - def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - return self.hmac_supported(algorithm) - - def _consume_errors(self) -> list[rust_openssl.OpenSSLError]: - return rust_openssl.capture_error_stack() - - def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - if self._fips_enabled and isinstance(algorithm, hashes.SHA1): - return False - - return isinstance( - algorithm, - ( - hashes.SHA1, - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - ), - ) - - def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool: - if isinstance(padding, PKCS1v15): - return True - elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1): - # FIPS 186-4 only allows salt length == digest length for PSS - # It is technically acceptable to set an explicit salt length - # equal to the digest length and this will incorrectly fail, but - # since we don't do that in the tests and this method is - # private, we'll ignore that until we need to do otherwise. - if ( - self._fips_enabled - and padding._salt_length != PSS.DIGEST_LENGTH - ): - return False - return self.hash_supported(padding._mgf._algorithm) - elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1): - return self._oaep_hash_supported( - padding._mgf._algorithm - ) and self._oaep_hash_supported(padding._algorithm) - else: - return False - - def rsa_encryption_supported(self, padding: AsymmetricPadding) -> bool: - if self._fips_enabled and isinstance(padding, PKCS1v15): - return False - else: - return self.rsa_padding_supported(padding) - - def dsa_supported(self) -> bool: - return ( - not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - and not self._fips_enabled - ) - - def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - if not self.dsa_supported(): - return False - return self.signature_hash_supported(algorithm) - - def cmac_algorithm_supported(self, algorithm) -> bool: - return self.cipher_supported( - algorithm, CBC(b"\x00" * algorithm.block_size) - ) - - def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool: - if self._fips_enabled and not isinstance( - curve, self._fips_ecdh_curves - ): - return False - - return rust_openssl.ec.curve_supported(curve) - - def elliptic_curve_signature_algorithm_supported( - self, - signature_algorithm: ec.EllipticCurveSignatureAlgorithm, - curve: ec.EllipticCurve, - ) -> bool: - # We only support ECDSA right now. - if not isinstance(signature_algorithm, ec.ECDSA): - return False - - return self.elliptic_curve_supported(curve) and ( - isinstance(signature_algorithm.algorithm, asym_utils.Prehashed) - or self.hash_supported(signature_algorithm.algorithm) - ) - - def elliptic_curve_exchange_algorithm_supported( - self, algorithm: ec.ECDH, curve: ec.EllipticCurve - ) -> bool: - return self.elliptic_curve_supported(curve) and isinstance( - algorithm, ec.ECDH - ) - - def dh_supported(self) -> bool: - return ( - not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - and not rust_openssl.CRYPTOGRAPHY_IS_AWSLC - ) - - def dh_x942_serialization_supported(self) -> bool: - return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 - - def x25519_supported(self) -> bool: - return not self._fips_enabled - - def x448_supported(self) -> bool: - if self._fips_enabled: - return False - return ( - not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL - and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - and not rust_openssl.CRYPTOGRAPHY_IS_AWSLC - ) - - def mlkem_supported(self) -> bool: - return ( - rust_openssl.CRYPTOGRAPHY_IS_AWSLC - or rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - or rust_openssl.CRYPTOGRAPHY_OPENSSL_350_OR_GREATER - ) - - def mldsa_supported(self) -> bool: - return ( - rust_openssl.CRYPTOGRAPHY_IS_AWSLC - or rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - or rust_openssl.CRYPTOGRAPHY_OPENSSL_350_OR_GREATER - ) - - def ed25519_supported(self) -> bool: - return True - - def ed448_supported(self) -> bool: - return ( - not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL - and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - and not rust_openssl.CRYPTOGRAPHY_IS_AWSLC - ) - - def ecdsa_deterministic_supported(self) -> bool: - return ( - rust_openssl.CRYPTOGRAPHY_OPENSSL_320_OR_GREATER - and not self._fips_enabled - ) - - def poly1305_supported(self) -> bool: - if rust_openssl.CRYPTOGRAPHY_IS_AWSLC: - return True - return not self._fips_enabled - - def pkcs7_supported(self) -> bool: - return True - - -backend = Backend() diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py deleted file mode 100644 index b5093362..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so deleted file mode 100755 index 9a8671c8..00000000 Binary files a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust.abi3.so and /dev/null differ diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi deleted file mode 100644 index c3148f12..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi +++ /dev/null @@ -1,67 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import padding -from cryptography.hazmat.primitives._serialization import ( - KeySerializationEncryptionBuilder, -) -from cryptography.utils import Buffer - -class PKCS7PaddingContext(padding.PaddingContext): - def __init__(self, block_size: int) -> None: ... - def update(self, data: Buffer) -> bytes: ... - def finalize(self) -> bytes: ... - -class ANSIX923PaddingContext(padding.PaddingContext): - def __init__(self, block_size: int) -> None: ... - def update(self, data: Buffer) -> bytes: ... - def finalize(self) -> bytes: ... - -class PKCS7UnpaddingContext(padding.PaddingContext): - def __init__(self, block_size: int) -> None: ... - def update(self, data: Buffer) -> bytes: ... - def finalize(self) -> bytes: ... - -class ANSIX923UnpaddingContext(padding.PaddingContext): - def __init__(self, block_size: int) -> None: ... - def update(self, data: Buffer) -> bytes: ... - def finalize(self) -> bytes: ... - -class Encoding: - PEM: typing.ClassVar[Encoding] - DER: typing.ClassVar[Encoding] - OpenSSH: typing.ClassVar[Encoding] - Raw: typing.ClassVar[Encoding] - X962: typing.ClassVar[Encoding] - SMIME: typing.ClassVar[Encoding] - -class PrivateFormat: - PKCS8: typing.ClassVar[PrivateFormat] - TraditionalOpenSSL: typing.ClassVar[PrivateFormat] - Raw: typing.ClassVar[PrivateFormat] - OpenSSH: typing.ClassVar[PrivateFormat] - PKCS12: typing.ClassVar[PrivateFormat] - def encryption_builder(self) -> KeySerializationEncryptionBuilder: ... - -class PublicFormat: - SubjectPublicKeyInfo: typing.ClassVar[PublicFormat] - PKCS1: typing.ClassVar[PublicFormat] - OpenSSH: typing.ClassVar[PublicFormat] - Raw: typing.ClassVar[PublicFormat] - CompressedPoint: typing.ClassVar[PublicFormat] - UncompressedPoint: typing.ClassVar[PublicFormat] - -class ParameterFormat: - PKCS3: typing.ClassVar[ParameterFormat] - -class ObjectIdentifier: - def __init__(self, value: str) -> None: ... - @property - def dotted_string(self) -> str: ... - @property - def _name(self) -> str: ... - -T = typing.TypeVar("T") diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi deleted file mode 100644 index 3d4ea4eb..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi +++ /dev/null @@ -1,8 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -lib: typing.Any -ffi: typing.Any diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi deleted file mode 100644 index 3b5f208e..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi +++ /dev/null @@ -1,7 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -def decode_dss_signature(signature: bytes) -> tuple[int, int]: ... -def encode_dss_signature(r: int, s: int) -> bytes: ... -def parse_spki_for_data(data: bytes) -> bytes: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi deleted file mode 100644 index 66fc0658..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi +++ /dev/null @@ -1,133 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import datetime -import typing - -T = typing.TypeVar("T") - -def decode_der(cls: type[T], value: bytes) -> T: ... -def encode_der(value: typing.Any) -> bytes: ... -def non_root_python_to_rust(cls: type) -> Type: ... - -# Type is a Rust enum with tuple variants. For now, we express the type -# annotations like this: -class Type: - Sequence: typing.ClassVar[type] - SequenceOf: typing.ClassVar[type] - Set: typing.ClassVar[type] - SetOf: typing.ClassVar[type] - Option: typing.ClassVar[type] - Choice: typing.ClassVar[type] - ValueSet: typing.ClassVar[type] - PyBool: typing.ClassVar[type] - PyInt: typing.ClassVar[type] - PyBytes: typing.ClassVar[type] - PyStr: typing.ClassVar[type] - -class Annotation: - default: typing.Any | None - encoding: Encoding | None - size: Size | None - def __new__( - cls, - default: typing.Any | None = None, - encoding: Encoding | None = None, - size: Size | None = None, - ) -> Annotation: ... - def is_empty(self) -> bool: ... - -# Encoding is a Rust enum with tuple variants. For now, we express the type -# annotations like this: -class Encoding: - Implicit: typing.ClassVar[type] - Explicit: typing.ClassVar[type] - -class Size: - min: int - max: int | None - - def __new__(cls, min: int, max: int | None) -> Size: ... - @staticmethod - def exact(n: int) -> Size: ... - -class AnnotatedType: - inner: Type - annotation: Annotation - - def __new__(cls, inner: Type, annotation: Annotation) -> AnnotatedType: ... - -class AnnotatedTypeObject: - annotated_type: AnnotatedType - value: typing.Any - - def __new__( - cls, annotated_type: AnnotatedType, value: typing.Any - ) -> AnnotatedTypeObject: ... - -class Variant: - python_class: type - ann_type: AnnotatedType - tag_name: str | None - - def __new__( - cls, - python_class: type, - ann_type: AnnotatedType, - tag_name: str | None, - ) -> Variant: ... - -class PrintableString: - def __new__(cls, inner: str) -> PrintableString: ... - def __repr__(self) -> str: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - def as_str(self) -> str: ... - -class IA5String: - def __new__(cls, inner: str) -> IA5String: ... - def __repr__(self) -> str: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - def as_str(self) -> str: ... - -class UTCTime: - def __new__(cls, inner: datetime.datetime) -> UTCTime: ... - def __repr__(self) -> str: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - def as_datetime(self) -> datetime.datetime: ... - -class GeneralizedTime: - def __new__(cls, inner: datetime.datetime) -> GeneralizedTime: ... - def __repr__(self) -> str: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - def as_datetime(self) -> datetime.datetime: ... - -class BitString: - def __new__(cls, data: bytes, padding_bits: int) -> BitString: ... - def __repr__(self) -> str: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - def as_bytes(self) -> bytes: ... - def padding_bits(self) -> int: ... - -class Tlv: - @property - def tag_bytes(self) -> bytes: ... - @property - def data(self) -> memoryview: ... - def parse(self, cls: type): ... - -class SetOf(typing.Generic[T]): - def __new__(cls, inner: list[T]) -> SetOf[T]: ... - def as_list(self) -> list[T]: ... - def __eq__(self, other: object) -> bool: ... - def __repr__(self) -> str: ... - -class Null: - def __new__(cls) -> Null: ... - def __repr__(self) -> str: ... - def __eq__(self, other: object) -> bool: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi deleted file mode 100644 index 09f46b1e..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi +++ /dev/null @@ -1,17 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -class _Reasons: - BACKEND_MISSING_INTERFACE: _Reasons - UNSUPPORTED_HASH: _Reasons - UNSUPPORTED_CIPHER: _Reasons - UNSUPPORTED_PADDING: _Reasons - UNSUPPORTED_MGF: _Reasons - UNSUPPORTED_PUBLIC_KEY_ALGORITHM: _Reasons - UNSUPPORTED_ELLIPTIC_CURVE: _Reasons - UNSUPPORTED_SERIALIZATION: _Reasons - UNSUPPORTED_X509: _Reasons - UNSUPPORTED_EXCHANGE_ALGORITHM: _Reasons - UNSUPPORTED_DIFFIE_HELLMAN: _Reasons - UNSUPPORTED_MAC: _Reasons diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi deleted file mode 100644 index 103e96c1..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi +++ /dev/null @@ -1,117 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import datetime -from collections.abc import Iterator - -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes -from cryptography.x509 import ocsp - -class OCSPRequest: - @property - def issuer_key_hash(self) -> bytes: ... - @property - def issuer_name_hash(self) -> bytes: ... - @property - def hash_algorithm(self) -> hashes.HashAlgorithm: ... - @property - def serial_number(self) -> int: ... - def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... - @property - def extensions(self) -> x509.Extensions: ... - -class OCSPResponse: - @property - def responses(self) -> Iterator[OCSPSingleResponse]: ... - @property - def response_status(self) -> ocsp.OCSPResponseStatus: ... - @property - def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... - @property - def signature_hash_algorithm( - self, - ) -> hashes.HashAlgorithm | None: ... - @property - def signature(self) -> bytes: ... - @property - def tbs_response_bytes(self) -> bytes: ... - @property - def certificates(self) -> list[x509.Certificate]: ... - @property - def responder_key_hash(self) -> bytes | None: ... - @property - def responder_name(self) -> x509.Name | None: ... - @property - def produced_at(self) -> datetime.datetime: ... - @property - def produced_at_utc(self) -> datetime.datetime: ... - @property - def certificate_status(self) -> ocsp.OCSPCertStatus: ... - @property - def revocation_time(self) -> datetime.datetime | None: ... - @property - def revocation_time_utc(self) -> datetime.datetime | None: ... - @property - def revocation_reason(self) -> x509.ReasonFlags | None: ... - @property - def this_update(self) -> datetime.datetime: ... - @property - def this_update_utc(self) -> datetime.datetime: ... - @property - def next_update(self) -> datetime.datetime | None: ... - @property - def next_update_utc(self) -> datetime.datetime | None: ... - @property - def issuer_key_hash(self) -> bytes: ... - @property - def issuer_name_hash(self) -> bytes: ... - @property - def hash_algorithm(self) -> hashes.HashAlgorithm: ... - @property - def serial_number(self) -> int: ... - @property - def extensions(self) -> x509.Extensions: ... - @property - def single_extensions(self) -> x509.Extensions: ... - def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... - -class OCSPSingleResponse: - @property - def certificate_status(self) -> ocsp.OCSPCertStatus: ... - @property - def revocation_time(self) -> datetime.datetime | None: ... - @property - def revocation_time_utc(self) -> datetime.datetime | None: ... - @property - def revocation_reason(self) -> x509.ReasonFlags | None: ... - @property - def this_update(self) -> datetime.datetime: ... - @property - def this_update_utc(self) -> datetime.datetime: ... - @property - def next_update(self) -> datetime.datetime | None: ... - @property - def next_update_utc(self) -> datetime.datetime | None: ... - @property - def issuer_key_hash(self) -> bytes: ... - @property - def issuer_name_hash(self) -> bytes: ... - @property - def hash_algorithm(self) -> hashes.HashAlgorithm: ... - @property - def serial_number(self) -> int: ... - -def load_der_ocsp_request(data: bytes) -> ocsp.OCSPRequest: ... -def load_der_ocsp_response(data: bytes) -> ocsp.OCSPResponse: ... -def create_ocsp_request( - builder: ocsp.OCSPRequestBuilder, -) -> ocsp.OCSPRequest: ... -def create_ocsp_response( - status: ocsp.OCSPResponseStatus, - builder: ocsp.OCSPResponseBuilder | None, - private_key: PrivateKeyTypes | None, - hash_algorithm: hashes.HashAlgorithm | None, -) -> ocsp.OCSPResponse: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi deleted file mode 100644 index 404a300a..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi +++ /dev/null @@ -1,80 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.bindings._rust.openssl import ( - aead, - ciphers, - cmac, - dh, - dsa, - ec, - ed448, - ed25519, - hashes, - hmac, - hpke, - kdf, - keys, - mldsa, - mlkem, - poly1305, - rsa, - x448, - x25519, -) - -__all__ = [ - "aead", - "ciphers", - "cmac", - "dh", - "dsa", - "ec", - "ed448", - "ed25519", - "hashes", - "hmac", - "hpke", - "kdf", - "keys", - "mldsa", - "mlkem", - "openssl_version", - "openssl_version_text", - "poly1305", - "raise_openssl_error", - "rsa", - "x448", - "x25519", -] - -CRYPTOGRAPHY_IS_LIBRESSL: bool -CRYPTOGRAPHY_IS_BORINGSSL: bool -CRYPTOGRAPHY_IS_AWSLC: bool -CRYPTOGRAPHY_OPENSSL_309_OR_GREATER: bool -CRYPTOGRAPHY_OPENSSL_320_OR_GREATER: bool -CRYPTOGRAPHY_OPENSSL_330_OR_GREATER: bool -CRYPTOGRAPHY_OPENSSL_350_OR_GREATER: bool - -class Providers: ... - -_legacy_provider_loaded: bool -_providers: Providers - -def openssl_version() -> int: ... -def openssl_version_text() -> str: ... -def raise_openssl_error() -> typing.NoReturn: ... -def capture_error_stack() -> list[OpenSSLError]: ... -def is_fips_enabled() -> bool: ... -def enable_fips(providers: Providers) -> None: ... - -class OpenSSLError: - @property - def lib(self) -> int: ... - @property - def reason(self) -> int: ... - @property - def reason_text(self) -> bytes: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi deleted file mode 100644 index eb44608a..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi +++ /dev/null @@ -1,189 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from collections.abc import Sequence - -from cryptography.utils import Buffer - -class AESGCM: - def __init__(self, key: Buffer) -> None: ... - @staticmethod - def generate_key(bit_length: int) -> bytes: ... - def encrypt( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - ) -> bytes: ... - def decrypt( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - ) -> bytes: ... - def encrypt_into( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - buf: Buffer, - ) -> int: ... - def decrypt_into( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - buf: Buffer, - ) -> int: ... - -class ChaCha20Poly1305: - def __init__(self, key: Buffer) -> None: ... - @staticmethod - def generate_key() -> bytes: ... - def encrypt( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - ) -> bytes: ... - def encrypt_into( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - buf: Buffer, - ) -> int: ... - def decrypt( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - ) -> bytes: ... - def decrypt_into( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - buf: Buffer, - ) -> int: ... - -class AESCCM: - def __init__(self, key: Buffer, tag_length: int = 16) -> None: ... - @staticmethod - def generate_key(bit_length: int) -> bytes: ... - def encrypt( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - ) -> bytes: ... - def encrypt_into( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - buf: Buffer, - ) -> int: ... - def decrypt( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - ) -> bytes: ... - def decrypt_into( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - buf: Buffer, - ) -> int: ... - -class AESSIV: - def __init__(self, key: Buffer) -> None: ... - @staticmethod - def generate_key(bit_length: int) -> bytes: ... - def encrypt( - self, - data: Buffer, - associated_data: Sequence[Buffer] | None, - ) -> bytes: ... - def encrypt_into( - self, - data: Buffer, - associated_data: Sequence[Buffer] | None, - buf: Buffer, - ) -> int: ... - def decrypt( - self, - data: Buffer, - associated_data: Sequence[Buffer] | None, - ) -> bytes: ... - def decrypt_into( - self, - data: Buffer, - associated_data: Sequence[Buffer] | None, - buf: Buffer, - ) -> int: ... - -class AESOCB3: - def __init__(self, key: Buffer) -> None: ... - @staticmethod - def generate_key(bit_length: int) -> bytes: ... - def encrypt( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - ) -> bytes: ... - def encrypt_into( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - buf: Buffer, - ) -> int: ... - def decrypt( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - ) -> bytes: ... - def decrypt_into( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - buf: Buffer, - ) -> int: ... - -class AESGCMSIV: - def __init__(self, key: Buffer) -> None: ... - @staticmethod - def generate_key(bit_length: int) -> bytes: ... - def encrypt( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - ) -> bytes: ... - def encrypt_into( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - buf: Buffer, - ) -> int: ... - def decrypt( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - ) -> bytes: ... - def decrypt_into( - self, - nonce: Buffer, - data: Buffer, - associated_data: Buffer | None, - buf: Buffer, - ) -> int: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi deleted file mode 100644 index a48fb017..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi +++ /dev/null @@ -1,38 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import ciphers -from cryptography.hazmat.primitives.ciphers import modes - -@typing.overload -def create_encryption_ctx( - algorithm: ciphers.CipherAlgorithm, mode: modes.ModeWithAuthenticationTag -) -> ciphers.AEADEncryptionContext: ... -@typing.overload -def create_encryption_ctx( - algorithm: ciphers.CipherAlgorithm, mode: modes.Mode | None -) -> ciphers.CipherContext: ... -@typing.overload -def create_decryption_ctx( - algorithm: ciphers.CipherAlgorithm, mode: modes.ModeWithAuthenticationTag -) -> ciphers.AEADDecryptionContext: ... -@typing.overload -def create_decryption_ctx( - algorithm: ciphers.CipherAlgorithm, mode: modes.Mode | None -) -> ciphers.CipherContext: ... -def cipher_supported( - algorithm: ciphers.CipherAlgorithm, mode: modes.Mode -) -> bool: ... -def _advance( - ctx: ciphers.AEADEncryptionContext | ciphers.AEADDecryptionContext, n: int -) -> None: ... -def _advance_aad( - ctx: ciphers.AEADEncryptionContext | ciphers.AEADDecryptionContext, n: int -) -> None: ... - -class CipherContext: ... -class AEADEncryptionContext: ... -class AEADDecryptionContext: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi deleted file mode 100644 index 9c03508b..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi +++ /dev/null @@ -1,18 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import ciphers - -class CMAC: - def __init__( - self, - algorithm: ciphers.BlockCipherAlgorithm, - backend: typing.Any = None, - ) -> None: ... - def update(self, data: bytes) -> None: ... - def finalize(self) -> bytes: ... - def verify(self, signature: bytes) -> None: ... - def copy(self) -> CMAC: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi deleted file mode 100644 index 08733d74..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi +++ /dev/null @@ -1,51 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric import dh - -MIN_MODULUS_SIZE: int - -class DHPrivateKey: ... -class DHPublicKey: ... -class DHParameters: ... - -class DHPrivateNumbers: - def __init__(self, x: int, public_numbers: DHPublicNumbers) -> None: ... - def private_key(self, backend: typing.Any = None) -> dh.DHPrivateKey: ... - @property - def x(self) -> int: ... - @property - def public_numbers(self) -> DHPublicNumbers: ... - -class DHPublicNumbers: - def __init__( - self, y: int, parameter_numbers: DHParameterNumbers - ) -> None: ... - def public_key(self, backend: typing.Any = None) -> dh.DHPublicKey: ... - @property - def y(self) -> int: ... - @property - def parameter_numbers(self) -> DHParameterNumbers: ... - -class DHParameterNumbers: - def __init__(self, p: int, g: int, q: int | None = None) -> None: ... - def parameters(self, backend: typing.Any = None) -> dh.DHParameters: ... - @property - def p(self) -> int: ... - @property - def g(self) -> int: ... - @property - def q(self) -> int | None: ... - -def generate_parameters( - generator: int, key_size: int, backend: typing.Any = None -) -> dh.DHParameters: ... -def from_pem_parameters( - data: bytes, backend: typing.Any = None -) -> dh.DHParameters: ... -def from_der_parameters( - data: bytes, backend: typing.Any = None -) -> dh.DHParameters: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi deleted file mode 100644 index 0922a4c4..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi +++ /dev/null @@ -1,41 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric import dsa - -class DSAPrivateKey: ... -class DSAPublicKey: ... -class DSAParameters: ... - -class DSAPrivateNumbers: - def __init__(self, x: int, public_numbers: DSAPublicNumbers) -> None: ... - @property - def x(self) -> int: ... - @property - def public_numbers(self) -> DSAPublicNumbers: ... - def private_key(self, backend: typing.Any = None) -> dsa.DSAPrivateKey: ... - -class DSAPublicNumbers: - def __init__( - self, y: int, parameter_numbers: DSAParameterNumbers - ) -> None: ... - @property - def y(self) -> int: ... - @property - def parameter_numbers(self) -> DSAParameterNumbers: ... - def public_key(self, backend: typing.Any = None) -> dsa.DSAPublicKey: ... - -class DSAParameterNumbers: - def __init__(self, p: int, q: int, g: int) -> None: ... - @property - def p(self) -> int: ... - @property - def q(self) -> int: ... - @property - def g(self) -> int: ... - def parameters(self, backend: typing.Any = None) -> dsa.DSAParameters: ... - -def generate_parameters(key_size: int) -> dsa.DSAParameters: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi deleted file mode 100644 index 5c3b7bf6..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi +++ /dev/null @@ -1,52 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric import ec - -class ECPrivateKey: ... -class ECPublicKey: ... - -class EllipticCurvePrivateNumbers: - def __init__( - self, private_value: int, public_numbers: EllipticCurvePublicNumbers - ) -> None: ... - def private_key( - self, backend: typing.Any = None - ) -> ec.EllipticCurvePrivateKey: ... - @property - def private_value(self) -> int: ... - @property - def public_numbers(self) -> EllipticCurvePublicNumbers: ... - -class EllipticCurvePublicNumbers: - def __init__(self, x: int, y: int, curve: ec.EllipticCurve) -> None: ... - def public_key( - self, backend: typing.Any = None - ) -> ec.EllipticCurvePublicKey: ... - @property - def x(self) -> int: ... - @property - def y(self) -> int: ... - @property - def curve(self) -> ec.EllipticCurve: ... - def __eq__(self, other: object) -> bool: ... - -def curve_supported(curve: ec.EllipticCurve) -> bool: ... -def generate_private_key( - curve: ec.EllipticCurve, backend: typing.Any = None -) -> ec.EllipticCurvePrivateKey: ... -def from_private_numbers( - numbers: ec.EllipticCurvePrivateNumbers, -) -> ec.EllipticCurvePrivateKey: ... -def from_public_numbers( - numbers: ec.EllipticCurvePublicNumbers, -) -> ec.EllipticCurvePublicKey: ... -def from_public_bytes( - curve: ec.EllipticCurve, data: bytes -) -> ec.EllipticCurvePublicKey: ... -def derive_private_key( - private_value: int, curve: ec.EllipticCurve -) -> ec.EllipticCurvePrivateKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi deleted file mode 100644 index f85b3d1b..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import ed25519 -from cryptography.utils import Buffer - -class Ed25519PrivateKey: ... -class Ed25519PublicKey: ... - -def generate_key() -> ed25519.Ed25519PrivateKey: ... -def from_private_bytes(data: Buffer) -> ed25519.Ed25519PrivateKey: ... -def from_public_bytes(data: bytes) -> ed25519.Ed25519PublicKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi deleted file mode 100644 index c8ca0ecb..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import ed448 -from cryptography.utils import Buffer - -class Ed448PrivateKey: ... -class Ed448PublicKey: ... - -def generate_key() -> ed448.Ed448PrivateKey: ... -def from_private_bytes(data: Buffer) -> ed448.Ed448PrivateKey: ... -def from_public_bytes(data: bytes) -> ed448.Ed448PublicKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi deleted file mode 100644 index 106b531c..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi +++ /dev/null @@ -1,30 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import hashes -from cryptography.utils import Buffer - -class Hash(hashes.HashContext): - def __init__( - self, algorithm: hashes.HashAlgorithm, backend: typing.Any = None - ) -> None: ... - @property - def algorithm(self) -> hashes.HashAlgorithm: ... - def update(self, data: Buffer) -> None: ... - def finalize(self) -> bytes: ... - def copy(self) -> Hash: ... - @staticmethod - def hash(algorithm: hashes.HashAlgorithm, data: Buffer) -> bytes: ... - -def hash_supported(algorithm: hashes.HashAlgorithm) -> bool: ... - -class XOFHash: - def __init__(self, algorithm: hashes.ExtendableOutputFunction) -> None: ... - @property - def algorithm(self) -> hashes.ExtendableOutputFunction: ... - def update(self, data: Buffer) -> None: ... - def squeeze(self, length: int) -> bytes: ... - def copy(self) -> XOFHash: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi deleted file mode 100644 index 3883d1b1..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi +++ /dev/null @@ -1,22 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import hashes -from cryptography.utils import Buffer - -class HMAC(hashes.HashContext): - def __init__( - self, - key: Buffer, - algorithm: hashes.HashAlgorithm, - backend: typing.Any = None, - ) -> None: ... - @property - def algorithm(self) -> hashes.HashAlgorithm: ... - def update(self, data: Buffer) -> None: ... - def finalize(self) -> bytes: ... - def verify(self, signature: bytes) -> None: ... - def copy(self) -> HMAC: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi deleted file mode 100644 index 7ea17112..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/hpke.pyi +++ /dev/null @@ -1,109 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import ec, mlkem, x25519 -from cryptography.utils import Buffer - -class KEM: - X25519: KEM - P256: KEM - P384: KEM - P521: KEM - MLKEM768: KEM - MLKEM1024: KEM - MLKEM768_X25519: KEM - MLKEM1024_P384: KEM - def enc_length(self) -> int: ... - -class KDF: - HKDF_SHA256: KDF - HKDF_SHA384: KDF - HKDF_SHA512: KDF - SHAKE128: KDF - SHAKE256: KDF - -class AEAD: - AES_128_GCM: AEAD - AES_256_GCM: AEAD - CHACHA20_POLY1305: AEAD - -class MLKEM768X25519PrivateKey: - def __init__( - self, - mlkem_key: mlkem.MLKEM768PrivateKey, - x25519_key: x25519.X25519PrivateKey, - ) -> None: ... - def public_key(self) -> MLKEM768X25519PublicKey: ... - -class MLKEM768X25519PublicKey: - def __init__( - self, - mlkem_key: mlkem.MLKEM768PublicKey, - x25519_key: x25519.X25519PublicKey, - ) -> None: ... - -class MLKEM1024P384PrivateKey: - def __init__( - self, - mlkem_key: mlkem.MLKEM1024PrivateKey, - p384_key: ec.EllipticCurvePrivateKey, - ) -> None: ... - def public_key(self) -> MLKEM1024P384PublicKey: ... - -class MLKEM1024P384PublicKey: - def __init__( - self, - mlkem_key: mlkem.MLKEM1024PublicKey, - p384_key: ec.EllipticCurvePublicKey, - ) -> None: ... - -class Suite: - def __init__(self, kem: KEM, kdf: KDF, aead: AEAD) -> None: ... - def encrypt( - self, - plaintext: Buffer, - public_key: x25519.X25519PublicKey - | ec.EllipticCurvePublicKey - | mlkem.MLKEM768PublicKey - | mlkem.MLKEM1024PublicKey - | MLKEM768X25519PublicKey - | MLKEM1024P384PublicKey, - info: Buffer | None = None, - ) -> bytes: ... - def decrypt( - self, - ciphertext: Buffer, - private_key: x25519.X25519PrivateKey - | ec.EllipticCurvePrivateKey - | mlkem.MLKEM768PrivateKey - | mlkem.MLKEM1024PrivateKey - | MLKEM768X25519PrivateKey - | MLKEM1024P384PrivateKey, - info: Buffer | None = None, - ) -> bytes: ... - -def _encrypt_with_aad( - suite: Suite, - plaintext: Buffer, - public_key: x25519.X25519PublicKey - | ec.EllipticCurvePublicKey - | mlkem.MLKEM768PublicKey - | mlkem.MLKEM1024PublicKey - | MLKEM768X25519PublicKey - | MLKEM1024P384PublicKey, - info: Buffer | None = None, - aad: Buffer | None = None, -) -> bytes: ... -def _decrypt_with_aad( - suite: Suite, - ciphertext: Buffer, - private_key: x25519.X25519PrivateKey - | ec.EllipticCurvePrivateKey - | mlkem.MLKEM768PrivateKey - | mlkem.MLKEM1024PrivateKey - | MLKEM768X25519PrivateKey - | MLKEM1024P384PrivateKey, - info: Buffer | None = None, - aad: Buffer | None = None, -) -> bytes: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi deleted file mode 100644 index bb37b969..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi +++ /dev/null @@ -1,205 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.hashes import HashAlgorithm -from cryptography.hazmat.primitives.kdf.kbkdf import CounterLocation, Mode -from cryptography.utils import Buffer - -class PBKDF2HMAC: - def __init__( - self, - algorithm: HashAlgorithm, - length: int, - salt: bytes, - iterations: int, - backend: typing.Any = None, - ) -> None: ... - def derive(self, key_material: Buffer) -> bytes: ... - def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - -class Scrypt: - def __init__( - self, - salt: bytes, - length: int, - n: int, - r: int, - p: int, - backend: typing.Any = None, - ) -> None: ... - def derive(self, key_material: Buffer) -> bytes: ... - def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - -class Argon2d: - def __init__( - self, - *, - salt: bytes, - length: int, - iterations: int, - lanes: int, - memory_cost: int, - ad: bytes | None = None, - secret: bytes | None = None, - ) -> None: ... - def derive(self, key_material: bytes) -> bytes: ... - def derive_into(self, key_material: bytes, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - def derive_phc_encoded(self, key_material: bytes) -> str: ... - @classmethod - def verify_phc_encoded( - cls, key_material: bytes, phc_encoded: str, secret: bytes | None = None - ) -> None: ... - -class Argon2i: - def __init__( - self, - *, - salt: bytes, - length: int, - iterations: int, - lanes: int, - memory_cost: int, - ad: bytes | None = None, - secret: bytes | None = None, - ) -> None: ... - def derive(self, key_material: bytes) -> bytes: ... - def derive_into(self, key_material: bytes, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - def derive_phc_encoded(self, key_material: bytes) -> str: ... - @classmethod - def verify_phc_encoded( - cls, key_material: bytes, phc_encoded: str, secret: bytes | None = None - ) -> None: ... - -class Argon2id: - def __init__( - self, - *, - salt: bytes, - length: int, - iterations: int, - lanes: int, - memory_cost: int, - ad: bytes | None = None, - secret: bytes | None = None, - ) -> None: ... - def derive(self, key_material: bytes) -> bytes: ... - def derive_into(self, key_material: bytes, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - def derive_phc_encoded(self, key_material: bytes) -> str: ... - @classmethod - def verify_phc_encoded( - cls, key_material: bytes, phc_encoded: str, secret: bytes | None = None - ) -> None: ... - -class HKDF: - def __init__( - self, - algorithm: HashAlgorithm, - length: int, - salt: bytes | None, - info: bytes | None, - backend: typing.Any = None, - ): ... - @staticmethod - def extract( - algorithm: HashAlgorithm, salt: bytes | None, key_material: Buffer - ) -> bytes: ... - def derive(self, key_material: Buffer) -> bytes: ... - def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - -class HKDFExpand: - def __init__( - self, - algorithm: HashAlgorithm, - length: int, - info: bytes | None, - backend: typing.Any = None, - ): ... - def derive(self, key_material: Buffer) -> bytes: ... - def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - -class X963KDF: - def __init__( - self, - algorithm: HashAlgorithm, - length: int, - sharedinfo: bytes | None, - backend: typing.Any = None, - ) -> None: ... - def derive(self, key_material: Buffer) -> bytes: ... - def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - -class ConcatKDFHash: - def __init__( - self, - algorithm: HashAlgorithm, - length: int, - otherinfo: bytes | None, - backend: typing.Any = None, - ) -> None: ... - def derive(self, key_material: Buffer) -> bytes: ... - def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - -class ConcatKDFHMAC: - def __init__( - self, - algorithm: HashAlgorithm, - length: int, - salt: bytes | None, - otherinfo: bytes | None, - backend: typing.Any = None, - ) -> None: ... - def derive(self, key_material: Buffer) -> bytes: ... - def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - -class KBKDFHMAC: - def __init__( - self, - algorithm: HashAlgorithm, - mode: Mode, - length: int, - rlen: int, - llen: int | None, - location: CounterLocation, - label: bytes | None, - context: bytes | None, - fixed: bytes | None, - backend: typing.Any = None, - *, - break_location: int | None = None, - ) -> None: ... - def derive(self, key_material: Buffer) -> bytes: ... - def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - -class KBKDFCMAC: - def __init__( - self, - algorithm: typing.Any, - mode: Mode, - length: int, - rlen: int, - llen: int | None, - location: CounterLocation, - label: bytes | None, - context: bytes | None, - fixed: bytes | None, - backend: typing.Any = None, - *, - break_location: int | None = None, - ) -> None: ... - def derive(self, key_material: Buffer) -> bytes: ... - def derive_into(self, key_material: Buffer, buffer: Buffer) -> int: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi deleted file mode 100644 index 404057e0..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi +++ /dev/null @@ -1,34 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric.types import ( - PrivateKeyTypes, - PublicKeyTypes, -) -from cryptography.utils import Buffer - -def load_der_private_key( - data: Buffer, - password: bytes | None, - backend: typing.Any = None, - *, - unsafe_skip_rsa_key_validation: bool = False, -) -> PrivateKeyTypes: ... -def load_pem_private_key( - data: Buffer, - password: bytes | None, - backend: typing.Any = None, - *, - unsafe_skip_rsa_key_validation: bool = False, -) -> PrivateKeyTypes: ... -def load_der_public_key( - data: bytes, - backend: typing.Any = None, -) -> PublicKeyTypes: ... -def load_pem_public_key( - data: bytes, - backend: typing.Any = None, -) -> PublicKeyTypes: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi deleted file mode 100644 index 232469a0..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mldsa.pyi +++ /dev/null @@ -1,23 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import mldsa -from cryptography.utils import Buffer - -class MLDSA44PrivateKey: ... -class MLDSA44PublicKey: ... -class MLDSA65PrivateKey: ... -class MLDSA65PublicKey: ... -class MLDSA87PrivateKey: ... -class MLDSA87PublicKey: ... - -def generate_mldsa44_key() -> mldsa.MLDSA44PrivateKey: ... -def from_mldsa44_public_bytes(data: bytes) -> mldsa.MLDSA44PublicKey: ... -def from_mldsa44_seed_bytes(data: Buffer) -> mldsa.MLDSA44PrivateKey: ... -def generate_mldsa65_key() -> mldsa.MLDSA65PrivateKey: ... -def from_mldsa65_public_bytes(data: bytes) -> mldsa.MLDSA65PublicKey: ... -def from_mldsa65_seed_bytes(data: Buffer) -> mldsa.MLDSA65PrivateKey: ... -def generate_mldsa87_key() -> mldsa.MLDSA87PrivateKey: ... -def from_mldsa87_public_bytes(data: bytes) -> mldsa.MLDSA87PublicKey: ... -def from_mldsa87_seed_bytes(data: Buffer) -> mldsa.MLDSA87PrivateKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi deleted file mode 100644 index 768a340a..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/mlkem.pyi +++ /dev/null @@ -1,18 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import mlkem -from cryptography.utils import Buffer - -class MLKEM768PrivateKey: ... -class MLKEM768PublicKey: ... -class MLKEM1024PrivateKey: ... -class MLKEM1024PublicKey: ... - -def generate_mlkem768_key() -> mlkem.MLKEM768PrivateKey: ... -def from_mlkem768_seed_bytes(data: Buffer) -> mlkem.MLKEM768PrivateKey: ... -def from_mlkem768_public_bytes(data: Buffer) -> mlkem.MLKEM768PublicKey: ... -def generate_mlkem1024_key() -> mlkem.MLKEM1024PrivateKey: ... -def from_mlkem1024_seed_bytes(data: Buffer) -> mlkem.MLKEM1024PrivateKey: ... -def from_mlkem1024_public_bytes(data: Buffer) -> mlkem.MLKEM1024PublicKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi deleted file mode 100644 index 45a2a39f..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi +++ /dev/null @@ -1,15 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.utils import Buffer - -class Poly1305: - def __init__(self, key: Buffer) -> None: ... - @staticmethod - def generate_tag(key: Buffer, data: Buffer) -> bytes: ... - @staticmethod - def verify_tag(key: Buffer, data: Buffer, tag: bytes) -> None: ... - def update(self, data: Buffer) -> None: ... - def finalize(self) -> bytes: ... - def verify(self, tag: bytes) -> None: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi deleted file mode 100644 index ef7752dd..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi +++ /dev/null @@ -1,55 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric import rsa - -class RSAPrivateKey: ... -class RSAPublicKey: ... - -class RSAPrivateNumbers: - def __init__( - self, - p: int, - q: int, - d: int, - dmp1: int, - dmq1: int, - iqmp: int, - public_numbers: RSAPublicNumbers, - ) -> None: ... - @property - def p(self) -> int: ... - @property - def q(self) -> int: ... - @property - def d(self) -> int: ... - @property - def dmp1(self) -> int: ... - @property - def dmq1(self) -> int: ... - @property - def iqmp(self) -> int: ... - @property - def public_numbers(self) -> RSAPublicNumbers: ... - def private_key( - self, - backend: typing.Any = None, - *, - unsafe_skip_rsa_key_validation: bool = False, - ) -> rsa.RSAPrivateKey: ... - -class RSAPublicNumbers: - def __init__(self, e: int, n: int) -> None: ... - @property - def n(self) -> int: ... - @property - def e(self) -> int: ... - def public_key(self, backend: typing.Any = None) -> rsa.RSAPublicKey: ... - -def generate_private_key( - public_exponent: int, - key_size: int, -) -> rsa.RSAPrivateKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi deleted file mode 100644 index 38d2addd..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import x25519 -from cryptography.utils import Buffer - -class X25519PrivateKey: ... -class X25519PublicKey: ... - -def generate_key() -> x25519.X25519PrivateKey: ... -def from_private_bytes(data: Buffer) -> x25519.X25519PrivateKey: ... -def from_public_bytes(data: bytes) -> x25519.X25519PublicKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi deleted file mode 100644 index 3ac09809..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import x448 -from cryptography.utils import Buffer - -class X448PrivateKey: ... -class X448PublicKey: ... - -def generate_key() -> x448.X448PrivateKey: ... -def from_private_bytes(data: Buffer) -> x448.X448PrivateKey: ... -def from_public_bytes(data: bytes) -> x448.X448PublicKey: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi deleted file mode 100644 index b25becb6..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi +++ /dev/null @@ -1,52 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing -from collections.abc import Iterable - -from cryptography import x509 -from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes -from cryptography.hazmat.primitives.serialization import ( - KeySerializationEncryption, -) -from cryptography.hazmat.primitives.serialization.pkcs12 import ( - PKCS12KeyAndCertificates, - PKCS12PrivateKeyTypes, -) -from cryptography.utils import Buffer - -class PKCS12Certificate: - def __init__( - self, cert: x509.Certificate, friendly_name: bytes | None - ) -> None: ... - @property - def friendly_name(self) -> bytes | None: ... - @property - def certificate(self) -> x509.Certificate: ... - -def load_key_and_certificates( - data: Buffer, - password: Buffer | None, - backend: typing.Any = None, -) -> tuple[ - PrivateKeyTypes | None, - x509.Certificate | None, - list[x509.Certificate], -]: ... -def load_pkcs12( - data: bytes, - password: bytes | None, - backend: typing.Any = None, -) -> PKCS12KeyAndCertificates: ... -def serialize_java_truststore( - certs: Iterable[PKCS12Certificate], - encryption_algorithm: KeySerializationEncryption, -) -> bytes: ... -def serialize_key_and_certificates( - name: bytes | None, - key: PKCS12PrivateKeyTypes | None, - cert: x509.Certificate | None, - cas: Iterable[x509.Certificate | PKCS12Certificate] | None, - encryption_algorithm: KeySerializationEncryption, -) -> bytes: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi deleted file mode 100644 index 358b1358..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi +++ /dev/null @@ -1,50 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from collections.abc import Iterable - -from cryptography import x509 -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives.serialization import pkcs7 - -def serialize_certificates( - certs: list[x509.Certificate], - encoding: serialization.Encoding, -) -> bytes: ... -def encrypt_and_serialize( - builder: pkcs7.PKCS7EnvelopeBuilder, - content_encryption_algorithm: pkcs7.ContentEncryptionAlgorithm, - encoding: serialization.Encoding, - options: Iterable[pkcs7.PKCS7Options], -) -> bytes: ... -def sign_and_serialize( - builder: pkcs7.PKCS7SignatureBuilder, - encoding: serialization.Encoding, - options: Iterable[pkcs7.PKCS7Options], -) -> bytes: ... -def decrypt_der( - data: bytes, - certificate: x509.Certificate, - private_key: rsa.RSAPrivateKey, - options: Iterable[pkcs7.PKCS7Options], -) -> bytes: ... -def decrypt_pem( - data: bytes, - certificate: x509.Certificate, - private_key: rsa.RSAPrivateKey, - options: Iterable[pkcs7.PKCS7Options], -) -> bytes: ... -def decrypt_smime( - data: bytes, - certificate: x509.Certificate, - private_key: rsa.RSAPrivateKey, - options: Iterable[pkcs7.PKCS7Options], -) -> bytes: ... -def load_pem_pkcs7_certificates( - data: bytes, -) -> list[x509.Certificate]: ... -def load_der_pkcs7_certificates( - data: bytes, -) -> list[x509.Certificate]: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi deleted file mode 100644 index c6c6d0bb..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi +++ /dev/null @@ -1,23 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography import x509 -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.serialization import pkcs7 -from cryptography.utils import Buffer - -class TestCertificate: - not_after_tag: int - not_before_tag: int - issuer_value_tags: list[int] - subject_value_tags: list[int] - -def test_parse_certificate(data: bytes) -> TestCertificate: ... -def pkcs7_verify( - encoding: serialization.Encoding, - sig: bytes, - msg: Buffer | None, - certs: list[x509.Certificate], - options: list[pkcs7.PKCS7Options], -) -> None: ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi deleted file mode 100644 index 34a726e1..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi +++ /dev/null @@ -1,313 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import datetime -import typing -from collections.abc import Iterator - -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric.ec import ECDSA -from cryptography.hazmat.primitives.asymmetric.padding import PSS, PKCS1v15 -from cryptography.hazmat.primitives.asymmetric.types import ( - CertificateIssuerPublicKeyTypes, - CertificatePublicKeyTypes, - PrivateKeyTypes, -) -from cryptography.x509 import certificate_transparency - -def load_pem_x509_certificate( - data: bytes, backend: typing.Any = None -) -> x509.Certificate: ... -def load_der_x509_certificate( - data: bytes, backend: typing.Any = None -) -> x509.Certificate: ... -def load_pem_x509_certificates( - data: bytes, -) -> list[x509.Certificate]: ... -def load_pem_x509_crl( - data: bytes, backend: typing.Any = None -) -> x509.CertificateRevocationList: ... -def load_der_x509_crl( - data: bytes, backend: typing.Any = None -) -> x509.CertificateRevocationList: ... -def load_pem_x509_csr( - data: bytes, backend: typing.Any = None -) -> x509.CertificateSigningRequest: ... -def load_der_x509_csr( - data: bytes, backend: typing.Any = None -) -> x509.CertificateSigningRequest: ... -def encode_name_bytes(name: x509.Name) -> bytes: ... -def parse_name_bytes(data: bytes) -> x509.Name: ... -def encode_extension_value(extension: x509.ExtensionType) -> bytes: ... -def create_x509_certificate( - builder: x509.CertificateBuilder, - private_key: PrivateKeyTypes, - hash_algorithm: hashes.HashAlgorithm | None, - rsa_padding: PKCS1v15 | PSS | None, - ecdsa_deterministic: bool | None, -) -> x509.Certificate: ... -def create_x509_csr( - builder: x509.CertificateSigningRequestBuilder, - private_key: PrivateKeyTypes, - hash_algorithm: hashes.HashAlgorithm | None, - rsa_padding: PKCS1v15 | PSS | None, - ecdsa_deterministic: bool | None, -) -> x509.CertificateSigningRequest: ... -def create_revoked_certificate( - builder: x509.RevokedCertificateBuilder, -) -> x509.RevokedCertificate: ... -def create_x509_crl( - builder: x509.CertificateRevocationListBuilder, - private_key: PrivateKeyTypes, - hash_algorithm: hashes.HashAlgorithm | None, - rsa_padding: PKCS1v15 | PSS | None, - ecdsa_deterministic: bool | None, -) -> x509.CertificateRevocationList: ... - -class Sct: - @property - def version(self) -> certificate_transparency.Version: ... - @property - def log_id(self) -> bytes: ... - @property - def timestamp(self) -> datetime.datetime: ... - @property - def entry_type(self) -> certificate_transparency.LogEntryType: ... - @property - def signature_hash_algorithm(self) -> hashes.HashAlgorithm: ... - @property - def signature_algorithm( - self, - ) -> certificate_transparency.SignatureAlgorithm: ... - @property - def signature(self) -> bytes: ... - @property - def extension_bytes(self) -> bytes: ... - -class Certificate: - def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: ... - @property - def serial_number(self) -> int: ... - @property - def version(self) -> x509.Version: ... - def public_key(self) -> CertificatePublicKeyTypes: ... - @property - def public_key_algorithm_oid(self) -> x509.ObjectIdentifier: ... - @property - def not_valid_before(self) -> datetime.datetime: ... - @property - def not_valid_before_utc(self) -> datetime.datetime: ... - @property - def not_valid_after(self) -> datetime.datetime: ... - @property - def not_valid_after_utc(self) -> datetime.datetime: ... - @property - def issuer(self) -> x509.Name: ... - @property - def subject(self) -> x509.Name: ... - @property - def signature_hash_algorithm( - self, - ) -> hashes.HashAlgorithm | None: ... - @property - def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... - @property - def signature_algorithm_parameters( - self, - ) -> PSS | PKCS1v15 | ECDSA | None: ... - @property - def extensions(self) -> x509.Extensions: ... - @property - def signature(self) -> bytes: ... - @property - def tbs_certificate_bytes(self) -> bytes: ... - @property - def tbs_precertificate_bytes(self) -> bytes: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... - def verify_directly_issued_by(self, issuer: Certificate) -> None: ... - -class RevokedCertificate: - @property - def serial_number(self) -> int: ... - @property - def revocation_date(self) -> datetime.datetime: ... - @property - def revocation_date_utc(self) -> datetime.datetime: ... - @property - def extensions(self) -> x509.Extensions: ... - -class CertificateRevocationList: - def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... - def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: ... - def get_revoked_certificate_by_serial_number( - self, serial_number: int - ) -> x509.RevokedCertificate | None: ... - @property - def signature_hash_algorithm( - self, - ) -> hashes.HashAlgorithm | None: ... - @property - def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... - @property - def signature_algorithm_parameters( - self, - ) -> PSS | PKCS1v15 | ECDSA | None: ... - @property - def issuer(self) -> x509.Name: ... - @property - def next_update(self) -> datetime.datetime | None: ... - @property - def next_update_utc(self) -> datetime.datetime | None: ... - @property - def last_update(self) -> datetime.datetime: ... - @property - def last_update_utc(self) -> datetime.datetime: ... - @property - def extensions(self) -> x509.Extensions: ... - @property - def signature(self) -> bytes: ... - @property - def tbs_certlist_bytes(self) -> bytes: ... - def __eq__(self, other: object) -> bool: ... - def __len__(self) -> int: ... - @typing.overload - def __getitem__(self, idx: int) -> x509.RevokedCertificate: ... - @typing.overload - def __getitem__(self, idx: slice) -> list[x509.RevokedCertificate]: ... - def __iter__(self) -> Iterator[x509.RevokedCertificate]: ... - def is_signature_valid( - self, public_key: CertificateIssuerPublicKeyTypes - ) -> bool: ... - -class CertificateSigningRequest: - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - def public_key(self) -> CertificatePublicKeyTypes: ... - @property - def subject(self) -> x509.Name: ... - @property - def signature_hash_algorithm( - self, - ) -> hashes.HashAlgorithm | None: ... - @property - def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... - @property - def signature_algorithm_parameters( - self, - ) -> PSS | PKCS1v15 | ECDSA | None: ... - @property - def extensions(self) -> x509.Extensions: ... - @property - def attributes(self) -> x509.Attributes: ... - def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... - @property - def signature(self) -> bytes: ... - @property - def tbs_certrequest_bytes(self) -> bytes: ... - @property - def is_signature_valid(self) -> bool: ... - -class PolicyBuilder: - def time(self, time: datetime.datetime) -> PolicyBuilder: ... - def store(self, store: Store) -> PolicyBuilder: ... - def max_chain_depth(self, max_chain_depth: int) -> PolicyBuilder: ... - def extension_policies( - self, *, ca_policy: ExtensionPolicy, ee_policy: ExtensionPolicy - ) -> PolicyBuilder: ... - def build_client_verifier(self) -> ClientVerifier: ... - def build_server_verifier( - self, subject: x509.verification.Subject - ) -> ServerVerifier: ... - -class Policy: - @property - def max_chain_depth(self) -> int: ... - @property - def subject(self) -> x509.verification.Subject | None: ... - @property - def validation_time(self) -> datetime.datetime: ... - @property - def extended_key_usage(self) -> x509.ObjectIdentifier: ... - @property - def minimum_rsa_modulus(self) -> int: ... - -class Criticality: - CRITICAL: Criticality - AGNOSTIC: Criticality - NON_CRITICAL: Criticality - -T = typing.TypeVar("T", contravariant=True, bound=x509.ExtensionType) - -MaybeExtensionValidatorCallback = typing.Callable[ - [ - Policy, - x509.Certificate, - T | None, - ], - None, -] - -PresentExtensionValidatorCallback = typing.Callable[ - [Policy, x509.Certificate, T], - None, -] - -class ExtensionPolicy: - @staticmethod - def permit_all() -> ExtensionPolicy: ... - @staticmethod - def webpki_defaults_ca() -> ExtensionPolicy: ... - @staticmethod - def webpki_defaults_ee() -> ExtensionPolicy: ... - def require_not_present( - self, extension_type: type[x509.ExtensionType] - ) -> ExtensionPolicy: ... - def may_be_present( - self, - extension_type: type[T], - criticality: Criticality, - validator: MaybeExtensionValidatorCallback[T] | None, - ) -> ExtensionPolicy: ... - def require_present( - self, - extension_type: type[T], - criticality: Criticality, - validator: PresentExtensionValidatorCallback[T] | None, - ) -> ExtensionPolicy: ... - -class VerifiedClient: - @property - def subjects(self) -> list[x509.GeneralName] | None: ... - @property - def chain(self) -> list[x509.Certificate]: ... - -class ClientVerifier: - @property - def policy(self) -> Policy: ... - @property - def store(self) -> Store: ... - def verify( - self, - leaf: x509.Certificate, - intermediates: list[x509.Certificate], - ) -> VerifiedClient: ... - -class ServerVerifier: - @property - def policy(self) -> Policy: ... - @property - def store(self) -> Store: ... - def verify( - self, - leaf: x509.Certificate, - intermediates: list[x509.Certificate], - ) -> list[x509.Certificate]: ... - -class Store: - def __init__(self, certs: list[x509.Certificate]) -> None: ... - -class VerificationError(Exception): ... diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py deleted file mode 100644 index b5093362..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py deleted file mode 100644 index 1e447a59..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py +++ /dev/null @@ -1,199 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - - -def cryptography_has_set_cert_cb() -> list[str]: - return [ - "SSL_CTX_set_cert_cb", - "SSL_set_cert_cb", - ] - - -def cryptography_has_ssl_st() -> list[str]: - return [ - "SSL_ST_BEFORE", - "SSL_ST_OK", - "SSL_ST_INIT", - "SSL_ST_RENEGOTIATE", - ] - - -def cryptography_has_tls_st() -> list[str]: - return [ - "TLS_ST_BEFORE", - "TLS_ST_OK", - ] - - -def cryptography_has_ssl_sigalgs() -> list[str]: - return [ - "SSL_CTX_set1_sigalgs_list", - ] - - -def cryptography_has_psk() -> list[str]: - return [ - "SSL_CTX_use_psk_identity_hint", - "SSL_CTX_set_psk_server_callback", - "SSL_CTX_set_psk_client_callback", - ] - - -def cryptography_has_psk_tlsv13() -> list[str]: - return [ - "SSL_CTX_set_psk_find_session_callback", - "SSL_CTX_set_psk_use_session_callback", - "Cryptography_SSL_SESSION_new", - "SSL_CIPHER_find", - "SSL_SESSION_set1_master_key", - "SSL_SESSION_set_cipher", - "SSL_SESSION_set_protocol_version", - ] - - -def cryptography_has_custom_ext() -> list[str]: - return [ - "SSL_CTX_add_client_custom_ext", - "SSL_CTX_add_server_custom_ext", - "SSL_extension_supported", - ] - - -def cryptography_has_tlsv13_functions() -> list[str]: - return [ - "SSL_CTX_set_ciphersuites", - ] - - -def cryptography_has_tlsv13_hs_functions() -> list[str]: - return [ - "SSL_VERIFY_POST_HANDSHAKE", - "SSL_verify_client_post_handshake", - "SSL_CTX_set_post_handshake_auth", - "SSL_set_post_handshake_auth", - "SSL_SESSION_get_max_early_data", - "SSL_write_early_data", - "SSL_read_early_data", - "SSL_CTX_set_max_early_data", - ] - - -def cryptography_has_ssl_verify_client_post_handshake() -> list[str]: - return [ - "SSL_verify_client_post_handshake", - ] - - -def cryptography_has_engine() -> list[str]: - return [ - "ENGINE_by_id", - "ENGINE_init", - "ENGINE_finish", - "ENGINE_get_default_RAND", - "ENGINE_set_default_RAND", - "ENGINE_unregister_RAND", - "ENGINE_ctrl_cmd", - "ENGINE_free", - "ENGINE_get_name", - "ENGINE_ctrl_cmd_string", - "ENGINE_load_builtin_engines", - "ENGINE_load_private_key", - "ENGINE_load_public_key", - "SSL_CTX_set_client_cert_engine", - ] - - -def cryptography_has_verified_chain() -> list[str]: - return [ - "SSL_get0_verified_chain", - ] - - -def cryptography_has_srtp() -> list[str]: - return [ - "SSL_CTX_set_tlsext_use_srtp", - "SSL_set_tlsext_use_srtp", - "SSL_get_selected_srtp_profile", - ] - - -def cryptography_has_dtls_get_data_mtu() -> list[str]: - return [ - "DTLS_get_data_mtu", - ] - - -def cryptography_has_ssl_cookie() -> list[str]: - return [ - "SSL_OP_COOKIE_EXCHANGE", - "DTLS1_COOKIE_LENGTH", - "DTLSv1_listen", - "SSL_CTX_set_cookie_generate_cb", - "SSL_CTX_set_cookie_verify_cb", - ] - - -def cryptography_has_prime_checks() -> list[str]: - return [ - "BN_prime_checks_for_size", - ] - - -def cryptography_has_unexpected_eof_while_reading() -> list[str]: - return ["SSL_R_UNEXPECTED_EOF_WHILE_READING"] - - -def cryptography_has_ssl_op_ignore_unexpected_eof() -> list[str]: - return [ - "SSL_OP_IGNORE_UNEXPECTED_EOF", - ] - - -def cryptography_has_get_extms_support() -> list[str]: - return ["SSL_get_extms_support"] - - -def cryptography_has_ssl_get0_group_name() -> list[str]: - return ["SSL_get0_group_name"] - - -# This is a mapping of -# {condition: function-returning-names-dependent-on-that-condition} so we can -# loop over them and delete unsupported names at runtime. It will be removed -# when cffi supports #if in cdef. We use functions instead of just a dict of -# lists so we can use coverage to measure which are used. -CONDITIONAL_NAMES = { - "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb, - "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st, - "Cryptography_HAS_TLS_ST": cryptography_has_tls_st, - "Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs, - "Cryptography_HAS_PSK": cryptography_has_psk, - "Cryptography_HAS_PSK_TLSv1_3": cryptography_has_psk_tlsv13, - "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext, - "Cryptography_HAS_TLSv1_3_FUNCTIONS": cryptography_has_tlsv13_functions, - "Cryptography_HAS_TLSv1_3_HS_FUNCTIONS": ( - cryptography_has_tlsv13_hs_functions - ), - "Cryptography_HAS_SSL_VERIFY_CLIENT_POST_HANDSHAKE": ( - cryptography_has_ssl_verify_client_post_handshake - ), - "Cryptography_HAS_ENGINE": cryptography_has_engine, - "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain, - "Cryptography_HAS_SRTP": cryptography_has_srtp, - "Cryptography_HAS_DTLS_GET_DATA_MTU": cryptography_has_dtls_get_data_mtu, - "Cryptography_HAS_SSL_COOKIE": cryptography_has_ssl_cookie, - "Cryptography_HAS_PRIME_CHECKS": cryptography_has_prime_checks, - "Cryptography_HAS_UNEXPECTED_EOF_WHILE_READING": ( - cryptography_has_unexpected_eof_while_reading - ), - "Cryptography_HAS_SSL_OP_IGNORE_UNEXPECTED_EOF": ( - cryptography_has_ssl_op_ignore_unexpected_eof - ), - "Cryptography_HAS_GET_EXTMS_SUPPORT": cryptography_has_get_extms_support, - "Cryptography_HAS_SSL_GET0_GROUP_NAME": ( - cryptography_has_ssl_get0_group_name - ), -} diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py deleted file mode 100644 index 6e13df16..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/bindings/openssl/binding.py +++ /dev/null @@ -1,107 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import threading -import types -import typing -from collections.abc import Callable, Mapping - -import cryptography -from cryptography.exceptions import InternalError -from cryptography.hazmat.bindings._rust import _openssl, openssl -from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES - - -def _openssl_assert(ok: bool) -> None: - if not ok: - errors = openssl.capture_error_stack() - - raise InternalError( - "Unknown OpenSSL error. This error is commonly encountered when " - "another library is not cleaning up the OpenSSL error stack. If " - "you are using cryptography with another library that uses " - "OpenSSL try disabling it before reporting a bug. Otherwise " - "please file an issue at https://github.com/pyca/cryptography/" - "issues with information on how to reproduce " - f"this. ({errors!r})", - errors, - ) - - -def build_conditional_library( - lib: typing.Any, - conditional_names: Mapping[str, Callable[[], list[str]]], -) -> typing.Any: - conditional_lib = types.ModuleType("lib") - conditional_lib._original_lib = lib # type: ignore[attr-defined] - excluded_names = set() - for condition, names_cb in conditional_names.items(): - if not getattr(lib, condition): - excluded_names.update(names_cb()) - - for attr in dir(lib): - if attr not in excluded_names: - setattr(conditional_lib, attr, getattr(lib, attr)) - - return conditional_lib - - -class Binding: - """ - OpenSSL API wrapper. - """ - - lib: typing.ClassVar[typing.Any] = None - ffi: typing.Any = _openssl.ffi - _lib_loaded = False - _init_lock = threading.Lock() - - def __init__(self) -> None: - self._ensure_ffi_initialized() - - @classmethod - def _ensure_ffi_initialized(cls) -> None: - with cls._init_lock: - if not cls._lib_loaded: - cls.lib = build_conditional_library( - _openssl.lib, CONDITIONAL_NAMES - ) - cls._lib_loaded = True - - @classmethod - def init_static_locks(cls) -> None: - cls._ensure_ffi_initialized() - - -def _verify_package_version(version: str) -> None: - # Occasionally we run into situations where the version of the Python - # package does not match the version of the shared object that is loaded. - # This may occur in environments where multiple versions of cryptography - # are installed and available in the python path. To avoid errors cropping - # up later this code checks that the currently imported package and the - # shared object that were loaded have the same version and raise an - # ImportError if they do not - so_package_version = _openssl.ffi.string( - _openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION - ) - if version.encode("ascii") != so_package_version: - raise ImportError( - "The version of cryptography does not match the loaded " - "shared object. This can happen if you have multiple copies of " - "cryptography installed in your Python path. Please try creating " - "a new virtual environment to resolve this issue. " - f"Loaded python version: {version}, " - f"shared object version: {so_package_version}" - ) - - _openssl_assert( - _openssl.lib.OpenSSL_version_num() == openssl.openssl_version(), - ) - - -_verify_package_version(cryptography.__version__) - -Binding.init_static_locks() diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py deleted file mode 100644 index 41d73186..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py deleted file mode 100644 index 41d73186..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py deleted file mode 100644 index 703c8e4a..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py +++ /dev/null @@ -1,142 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import warnings - -from cryptography import utils -from cryptography.hazmat.primitives._cipheralgorithm import ( - BlockCipherAlgorithm, - CipherAlgorithm, - _verify_key_size, -) - - -class ARC4(CipherAlgorithm): - name = "RC4" - key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class TripleDES(BlockCipherAlgorithm): - name = "3DES" - block_size = 64 - key_sizes = frozenset([64, 128, 192]) - - def __init__(self, key: bytes): - if len(key) == 8: - warnings.warn( - "Single-key TripleDES (8-byte keys) is deprecated and " - "support will be removed in a future release. Use 24-byte " - "keys instead (e.g., key + key + key).", - utils.DeprecatedIn47, - stacklevel=2, - ) - key = key + key + key - elif len(key) == 16: - warnings.warn( - "Two-key TripleDES (16-byte keys) is deprecated and " - "support will be removed in a future release. Use 24-byte " - "keys instead (e.g., key + key[:8]).", - utils.DeprecatedIn47, - stacklevel=2, - ) - key = key + key[:8] - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -# Not actually supported, marker for tests -class _DES: - key_size = 64 - - -class Blowfish(BlockCipherAlgorithm): - name = "Blowfish" - block_size = 64 - key_sizes = frozenset(range(32, 449, 8)) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class CAST5(BlockCipherAlgorithm): - name = "CAST5" - block_size = 64 - key_sizes = frozenset(range(40, 129, 8)) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class SEED(BlockCipherAlgorithm): - name = "SEED" - block_size = 128 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class IDEA(BlockCipherAlgorithm): - name = "IDEA" - block_size = 64 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class Camellia(BlockCipherAlgorithm): - name = "camellia" - block_size = 128 - key_sizes = frozenset([128, 192, 256]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -# This class only allows RC2 with a 128-bit key. No support for -# effective key bits or other key sizes is provided. -class RC2(BlockCipherAlgorithm): - name = "RC2" - block_size = 64 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/modes.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/modes.py deleted file mode 100644 index 1786bb04..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/decrepit/ciphers/modes.py +++ /dev/null @@ -1,53 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography import utils -from cryptography.hazmat.primitives._modes import ( - ModeWithInitializationVector, - _check_iv_and_key_length, -) - - -class OFB(ModeWithInitializationVector): - name = "OFB" - - def __init__(self, initialization_vector: utils.Buffer): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> utils.Buffer: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class CFB(ModeWithInitializationVector): - name = "CFB" - - def __init__(self, initialization_vector: utils.Buffer): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> utils.Buffer: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class CFB8(ModeWithInitializationVector): - name = "CFB8" - - def __init__(self, initialization_vector: utils.Buffer): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> utils.Buffer: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py deleted file mode 100644 index b5093362..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py deleted file mode 100644 index ea55ffdf..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_asymmetric.py +++ /dev/null @@ -1,19 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -# This exists to break an import cycle. It is normally accessible from the -# asymmetric padding module. - - -class AsymmetricPadding(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this padding (e.g. "PSS", "PKCS1"). - """ diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py deleted file mode 100644 index 305a9fd3..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py +++ /dev/null @@ -1,60 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography import utils - -# This exists to break an import cycle. It is normally accessible from the -# ciphers module. - - -class CipherAlgorithm(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this mode (e.g. "AES", "Camellia"). - """ - - @property - @abc.abstractmethod - def key_sizes(self) -> frozenset[int]: - """ - Valid key sizes for this algorithm in bits - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The size of the key being used as an integer in bits (e.g. 128, 256). - """ - - -class BlockCipherAlgorithm(CipherAlgorithm): - key: utils.Buffer - - @property - @abc.abstractmethod - def block_size(self) -> int: - """ - The size of a block as an integer in bits (e.g. 64, 128). - """ - - -def _verify_key_size( - algorithm: CipherAlgorithm, key: utils.Buffer -) -> utils.Buffer: - # Verify that the key is instance of bytes - utils._check_byteslike("key", key) - - # Verify that the key size matches the expected key size - if len(key) * 8 not in algorithm.key_sizes: - raise ValueError( - f"Invalid key size ({len(key) * 8}) for {algorithm.name}." - ) - return key diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_modes.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_modes.py deleted file mode 100644 index deae8bcc..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_modes.py +++ /dev/null @@ -1,105 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.primitives._cipheralgorithm import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) - - -class Mode(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this mode (e.g. "ECB", "CBC"). - """ - - @abc.abstractmethod - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - """ - Checks that all the necessary invariants of this (mode, algorithm) - combination are met. - """ - - -class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def initialization_vector(self) -> utils.Buffer: - """ - The value of the initialization vector for this mode as bytes. - """ - - -class ModeWithTweak(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def tweak(self) -> utils.Buffer: - """ - The value of the tweak for this mode as bytes. - """ - - -class ModeWithNonce(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def nonce(self) -> utils.Buffer: - """ - The value of the nonce for this mode as bytes. - """ - - -class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def tag(self) -> bytes | None: - """ - The value of the tag supplied to the constructor of this mode. - """ - - -def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None: - if algorithm.key_size > 256 and algorithm.name == "AES": - raise ValueError( - "Only 128, 192, and 256 bit keys are allowed for this AES mode" - ) - - -def _check_iv_length( - self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm -) -> None: - iv_len = len(self.initialization_vector) - if iv_len * 8 != algorithm.block_size: - raise ValueError(f"Invalid IV size ({iv_len}) for {self.name}.") - - -def _check_nonce_length( - nonce: utils.Buffer, name: str, algorithm: CipherAlgorithm -) -> None: - if not isinstance(algorithm, BlockCipherAlgorithm): - raise UnsupportedAlgorithm( - f"{name} requires a block cipher algorithm", - _Reasons.UNSUPPORTED_CIPHER, - ) - if len(nonce) * 8 != algorithm.block_size: - raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.") - - -def _check_iv_and_key_length( - self: ModeWithInitializationVector, algorithm: CipherAlgorithm -) -> None: - if not isinstance(algorithm, BlockCipherAlgorithm): - raise UnsupportedAlgorithm( - f"{self} requires a block cipher algorithm", - _Reasons.UNSUPPORTED_CIPHER, - ) - _check_aes_key_length(self, algorithm) - _check_iv_length(self, algorithm) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py deleted file mode 100644 index 9c3c4744..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/_serialization.py +++ /dev/null @@ -1,136 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography import utils -from cryptography.hazmat.bindings._rust import Encoding as Encoding -from cryptography.hazmat.bindings._rust import ( - ParameterFormat as ParameterFormat, -) -from cryptography.hazmat.bindings._rust import PrivateFormat as PrivateFormat -from cryptography.hazmat.bindings._rust import PublicFormat as PublicFormat -from cryptography.hazmat.primitives.hashes import HashAlgorithm - -# This exists to break an import cycle. These classes are normally accessible -# from the serialization module. - - -class PBES(utils.Enum): - PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES" - PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC" - - -class KeySerializationEncryption(metaclass=abc.ABCMeta): - pass - - -class BestAvailableEncryption(KeySerializationEncryption): - def __init__(self, password: bytes): - if not isinstance(password, bytes) or len(password) == 0: - raise ValueError("Password must be 1 or more bytes.") - - self.password = password - - -class NoEncryption(KeySerializationEncryption): - pass - - -class KeySerializationEncryptionBuilder: - def __init__( - self, - format: PrivateFormat, - *, - _kdf_rounds: int | None = None, - _hmac_hash: HashAlgorithm | None = None, - _key_cert_algorithm: PBES | None = None, - ) -> None: - self._format = format - - self._kdf_rounds = _kdf_rounds - self._hmac_hash = _hmac_hash - self._key_cert_algorithm = _key_cert_algorithm - - def kdf_rounds(self, rounds: int) -> KeySerializationEncryptionBuilder: - if self._kdf_rounds is not None: - raise ValueError("kdf_rounds already set") - - if not isinstance(rounds, int): - raise TypeError("kdf_rounds must be an integer") - - if rounds < 1: - raise ValueError("kdf_rounds must be a positive integer") - - return KeySerializationEncryptionBuilder( - self._format, - _kdf_rounds=rounds, - _hmac_hash=self._hmac_hash, - _key_cert_algorithm=self._key_cert_algorithm, - ) - - def hmac_hash( - self, algorithm: HashAlgorithm - ) -> KeySerializationEncryptionBuilder: - if self._format is not PrivateFormat.PKCS12: - raise TypeError( - "hmac_hash only supported with PrivateFormat.PKCS12" - ) - - if self._hmac_hash is not None: - raise ValueError("hmac_hash already set") - return KeySerializationEncryptionBuilder( - self._format, - _kdf_rounds=self._kdf_rounds, - _hmac_hash=algorithm, - _key_cert_algorithm=self._key_cert_algorithm, - ) - - def key_cert_algorithm( - self, algorithm: PBES - ) -> KeySerializationEncryptionBuilder: - if self._format is not PrivateFormat.PKCS12: - raise TypeError( - "key_cert_algorithm only supported with PrivateFormat.PKCS12" - ) - if self._key_cert_algorithm is not None: - raise ValueError("key_cert_algorithm already set") - return KeySerializationEncryptionBuilder( - self._format, - _kdf_rounds=self._kdf_rounds, - _hmac_hash=self._hmac_hash, - _key_cert_algorithm=algorithm, - ) - - def build(self, password: bytes) -> KeySerializationEncryption: - if not isinstance(password, bytes) or len(password) == 0: - raise ValueError("Password must be 1 or more bytes.") - - return _KeySerializationEncryption( - self._format, - password, - kdf_rounds=self._kdf_rounds, - hmac_hash=self._hmac_hash, - key_cert_algorithm=self._key_cert_algorithm, - ) - - -class _KeySerializationEncryption(KeySerializationEncryption): - def __init__( - self, - format: PrivateFormat, - password: bytes, - *, - kdf_rounds: int | None, - hmac_hash: HashAlgorithm | None, - key_cert_algorithm: PBES | None, - ): - self._format = format - self.password = password - - self._kdf_rounds = kdf_rounds - self._hmac_hash = hmac_hash - self._key_cert_algorithm = key_cert_algorithm diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py deleted file mode 100644 index b5093362..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py deleted file mode 100644 index 2f6b834e..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py +++ /dev/null @@ -1,159 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization - -generate_parameters = rust_openssl.dh.generate_parameters - - -DHPrivateNumbers = rust_openssl.dh.DHPrivateNumbers -DHPublicNumbers = rust_openssl.dh.DHPublicNumbers -DHParameterNumbers = rust_openssl.dh.DHParameterNumbers - - -class DHParameters(metaclass=abc.ABCMeta): - @abc.abstractmethod - def generate_private_key(self) -> DHPrivateKey: - """ - Generates and returns a DHPrivateKey. - """ - - @abc.abstractmethod - def parameter_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.ParameterFormat, - ) -> bytes: - """ - Returns the parameters serialized as bytes. - """ - - @abc.abstractmethod - def parameter_numbers(self) -> DHParameterNumbers: - """ - Returns a DHParameterNumbers. - """ - - -DHParametersWithSerialization = DHParameters -DHParameters.register(rust_openssl.dh.DHParameters) - - -class DHPublicKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def parameters(self) -> DHParameters: - """ - The DHParameters object associated with this public key. - """ - - @abc.abstractmethod - def public_numbers(self) -> DHPublicNumbers: - """ - Returns a DHPublicNumbers. - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> DHPublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> DHPublicKey: - """ - Returns a deep copy. - """ - - -DHPublicKeyWithSerialization = DHPublicKey -DHPublicKey.register(rust_openssl.dh.DHPublicKey) - - -class DHPrivateKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def public_key(self) -> DHPublicKey: - """ - The DHPublicKey associated with this private key. - """ - - @abc.abstractmethod - def parameters(self) -> DHParameters: - """ - The DHParameters object associated with this private key. - """ - - @abc.abstractmethod - def exchange(self, peer_public_key: DHPublicKey) -> bytes: - """ - Given peer's DHPublicKey, carry out the key exchange and - return shared key as bytes. - """ - - @abc.abstractmethod - def private_numbers(self) -> DHPrivateNumbers: - """ - Returns a DHPrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def __copy__(self) -> DHPrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> DHPrivateKey: - """ - Returns a deep copy. - """ - - -DHPrivateKeyWithSerialization = DHPrivateKey -DHPrivateKey.register(rust_openssl.dh.DHPrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py deleted file mode 100644 index f2455574..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py +++ /dev/null @@ -1,179 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import typing - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization, hashes -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils -from cryptography.utils import Buffer - - -class DSAParameters(metaclass=abc.ABCMeta): - @abc.abstractmethod - def generate_private_key(self) -> DSAPrivateKey: - """ - Generates and returns a DSAPrivateKey. - """ - - @abc.abstractmethod - def parameter_numbers(self) -> DSAParameterNumbers: - """ - Returns a DSAParameterNumbers. - """ - - -DSAParametersWithNumbers = DSAParameters -DSAParameters.register(rust_openssl.dsa.DSAParameters) - - -class DSAPrivateKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def public_key(self) -> DSAPublicKey: - """ - The DSAPublicKey associated with this private key. - """ - - @abc.abstractmethod - def parameters(self) -> DSAParameters: - """ - The DSAParameters object associated with this private key. - """ - - @abc.abstractmethod - def sign( - self, - data: Buffer, - algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, - ) -> bytes: - """ - Signs the data - """ - - @abc.abstractmethod - def private_numbers(self) -> DSAPrivateNumbers: - """ - Returns a DSAPrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def __copy__(self) -> DSAPrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> DSAPrivateKey: - """ - Returns a deep copy. - """ - - -DSAPrivateKeyWithSerialization = DSAPrivateKey -DSAPrivateKey.register(rust_openssl.dsa.DSAPrivateKey) - - -class DSAPublicKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def parameters(self) -> DSAParameters: - """ - The DSAParameters object associated with this public key. - """ - - @abc.abstractmethod - def public_numbers(self) -> DSAPublicNumbers: - """ - Returns a DSAPublicNumbers. - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def verify( - self, - signature: Buffer, - data: Buffer, - algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, - ) -> None: - """ - Verifies the signature of the data. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> DSAPublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> DSAPublicKey: - """ - Returns a deep copy. - """ - - -DSAPublicKeyWithSerialization = DSAPublicKey -DSAPublicKey.register(rust_openssl.dsa.DSAPublicKey) - -DSAPrivateNumbers = rust_openssl.dsa.DSAPrivateNumbers -DSAPublicNumbers = rust_openssl.dsa.DSAPublicNumbers -DSAParameterNumbers = rust_openssl.dsa.DSAParameterNumbers - - -def generate_parameters( - key_size: int, backend: typing.Any = None -) -> DSAParameters: - if key_size not in (1024, 2048, 3072, 4096): - raise ValueError("Key size must be 1024, 2048, 3072, or 4096 bits.") - - return rust_openssl.dsa.generate_parameters(key_size) - - -def generate_private_key( - key_size: int, backend: typing.Any = None -) -> DSAPrivateKey: - parameters = generate_parameters(key_size) - return parameters.generate_private_key() diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py deleted file mode 100644 index 39e67519..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py +++ /dev/null @@ -1,369 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import typing - -from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat._oid import ObjectIdentifier -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization, hashes -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils - - -class EllipticCurveOID: - SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1") - SECP224R1 = ObjectIdentifier("1.3.132.0.33") - SECP256K1 = ObjectIdentifier("1.3.132.0.10") - SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7") - SECP384R1 = ObjectIdentifier("1.3.132.0.34") - SECP521R1 = ObjectIdentifier("1.3.132.0.35") - BRAINPOOLP256R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.7") - BRAINPOOLP384R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.11") - BRAINPOOLP512R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.13") - - -class EllipticCurve(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - The name of the curve. e.g. secp256r1. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - Bit size of a secret scalar for the curve. - """ - - @property - @abc.abstractmethod - def group_order(self) -> int: - """ - The order of the curve's group. - """ - - -class EllipticCurveSignatureAlgorithm(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def algorithm( - self, - ) -> asym_utils.Prehashed | hashes.HashAlgorithm: - """ - The digest algorithm used with this signature. - """ - - -class EllipticCurvePrivateKey(metaclass=abc.ABCMeta): - @abc.abstractmethod - def exchange( - self, algorithm: ECDH, peer_public_key: EllipticCurvePublicKey - ) -> bytes: - """ - Performs a key exchange operation using the provided algorithm with the - provided peer's public key. - """ - - @abc.abstractmethod - def public_key(self) -> EllipticCurvePublicKey: - """ - The EllipticCurvePublicKey for this private key. - """ - - @property - @abc.abstractmethod - def curve(self) -> EllipticCurve: - """ - The EllipticCurve that this key is on. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - Bit size of a secret scalar for the curve. - """ - - @abc.abstractmethod - def sign( - self, - data: utils.Buffer, - signature_algorithm: EllipticCurveSignatureAlgorithm, - ) -> bytes: - """ - Signs the data - """ - - @abc.abstractmethod - def private_numbers(self) -> EllipticCurvePrivateNumbers: - """ - Returns an EllipticCurvePrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def __copy__(self) -> EllipticCurvePrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> EllipticCurvePrivateKey: - """ - Returns a deep copy. - """ - - -EllipticCurvePrivateKeyWithSerialization = EllipticCurvePrivateKey -EllipticCurvePrivateKey.register(rust_openssl.ec.ECPrivateKey) - - -class EllipticCurvePublicKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def curve(self) -> EllipticCurve: - """ - The EllipticCurve that this key is on. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - Bit size of a secret scalar for the curve. - """ - - @abc.abstractmethod - def public_numbers(self) -> EllipticCurvePublicNumbers: - """ - Returns an EllipticCurvePublicNumbers. - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def verify( - self, - signature: utils.Buffer, - data: utils.Buffer, - signature_algorithm: EllipticCurveSignatureAlgorithm, - ) -> None: - """ - Verifies the signature of the data. - """ - - @classmethod - def from_encoded_point( - cls, curve: EllipticCurve, data: bytes - ) -> EllipticCurvePublicKey: - utils._check_bytes("data", data) - - if len(data) == 0: - raise ValueError("data must not be an empty byte string") - - if data[0] not in [0x02, 0x03, 0x04]: - raise ValueError("Unsupported elliptic curve point type") - - return rust_openssl.ec.from_public_bytes(curve, data) - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> EllipticCurvePublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> EllipticCurvePublicKey: - """ - Returns a deep copy. - """ - - -EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey -EllipticCurvePublicKey.register(rust_openssl.ec.ECPublicKey) - -EllipticCurvePrivateNumbers = rust_openssl.ec.EllipticCurvePrivateNumbers -EllipticCurvePublicNumbers = rust_openssl.ec.EllipticCurvePublicNumbers - - -class SECP521R1(EllipticCurve): - name = "secp521r1" - key_size = 521 - group_order = 0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409 # noqa: E501 - - -class SECP384R1(EllipticCurve): - name = "secp384r1" - key_size = 384 - group_order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973 # noqa: E501 - - -class SECP256R1(EllipticCurve): - name = "secp256r1" - key_size = 256 - group_order = ( - 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 - ) - - -class SECP256K1(EllipticCurve): - name = "secp256k1" - key_size = 256 - group_order = ( - 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - ) - - -class SECP224R1(EllipticCurve): - name = "secp224r1" - key_size = 224 - group_order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D - - -class SECP192R1(EllipticCurve): - name = "secp192r1" - key_size = 192 - group_order = 0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831 - - -class BrainpoolP256R1(EllipticCurve): - name = "brainpoolP256r1" - key_size = 256 - group_order = ( - 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - ) - - -class BrainpoolP384R1(EllipticCurve): - name = "brainpoolP384r1" - key_size = 384 - group_order = 0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565 # noqa: E501 - - -class BrainpoolP512R1(EllipticCurve): - name = "brainpoolP512r1" - key_size = 512 - group_order = 0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069 # noqa: E501 - - -_CURVE_TYPES: dict[str, EllipticCurve] = { - "prime192v1": SECP192R1(), - "prime256v1": SECP256R1(), - "secp192r1": SECP192R1(), - "secp224r1": SECP224R1(), - "secp256r1": SECP256R1(), - "secp384r1": SECP384R1(), - "secp521r1": SECP521R1(), - "secp256k1": SECP256K1(), - "brainpoolP256r1": BrainpoolP256R1(), - "brainpoolP384r1": BrainpoolP384R1(), - "brainpoolP512r1": BrainpoolP512R1(), -} - - -class ECDSA(EllipticCurveSignatureAlgorithm): - def __init__( - self, - algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, - deterministic_signing: bool = False, - ): - from cryptography.hazmat.backends.openssl.backend import backend - - if ( - deterministic_signing - and not backend.ecdsa_deterministic_supported() - ): - raise UnsupportedAlgorithm( - "ECDSA with deterministic signature (RFC 6979) is not " - "supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - self._algorithm = algorithm - self._deterministic_signing = deterministic_signing - - @property - def algorithm( - self, - ) -> asym_utils.Prehashed | hashes.HashAlgorithm: - return self._algorithm - - @property - def deterministic_signing( - self, - ) -> bool: - return self._deterministic_signing - - -generate_private_key = rust_openssl.ec.generate_private_key - - -def derive_private_key( - private_value: int, - curve: EllipticCurve, - backend: typing.Any = None, -) -> EllipticCurvePrivateKey: - if not isinstance(private_value, int): - raise TypeError("private_value must be an integer type.") - - if private_value <= 0: - raise ValueError("private_value must be a positive integer.") - - return rust_openssl.ec.derive_private_key(private_value, curve) - - -class ECDH: - pass - - -_OID_TO_CURVE = { - EllipticCurveOID.SECP192R1: SECP192R1, - EllipticCurveOID.SECP224R1: SECP224R1, - EllipticCurveOID.SECP256K1: SECP256K1, - EllipticCurveOID.SECP256R1: SECP256R1, - EllipticCurveOID.SECP384R1: SECP384R1, - EllipticCurveOID.SECP521R1: SECP521R1, - EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1, - EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1, - EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1, -} - - -def get_curve_for_oid(oid: ObjectIdentifier) -> type[EllipticCurve]: - try: - return _OID_TO_CURVE[oid] - except KeyError: - raise LookupError( - "The provided object identifier has no matching elliptic " - "curve class" - ) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py deleted file mode 100644 index 70aec5b1..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py +++ /dev/null @@ -1,116 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization -from cryptography.utils import Buffer - - -class Ed25519PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> Ed25519PublicKey: - return rust_openssl.ed25519.from_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - """ - - @abc.abstractmethod - def verify(self, signature: Buffer, data: Buffer) -> None: - """ - Verify the signature. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> Ed25519PublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> Ed25519PublicKey: - """ - Returns a deep copy. - """ - - -Ed25519PublicKey.register(rust_openssl.ed25519.Ed25519PublicKey) - - -class Ed25519PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> Ed25519PrivateKey: - return rust_openssl.ed25519.generate_key() - - @classmethod - def from_private_bytes(cls, data: Buffer) -> Ed25519PrivateKey: - return rust_openssl.ed25519.from_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> Ed25519PublicKey: - """ - The Ed25519PublicKey derived from the private key. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - """ - - @abc.abstractmethod - def sign(self, data: Buffer) -> bytes: - """ - Signs the data. - """ - - @abc.abstractmethod - def __copy__(self) -> Ed25519PrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> Ed25519PrivateKey: - """ - Returns a deep copy. - """ - - -Ed25519PrivateKey.register(rust_openssl.ed25519.Ed25519PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py deleted file mode 100644 index 9ecb478b..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py +++ /dev/null @@ -1,143 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization -from cryptography.utils import Buffer - - -class Ed448PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> Ed448PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed448_supported(): - raise UnsupportedAlgorithm( - "ed448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.ed448.from_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - """ - - @abc.abstractmethod - def verify(self, signature: Buffer, data: Buffer) -> None: - """ - Verify the signature. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> Ed448PublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> Ed448PublicKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "ed448"): - Ed448PublicKey.register(rust_openssl.ed448.Ed448PublicKey) - - -class Ed448PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> Ed448PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed448_supported(): - raise UnsupportedAlgorithm( - "ed448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.ed448.generate_key() - - @classmethod - def from_private_bytes(cls, data: Buffer) -> Ed448PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed448_supported(): - raise UnsupportedAlgorithm( - "ed448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.ed448.from_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> Ed448PublicKey: - """ - The Ed448PublicKey derived from the private key. - """ - - @abc.abstractmethod - def sign(self, data: Buffer) -> bytes: - """ - Signs the data. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - """ - - @abc.abstractmethod - def __copy__(self) -> Ed448PrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> Ed448PrivateKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "x448"): - Ed448PrivateKey.register(rust_openssl.ed448.Ed448PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mldsa.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mldsa.py deleted file mode 100644 index 9c529493..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mldsa.py +++ /dev/null @@ -1,504 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization -from cryptography.utils import Buffer - - -class MLDSA44PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> MLDSA44PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mldsa_supported(): - raise UnsupportedAlgorithm( - "ML-DSA-44 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mldsa.from_mldsa44_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - - The public key is 1,312 bytes for MLDSA-44. - """ - - @abc.abstractmethod - def verify( - self, - signature: Buffer, - data: Buffer, - context: Buffer | None = None, - ) -> None: - """ - Verify the signature. - """ - - @abc.abstractmethod - def verify_mu( - self, - signature: Buffer, - mu: Buffer, - ) -> None: - """ - Verify the signature over a precomputed mu (message representative). - - mu must be 64 bytes. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> MLDSA44PublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> MLDSA44PublicKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "mldsa"): - MLDSA44PublicKey.register(rust_openssl.mldsa.MLDSA44PublicKey) - - -class MLDSA44PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> MLDSA44PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mldsa_supported(): - raise UnsupportedAlgorithm( - "ML-DSA-44 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mldsa.generate_mldsa44_key() - - @classmethod - def from_seed_bytes(cls, data: Buffer) -> MLDSA44PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mldsa_supported(): - raise UnsupportedAlgorithm( - "ML-DSA-44 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mldsa.from_mldsa44_seed_bytes(data) - - @abc.abstractmethod - def public_key(self) -> MLDSA44PublicKey: - """ - The MLDSA44PublicKey derived from the private key. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - - This method only returns the serialization of the seed form of the - private key, never the expanded one. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - - This method only returns the seed form of the private key (32 bytes). - """ - - @abc.abstractmethod - def sign(self, data: Buffer, context: Buffer | None = None) -> bytes: - """ - Signs the data. - """ - - @abc.abstractmethod - def sign_mu(self, mu: Buffer) -> bytes: - """ - Signs a precomputed mu (message representative). - - mu must be 64 bytes and already incorporates the context, so no - context is accepted here. - """ - - @abc.abstractmethod - def __copy__(self) -> MLDSA44PrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> MLDSA44PrivateKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "mldsa"): - MLDSA44PrivateKey.register(rust_openssl.mldsa.MLDSA44PrivateKey) - - -class MLDSA65PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> MLDSA65PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mldsa_supported(): - raise UnsupportedAlgorithm( - "ML-DSA-65 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mldsa.from_mldsa65_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - - The public key is 1,952 bytes for MLDSA-65. - """ - - @abc.abstractmethod - def verify( - self, - signature: Buffer, - data: Buffer, - context: Buffer | None = None, - ) -> None: - """ - Verify the signature. - """ - - @abc.abstractmethod - def verify_mu( - self, - signature: Buffer, - mu: Buffer, - ) -> None: - """ - Verify the signature over a precomputed mu (message representative). - - mu must be 64 bytes. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> MLDSA65PublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> MLDSA65PublicKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "mldsa"): - MLDSA65PublicKey.register(rust_openssl.mldsa.MLDSA65PublicKey) - - -class MLDSA65PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> MLDSA65PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mldsa_supported(): - raise UnsupportedAlgorithm( - "ML-DSA-65 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mldsa.generate_mldsa65_key() - - @classmethod - def from_seed_bytes(cls, data: Buffer) -> MLDSA65PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mldsa_supported(): - raise UnsupportedAlgorithm( - "ML-DSA-65 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mldsa.from_mldsa65_seed_bytes(data) - - @abc.abstractmethod - def public_key(self) -> MLDSA65PublicKey: - """ - The MLDSA65PublicKey derived from the private key. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - - This method only returns the serialization of the seed form of the - private key, never the expanded one. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - - This method only returns the seed form of the private key (32 bytes). - """ - - @abc.abstractmethod - def sign(self, data: Buffer, context: Buffer | None = None) -> bytes: - """ - Signs the data. - """ - - @abc.abstractmethod - def sign_mu(self, mu: Buffer) -> bytes: - """ - Signs a precomputed mu (message representative). - - mu must be 64 bytes and already incorporates the context, so no - context is accepted here. - """ - - @abc.abstractmethod - def __copy__(self) -> MLDSA65PrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> MLDSA65PrivateKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "mldsa"): - MLDSA65PrivateKey.register(rust_openssl.mldsa.MLDSA65PrivateKey) - - -class MLDSA87PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> MLDSA87PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mldsa_supported(): - raise UnsupportedAlgorithm( - "ML-DSA-87 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mldsa.from_mldsa87_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - - The public key is 2,592 bytes for MLDSA-87. - """ - - @abc.abstractmethod - def verify( - self, - signature: Buffer, - data: Buffer, - context: Buffer | None = None, - ) -> None: - """ - Verify the signature. - """ - - @abc.abstractmethod - def verify_mu( - self, - signature: Buffer, - mu: Buffer, - ) -> None: - """ - Verify the signature over a precomputed mu (message representative). - - mu must be 64 bytes. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> MLDSA87PublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> MLDSA87PublicKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "mldsa"): - MLDSA87PublicKey.register(rust_openssl.mldsa.MLDSA87PublicKey) - - -class MLDSA87PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> MLDSA87PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mldsa_supported(): - raise UnsupportedAlgorithm( - "ML-DSA-87 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mldsa.generate_mldsa87_key() - - @classmethod - def from_seed_bytes(cls, data: Buffer) -> MLDSA87PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mldsa_supported(): - raise UnsupportedAlgorithm( - "ML-DSA-87 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mldsa.from_mldsa87_seed_bytes(data) - - @abc.abstractmethod - def public_key(self) -> MLDSA87PublicKey: - """ - The MLDSA87PublicKey derived from the private key. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - - This method only returns the serialization of the seed form of the - private key, never the expanded one. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - - This method only returns the seed form of the private key (32 bytes). - """ - - @abc.abstractmethod - def sign(self, data: Buffer, context: Buffer | None = None) -> bytes: - """ - Signs the data. - """ - - @abc.abstractmethod - def sign_mu(self, mu: Buffer) -> bytes: - """ - Signs a precomputed mu (message representative). - - mu must be 64 bytes and already incorporates the context, so no - context is accepted here. - """ - - @abc.abstractmethod - def __copy__(self) -> MLDSA87PrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> MLDSA87PrivateKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "mldsa"): - MLDSA87PrivateKey.register(rust_openssl.mldsa.MLDSA87PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mlkem.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mlkem.py deleted file mode 100644 index 64bca115..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/mlkem.py +++ /dev/null @@ -1,278 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization -from cryptography.utils import Buffer - - -class MLKEM768PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: Buffer) -> MLKEM768PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mlkem_supported(): - raise UnsupportedAlgorithm( - "ML-KEM-768 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mlkem.from_mlkem768_public_bytes(data) - - @abc.abstractmethod - def encapsulate(self) -> tuple[bytes, bytes]: - """ - Encapsulate: returns (shared_secret, ciphertext). - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - - The public key is 1,184 bytes for ML-KEM-768. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> MLKEM768PublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> MLKEM768PublicKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "mlkem"): - MLKEM768PublicKey.register(rust_openssl.mlkem.MLKEM768PublicKey) - - -class MLKEM768PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> MLKEM768PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mlkem_supported(): - raise UnsupportedAlgorithm( - "ML-KEM-768 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mlkem.generate_mlkem768_key() - - @classmethod - def from_seed_bytes(cls, data: Buffer) -> MLKEM768PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mlkem_supported(): - raise UnsupportedAlgorithm( - "ML-KEM-768 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mlkem.from_mlkem768_seed_bytes(data) - - @abc.abstractmethod - def decapsulate(self, ciphertext: Buffer) -> bytes: - """ - Decapsulate: returns shared_secret. - """ - - @abc.abstractmethod - def public_key(self) -> MLKEM768PublicKey: - """ - The MLKEM768PublicKey derived from this private key. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key (64-byte seed). - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - """ - - @abc.abstractmethod - def __copy__(self) -> MLKEM768PrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> MLKEM768PrivateKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "mlkem"): - MLKEM768PrivateKey.register(rust_openssl.mlkem.MLKEM768PrivateKey) - - -class MLKEM1024PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: Buffer) -> MLKEM1024PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mlkem_supported(): - raise UnsupportedAlgorithm( - "ML-KEM-1024 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mlkem.from_mlkem1024_public_bytes(data) - - @abc.abstractmethod - def encapsulate(self) -> tuple[bytes, bytes]: - """ - Encapsulate: returns (shared_secret, ciphertext). - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - - The public key is 1,568 bytes for ML-KEM-1024. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> MLKEM1024PublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> MLKEM1024PublicKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "mlkem"): - MLKEM1024PublicKey.register(rust_openssl.mlkem.MLKEM1024PublicKey) - - -class MLKEM1024PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> MLKEM1024PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mlkem_supported(): - raise UnsupportedAlgorithm( - "ML-KEM-1024 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mlkem.generate_mlkem1024_key() - - @classmethod - def from_seed_bytes(cls, data: Buffer) -> MLKEM1024PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.mlkem_supported(): - raise UnsupportedAlgorithm( - "ML-KEM-1024 is not supported by this backend.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.mlkem.from_mlkem1024_seed_bytes(data) - - @abc.abstractmethod - def decapsulate(self, ciphertext: Buffer) -> bytes: - """ - Decapsulate: returns shared_secret. - """ - - @abc.abstractmethod - def public_key(self) -> MLKEM1024PublicKey: - """ - The MLKEM1024PublicKey derived from this private key. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key (64-byte seed). - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - """ - - @abc.abstractmethod - def __copy__(self) -> MLKEM1024PrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> MLKEM1024PrivateKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "mlkem"): - MLKEM1024PrivateKey.register(rust_openssl.mlkem.MLKEM1024PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py deleted file mode 100644 index 5121a288..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py +++ /dev/null @@ -1,111 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives._asymmetric import ( - AsymmetricPadding as AsymmetricPadding, -) -from cryptography.hazmat.primitives.asymmetric import rsa - - -class PKCS1v15(AsymmetricPadding): - name = "EMSA-PKCS1-v1_5" - - -class _MaxLength: - "Sentinel value for `MAX_LENGTH`." - - -class _Auto: - "Sentinel value for `AUTO`." - - -class _DigestLength: - "Sentinel value for `DIGEST_LENGTH`." - - -class PSS(AsymmetricPadding): - MAX_LENGTH = _MaxLength() - AUTO = _Auto() - DIGEST_LENGTH = _DigestLength() - name = "EMSA-PSS" - _salt_length: int | _MaxLength | _Auto | _DigestLength - - def __init__( - self, - mgf: MGF, - salt_length: int | _MaxLength | _Auto | _DigestLength, - ) -> None: - self._mgf = mgf - - if not isinstance( - salt_length, (int, _MaxLength, _Auto, _DigestLength) - ): - raise TypeError( - "salt_length must be an integer, MAX_LENGTH, " - "DIGEST_LENGTH, or AUTO" - ) - - if isinstance(salt_length, int) and salt_length < 0: - raise ValueError("salt_length must be zero or greater.") - - self._salt_length = salt_length - - @property - def mgf(self) -> MGF: - return self._mgf - - -class OAEP(AsymmetricPadding): - name = "EME-OAEP" - - def __init__( - self, - mgf: MGF, - algorithm: hashes.HashAlgorithm, - label: bytes | None, - ): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of hashes.HashAlgorithm.") - - self._mgf = mgf - self._algorithm = algorithm - self._label = label - - @property - def algorithm(self) -> hashes.HashAlgorithm: - return self._algorithm - - @property - def mgf(self) -> MGF: - return self._mgf - - -class MGF(metaclass=abc.ABCMeta): - _algorithm: hashes.HashAlgorithm - - -class MGF1(MGF): - def __init__(self, algorithm: hashes.HashAlgorithm): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of hashes.HashAlgorithm.") - - self._algorithm = algorithm - - -def calculate_max_pss_salt_length( - key: rsa.RSAPrivateKey | rsa.RSAPublicKey, - hash_algorithm: hashes.HashAlgorithm, -) -> int: - if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): - raise TypeError("key must be an RSA public or private key") - # bit length - 1 per RFC 3447 - emlen = (key.key_size + 6) // 8 - salt_length = emlen - hash_algorithm.digest_size - 2 - assert salt_length >= 0 - return salt_length diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py deleted file mode 100644 index d730cebd..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py +++ /dev/null @@ -1,295 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import random -import typing -from math import gcd, lcm - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization, hashes -from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils - - -class RSAPrivateKey(metaclass=abc.ABCMeta): - @abc.abstractmethod - def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes: - """ - Decrypts the provided ciphertext. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the public modulus. - """ - - @abc.abstractmethod - def public_key(self) -> RSAPublicKey: - """ - The RSAPublicKey associated with this private key. - """ - - @abc.abstractmethod - def sign( - self, - data: bytes, - padding: AsymmetricPadding, - algorithm: asym_utils.Prehashed - | hashes.HashAlgorithm - | asym_utils.NoDigestInfo, - ) -> bytes: - """ - Signs the data. - """ - - @abc.abstractmethod - def private_numbers(self) -> RSAPrivateNumbers: - """ - Returns an RSAPrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def __copy__(self) -> RSAPrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> RSAPrivateKey: - """ - Returns a deep copy. - """ - - -RSAPrivateKeyWithSerialization = RSAPrivateKey -RSAPrivateKey.register(rust_openssl.rsa.RSAPrivateKey) - - -class RSAPublicKey(metaclass=abc.ABCMeta): - @abc.abstractmethod - def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes: - """ - Encrypts the given plaintext. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the public modulus. - """ - - @abc.abstractmethod - def public_numbers(self) -> RSAPublicNumbers: - """ - Returns an RSAPublicNumbers - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def verify( - self, - signature: bytes, - data: bytes, - padding: AsymmetricPadding, - algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, - ) -> None: - """ - Verifies the signature of the data. - """ - - @abc.abstractmethod - def recover_data_from_signature( - self, - signature: bytes, - padding: AsymmetricPadding, - algorithm: hashes.HashAlgorithm | asym_utils.NoDigestInfo | None, - ) -> bytes: - """ - Recovers the original data from the signature. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> RSAPublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> RSAPublicKey: - """ - Returns a deep copy. - """ - - -RSAPublicKeyWithSerialization = RSAPublicKey -RSAPublicKey.register(rust_openssl.rsa.RSAPublicKey) - -RSAPrivateNumbers = rust_openssl.rsa.RSAPrivateNumbers -RSAPublicNumbers = rust_openssl.rsa.RSAPublicNumbers - - -def generate_private_key( - public_exponent: int, - key_size: int, - backend: typing.Any = None, -) -> RSAPrivateKey: - _verify_rsa_parameters(public_exponent, key_size) - return rust_openssl.rsa.generate_private_key(public_exponent, key_size) - - -def _verify_rsa_parameters(public_exponent: int, key_size: int) -> None: - if public_exponent not in (3, 65537): - raise ValueError( - "public_exponent must be either 3 (for legacy compatibility) or " - "65537. Almost everyone should choose 65537 here!" - ) - - if key_size < 1024: - raise ValueError("key_size must be at least 1024-bits.") - - -def _modinv(e: int, m: int) -> int: - """ - Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 - """ - x1, x2 = 1, 0 - a, b = e, m - while b > 0: - q, r = divmod(a, b) - xn = x1 - q * x2 - a, b, x1, x2 = b, r, x2, xn - return x1 % m - - -def rsa_crt_iqmp(p: int, q: int) -> int: - """ - Compute the CRT (q ** -1) % p value from RSA primes p and q. - """ - if p <= 1 or q <= 1: - raise ValueError("Values can't be <= 1") - return _modinv(q, p) - - -def rsa_crt_dmp1(private_exponent: int, p: int) -> int: - """ - Compute the CRT private_exponent % (p - 1) value from the RSA - private_exponent (d) and p. - """ - if private_exponent <= 1 or p <= 1: - raise ValueError("Values can't be <= 1") - return private_exponent % (p - 1) - - -def rsa_crt_dmq1(private_exponent: int, q: int) -> int: - """ - Compute the CRT private_exponent % (q - 1) value from the RSA - private_exponent (d) and q. - """ - if private_exponent <= 1 or q <= 1: - raise ValueError("Values can't be <= 1") - return private_exponent % (q - 1) - - -def rsa_recover_private_exponent(e: int, p: int, q: int) -> int: - """ - Compute the RSA private_exponent (d) given the public exponent (e) - and the RSA primes p and q. - - This uses the Carmichael totient function to generate the - smallest possible working value of the private exponent. - """ - # This lambda_n is the Carmichael totient function. - # The original RSA paper uses the Euler totient function - # here: phi_n = (p - 1) * (q - 1) - # Either version of the private exponent will work, but the - # one generated by the older formulation may be larger - # than necessary. (lambda_n always divides phi_n) - if e <= 1 or p <= 1 or q <= 1: - raise ValueError("Values can't be <= 1") - return _modinv(e, lcm(p - 1, q - 1)) - - -# Controls the number of iterations rsa_recover_prime_factors will perform -# to obtain the prime factors. -_MAX_RECOVERY_ATTEMPTS = 500 - - -def rsa_recover_prime_factors(n: int, e: int, d: int) -> tuple[int, int]: - """ - Compute factors p and q from the private exponent d. We assume that n has - no more than two factors. This function is adapted from code in PyCrypto. - """ - # reject invalid values early - if d <= 1 or e <= 1: - raise ValueError("d, e can't be <= 1") - if 17 != pow(17, e * d, n): - raise ValueError("n, d, e don't match") - # See 8.2.2(i) in Handbook of Applied Cryptography. - ktot = d * e - 1 - # The quantity d*e-1 is a multiple of phi(n), even, - # and can be represented as t*2^s. - t = ktot - while t % 2 == 0: - t = t // 2 - # Cycle through all multiplicative inverses in Zn. - # The algorithm is non-deterministic, but there is a 50% chance - # any candidate a leads to successful factoring. - # See "Digitalized Signatures and Public Key Functions as Intractable - # as Factorization", M. Rabin, 1979 - spotted = False - tries = 0 - while not spotted and tries < _MAX_RECOVERY_ATTEMPTS: - a = random.randint(2, n - 1) - tries += 1 - k = t - # Cycle through all values a^{t*2^i}=a^k - while k < ktot: - cand = pow(a, k, n) - # Check if a^k is a non-trivial root of unity (mod n) - if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1: - # We have found a number such that (cand-1)(cand+1)=0 (mod n). - # Either of the terms divides n. - p = gcd(cand + 1, n) - spotted = True - break - k *= 2 - if not spotted: - raise ValueError("Unable to compute factors p and q from exponent d.") - # Found ! - q, r = divmod(n, p) - assert r == 0 - p, q = sorted((p, q), reverse=True) - return (p, q) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py deleted file mode 100644 index 6555e35c..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/types.py +++ /dev/null @@ -1,91 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography.hazmat.primitives.asymmetric import ( - dh, - dsa, - ec, - ed448, - ed25519, - mldsa, - mlkem, - rsa, - x448, - x25519, -) - -# Every asymmetric key type -PublicKeyTypes = typing.Union[ - dh.DHPublicKey, - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - mldsa.MLDSA44PublicKey, - mldsa.MLDSA65PublicKey, - mldsa.MLDSA87PublicKey, - mlkem.MLKEM768PublicKey, - mlkem.MLKEM1024PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, -] -# Every asymmetric key type -PrivateKeyTypes = typing.Union[ - dh.DHPrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - mldsa.MLDSA44PrivateKey, - mldsa.MLDSA65PrivateKey, - mldsa.MLDSA87PrivateKey, - mlkem.MLKEM768PrivateKey, - mlkem.MLKEM1024PrivateKey, - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - x25519.X25519PrivateKey, - x448.X448PrivateKey, -] -# Just the key types we allow to be used for x509 signing. This mirrors -# the certificate public key types -CertificateIssuerPrivateKeyTypes = typing.Union[ - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - mldsa.MLDSA44PrivateKey, - mldsa.MLDSA65PrivateKey, - mldsa.MLDSA87PrivateKey, -] -# Just the key types we allow to be used for x509 signing. This mirrors -# the certificate private key types -CertificateIssuerPublicKeyTypes = typing.Union[ - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - mldsa.MLDSA44PublicKey, - mldsa.MLDSA65PublicKey, - mldsa.MLDSA87PublicKey, -] -# This type removes DHPublicKey. x448/x25519 can be a public key -# but cannot be used in signing so they are allowed here. -CertificatePublicKeyTypes = typing.Union[ - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - mldsa.MLDSA44PublicKey, - mldsa.MLDSA65PublicKey, - mldsa.MLDSA87PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, -] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py deleted file mode 100644 index c01c3427..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py +++ /dev/null @@ -1,28 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import asn1 -from cryptography.hazmat.primitives import hashes - -decode_dss_signature = asn1.decode_dss_signature -encode_dss_signature = asn1.encode_dss_signature - - -class NoDigestInfo: - pass - - -class Prehashed: - def __init__(self, algorithm: hashes.HashAlgorithm): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of HashAlgorithm.") - - self._algorithm = algorithm - self._digest_size = algorithm.digest_size - - @property - def digest_size(self) -> int: - return self._digest_size diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py deleted file mode 100644 index 74989988..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py +++ /dev/null @@ -1,134 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization -from cryptography.utils import Buffer - - -class X25519PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> X25519PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x25519_supported(): - raise UnsupportedAlgorithm( - "X25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return rust_openssl.x25519.from_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> X25519PublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> X25519PublicKey: - """ - Returns a deep copy. - """ - - -X25519PublicKey.register(rust_openssl.x25519.X25519PublicKey) - - -class X25519PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> X25519PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x25519_supported(): - raise UnsupportedAlgorithm( - "X25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - return rust_openssl.x25519.generate_key() - - @classmethod - def from_private_bytes(cls, data: Buffer) -> X25519PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x25519_supported(): - raise UnsupportedAlgorithm( - "X25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return rust_openssl.x25519.from_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> X25519PublicKey: - """ - Returns the public key associated with this private key - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - """ - - @abc.abstractmethod - def exchange(self, peer_public_key: X25519PublicKey) -> bytes: - """ - Performs a key exchange operation using the provided peer's public key. - """ - - @abc.abstractmethod - def __copy__(self) -> X25519PrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> X25519PrivateKey: - """ - Returns a deep copy. - """ - - -X25519PrivateKey.register(rust_openssl.x25519.X25519PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py deleted file mode 100644 index b9dc8261..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py +++ /dev/null @@ -1,137 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization -from cryptography.utils import Buffer - - -class X448PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> X448PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x448_supported(): - raise UnsupportedAlgorithm( - "X448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return rust_openssl.x448.from_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __copy__(self) -> X448PublicKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> X448PublicKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "x448"): - X448PublicKey.register(rust_openssl.x448.X448PublicKey) - - -class X448PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> X448PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x448_supported(): - raise UnsupportedAlgorithm( - "X448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return rust_openssl.x448.generate_key() - - @classmethod - def from_private_bytes(cls, data: Buffer) -> X448PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x448_supported(): - raise UnsupportedAlgorithm( - "X448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return rust_openssl.x448.from_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> X448PublicKey: - """ - Returns the public key associated with this private key - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - """ - - @abc.abstractmethod - def exchange(self, peer_public_key: X448PublicKey) -> bytes: - """ - Performs a key exchange operation using the provided peer's public key. - """ - - @abc.abstractmethod - def __copy__(self) -> X448PrivateKey: - """ - Returns a copy. - """ - - @abc.abstractmethod - def __deepcopy__(self, memo: dict) -> X448PrivateKey: - """ - Returns a deep copy. - """ - - -if hasattr(rust_openssl, "x448"): - X448PrivateKey.register(rust_openssl.x448.X448PrivateKey) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py deleted file mode 100644 index 10c15d0f..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.primitives._cipheralgorithm import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) -from cryptography.hazmat.primitives.ciphers.base import ( - AEADCipherContext, - AEADDecryptionContext, - AEADEncryptionContext, - Cipher, - CipherContext, -) - -__all__ = [ - "AEADCipherContext", - "AEADDecryptionContext", - "AEADEncryptionContext", - "BlockCipherAlgorithm", - "Cipher", - "CipherAlgorithm", - "CipherContext", -] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py deleted file mode 100644 index c8a582d7..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/aead.py +++ /dev/null @@ -1,23 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -__all__ = [ - "AESCCM", - "AESGCM", - "AESGCMSIV", - "AESOCB3", - "AESSIV", - "ChaCha20Poly1305", -] - -AESGCM = rust_openssl.aead.AESGCM -ChaCha20Poly1305 = rust_openssl.aead.ChaCha20Poly1305 -AESCCM = rust_openssl.aead.AESCCM -AESSIV = rust_openssl.aead.AESSIV -AESOCB3 = rust_openssl.aead.AESOCB3 -AESGCMSIV = rust_openssl.aead.AESGCMSIV diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py deleted file mode 100644 index 6b20dd57..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py +++ /dev/null @@ -1,138 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography import utils -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - ARC4 as ARC4, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - CAST5 as CAST5, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - IDEA as IDEA, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - SEED as SEED, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - Blowfish as Blowfish, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - Camellia as Camellia, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - TripleDES as TripleDES, -) -from cryptography.hazmat.primitives._cipheralgorithm import _verify_key_size -from cryptography.hazmat.primitives.ciphers import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) - - -class AES(BlockCipherAlgorithm): - name = "AES" - block_size = 128 - # 512 added to support AES-256-XTS, which uses 512-bit keys - key_sizes = frozenset([128, 192, 256, 512]) - - def __init__(self, key: utils.Buffer): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class AES128(BlockCipherAlgorithm): - name = "AES" - block_size = 128 - key_sizes = frozenset([128]) - key_size = 128 - - def __init__(self, key: utils.Buffer): - self.key = _verify_key_size(self, key) - - -class AES256(BlockCipherAlgorithm): - name = "AES" - block_size = 128 - key_sizes = frozenset([256]) - key_size = 256 - - def __init__(self, key: utils.Buffer): - self.key = _verify_key_size(self, key) - - -utils.deprecated( - Camellia, - __name__, - "Camellia has been moved to " - "cryptography.hazmat.decrepit.ciphers.algorithms.Camellia and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.algorithms in 49.0.0.", - utils.DeprecatedIn43, - name="Camellia", -) - - -utils.deprecated( - ARC4, - __name__, - "ARC4 has been moved to " - "cryptography.hazmat.decrepit.ciphers.algorithms.ARC4 and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0.", - utils.DeprecatedIn43, - name="ARC4", -) - - -utils.deprecated( - TripleDES, - __name__, - "TripleDES has been moved to " - "cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0.", - utils.DeprecatedIn43, - name="TripleDES", -) - - -class ChaCha20(CipherAlgorithm): - name = "ChaCha20" - key_sizes = frozenset([256]) - - def __init__(self, key: utils.Buffer, nonce: utils.Buffer): - self.key = _verify_key_size(self, key) - utils._check_byteslike("nonce", nonce) - - if len(nonce) != 16: - raise ValueError("nonce must be 128-bits (16 bytes)") - - self._nonce = nonce - - @property - def nonce(self) -> utils.Buffer: - return self._nonce - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class SM4(BlockCipherAlgorithm): - name = "SM4" - block_size = 128 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py deleted file mode 100644 index 24fceea2..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/base.py +++ /dev/null @@ -1,146 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import typing - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives._cipheralgorithm import CipherAlgorithm -from cryptography.hazmat.primitives.ciphers import modes -from cryptography.utils import Buffer - - -class CipherContext(metaclass=abc.ABCMeta): - @abc.abstractmethod - def update(self, data: Buffer) -> bytes: - """ - Processes the provided bytes through the cipher and returns the results - as bytes. - """ - - @abc.abstractmethod - def update_into(self, data: Buffer, buf: Buffer) -> int: - """ - Processes the provided bytes and writes the resulting data into the - provided buffer. Returns the number of bytes written. - """ - - @abc.abstractmethod - def finalize(self) -> bytes: - """ - Returns the results of processing the final block as bytes. - """ - - @abc.abstractmethod - def reset_nonce(self, nonce: bytes) -> None: - """ - Resets the nonce for the cipher context to the provided value. - Raises an exception if it does not support reset or if the - provided nonce does not have a valid length. - """ - - -class AEADCipherContext(CipherContext, metaclass=abc.ABCMeta): - @abc.abstractmethod - def authenticate_additional_data(self, data: Buffer) -> None: - """ - Authenticates the provided bytes. - """ - - -class AEADDecryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): - @abc.abstractmethod - def finalize_with_tag(self, tag: bytes) -> bytes: - """ - Returns the results of processing the final block as bytes and allows - delayed passing of the authentication tag. - """ - - -class AEADEncryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def tag(self) -> bytes: - """ - Returns tag bytes. This is only available after encryption is - finalized. - """ - - -Mode = typing.TypeVar( - "Mode", bound=typing.Optional[modes.Mode], covariant=True -) - - -class Cipher(typing.Generic[Mode]): - def __init__( - self, - algorithm: CipherAlgorithm, - mode: Mode, - backend: typing.Any = None, - ) -> None: - if not isinstance(algorithm, CipherAlgorithm): - raise TypeError("Expected interface of CipherAlgorithm.") - - if mode is not None: - # mypy needs this assert to narrow the type from our generic - # type. Maybe it won't some time in the future. - assert isinstance(mode, modes.Mode) - mode.validate_for_algorithm(algorithm) - - self.algorithm = algorithm - self.mode = mode - - @typing.overload - def encryptor( - self: Cipher[modes.ModeWithAuthenticationTag], - ) -> AEADEncryptionContext: ... - - @typing.overload - def encryptor( - self: _CIPHER_TYPE, - ) -> CipherContext: ... - - def encryptor(self): - if isinstance(self.mode, modes.ModeWithAuthenticationTag): - if self.mode.tag is not None: - raise ValueError( - "Authentication tag must be None when encrypting." - ) - - return rust_openssl.ciphers.create_encryption_ctx( - self.algorithm, self.mode - ) - - @typing.overload - def decryptor( - self: Cipher[modes.ModeWithAuthenticationTag], - ) -> AEADDecryptionContext: ... - - @typing.overload - def decryptor( - self: _CIPHER_TYPE, - ) -> CipherContext: ... - - def decryptor(self): - return rust_openssl.ciphers.create_decryption_ctx( - self.algorithm, self.mode - ) - - -_CIPHER_TYPE = Cipher[ - typing.Union[ - modes.ModeWithNonce, - modes.ModeWithTweak, - modes.ECB, - modes.ModeWithInitializationVector, - None, - ] -] - -CipherContext.register(rust_openssl.ciphers.CipherContext) -AEADEncryptionContext.register(rust_openssl.ciphers.AEADEncryptionContext) -AEADDecryptionContext.register(rust_openssl.ciphers.AEADDecryptionContext) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py deleted file mode 100644 index ddf01701..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/ciphers/modes.py +++ /dev/null @@ -1,192 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.decrepit.ciphers.modes import CFB as CFB -from cryptography.hazmat.decrepit.ciphers.modes import CFB8 as CFB8 -from cryptography.hazmat.decrepit.ciphers.modes import OFB as OFB -from cryptography.hazmat.primitives._cipheralgorithm import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) -from cryptography.hazmat.primitives._modes import ( - Mode as Mode, -) -from cryptography.hazmat.primitives._modes import ( - ModeWithAuthenticationTag as ModeWithAuthenticationTag, -) -from cryptography.hazmat.primitives._modes import ( - ModeWithInitializationVector as ModeWithInitializationVector, -) -from cryptography.hazmat.primitives._modes import ( - ModeWithNonce as ModeWithNonce, -) -from cryptography.hazmat.primitives._modes import ( - ModeWithTweak as ModeWithTweak, -) -from cryptography.hazmat.primitives._modes import ( - _check_aes_key_length, - _check_iv_and_key_length, - _check_nonce_length, -) -from cryptography.hazmat.primitives.ciphers import algorithms - - -class CBC(ModeWithInitializationVector): - name = "CBC" - - def __init__(self, initialization_vector: utils.Buffer): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> utils.Buffer: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class XTS(ModeWithTweak): - name = "XTS" - - def __init__(self, tweak: utils.Buffer): - utils._check_byteslike("tweak", tweak) - - if len(tweak) != 16: - raise ValueError("tweak must be 128-bits (16 bytes)") - - self._tweak = tweak - - @property - def tweak(self) -> utils.Buffer: - return self._tweak - - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)): - raise TypeError( - "The AES128 and AES256 classes do not support XTS, please use " - "the standard AES class instead." - ) - - if algorithm.key_size not in (256, 512): - raise ValueError( - "The XTS specification requires a 256-bit key for AES-128-XTS" - " and 512-bit key for AES-256-XTS" - ) - - -class ECB(Mode): - name = "ECB" - - validate_for_algorithm = _check_aes_key_length - - -class CTR(ModeWithNonce): - name = "CTR" - - def __init__(self, nonce: utils.Buffer): - utils._check_byteslike("nonce", nonce) - self._nonce = nonce - - @property - def nonce(self) -> utils.Buffer: - return self._nonce - - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - _check_aes_key_length(self, algorithm) - _check_nonce_length(self.nonce, self.name, algorithm) - - -class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag): - name = "GCM" - _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8 - _MAX_AAD_BYTES = (2**64) // 8 - - def __init__( - self, - initialization_vector: utils.Buffer, - tag: bytes | None = None, - min_tag_length: int = 16, - ): - # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive - # This is a sane limit anyway so we'll enforce it here. - utils._check_byteslike("initialization_vector", initialization_vector) - if len(initialization_vector) < 8 or len(initialization_vector) > 128: - raise ValueError( - "initialization_vector must be between 8 and 128 bytes (64 " - "and 1024 bits)." - ) - self._initialization_vector = initialization_vector - if min_tag_length < 4: - raise ValueError("min_tag_length must be >= 4") - if tag is not None: - utils._check_bytes("tag", tag) - if len(tag) < min_tag_length: - raise ValueError( - f"Authentication tag must be {min_tag_length} bytes or " - "longer." - ) - self._tag = tag - self._min_tag_length = min_tag_length - - @property - def tag(self) -> bytes | None: - return self._tag - - @property - def initialization_vector(self) -> utils.Buffer: - return self._initialization_vector - - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - _check_aes_key_length(self, algorithm) - if not isinstance(algorithm, BlockCipherAlgorithm): - raise UnsupportedAlgorithm( - "GCM requires a block cipher algorithm", - _Reasons.UNSUPPORTED_CIPHER, - ) - block_size_bytes = algorithm.block_size // 8 - if self._tag is not None and len(self._tag) > block_size_bytes: - raise ValueError( - f"Authentication tag cannot be more than {block_size_bytes} " - "bytes." - ) - - -utils.deprecated( - OFB, - __name__, - "OFB has been moved to " - "cryptography.hazmat.decrepit.ciphers.modes.OFB and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.modes in 49.0.0.", - utils.DeprecatedIn47, - name="OFB", -) - - -utils.deprecated( - CFB, - __name__, - "CFB has been moved to " - "cryptography.hazmat.decrepit.ciphers.modes.CFB and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.modes in 49.0.0.", - utils.DeprecatedIn47, - name="CFB", -) - - -utils.deprecated( - CFB8, - __name__, - "CFB8 has been moved to " - "cryptography.hazmat.decrepit.ciphers.modes.CFB8 and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.modes in 49.0.0.", - utils.DeprecatedIn47, - name="CFB8", -) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py deleted file mode 100644 index 2c67ce22..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/cmac.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -__all__ = ["CMAC"] -CMAC = rust_openssl.cmac.CMAC diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py deleted file mode 100644 index 3975c714..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/constant_time.py +++ /dev/null @@ -1,14 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import hmac - - -def bytes_eq(a: bytes, b: bytes) -> bool: - if not isinstance(a, bytes) or not isinstance(b, bytes): - raise TypeError("a and b must be bytes.") - - return hmac.compare_digest(a, b) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py deleted file mode 100644 index 4b55ec33..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hashes.py +++ /dev/null @@ -1,246 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.utils import Buffer - -__all__ = [ - "MD5", - "SHA1", - "SHA3_224", - "SHA3_256", - "SHA3_384", - "SHA3_512", - "SHA224", - "SHA256", - "SHA384", - "SHA512", - "SHA512_224", - "SHA512_256", - "SHAKE128", - "SHAKE256", - "SM3", - "BLAKE2b", - "BLAKE2s", - "ExtendableOutputFunction", - "Hash", - "HashAlgorithm", - "HashContext", - "XOFHash", -] - - -class HashAlgorithm(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this algorithm (e.g. "sha256", "md5"). - """ - - @property - @abc.abstractmethod - def digest_size(self) -> int: - """ - The size of the resulting digest in bytes. - """ - - @property - @abc.abstractmethod - def block_size(self) -> int | None: - """ - The internal block size of the hash function, or None if the hash - function does not use blocks internally (e.g. SHA3). - """ - - -class HashContext(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def algorithm(self) -> HashAlgorithm: - """ - A HashAlgorithm that will be used by this context. - """ - - @abc.abstractmethod - def update(self, data: Buffer) -> None: - """ - Processes the provided bytes through the hash. - """ - - @abc.abstractmethod - def finalize(self) -> bytes: - """ - Finalizes the hash context and returns the hash digest as bytes. - """ - - @abc.abstractmethod - def copy(self) -> HashContext: - """ - Return a HashContext that is a copy of the current context. - """ - - -Hash = rust_openssl.hashes.Hash -HashContext.register(Hash) - -XOFHash = rust_openssl.hashes.XOFHash - - -class ExtendableOutputFunction(metaclass=abc.ABCMeta): - """ - An interface for extendable output functions. - """ - - -class SHA1(HashAlgorithm): - name = "sha1" - digest_size = 20 - block_size = 64 - - -class SHA512_224(HashAlgorithm): # noqa: N801 - name = "sha512-224" - digest_size = 28 - block_size = 128 - - -class SHA512_256(HashAlgorithm): # noqa: N801 - name = "sha512-256" - digest_size = 32 - block_size = 128 - - -class SHA224(HashAlgorithm): - name = "sha224" - digest_size = 28 - block_size = 64 - - -class SHA256(HashAlgorithm): - name = "sha256" - digest_size = 32 - block_size = 64 - - -class SHA384(HashAlgorithm): - name = "sha384" - digest_size = 48 - block_size = 128 - - -class SHA512(HashAlgorithm): - name = "sha512" - digest_size = 64 - block_size = 128 - - -class SHA3_224(HashAlgorithm): # noqa: N801 - name = "sha3-224" - digest_size = 28 - block_size = None - - -class SHA3_256(HashAlgorithm): # noqa: N801 - name = "sha3-256" - digest_size = 32 - block_size = None - - -class SHA3_384(HashAlgorithm): # noqa: N801 - name = "sha3-384" - digest_size = 48 - block_size = None - - -class SHA3_512(HashAlgorithm): # noqa: N801 - name = "sha3-512" - digest_size = 64 - block_size = None - - -class SHAKE128(HashAlgorithm, ExtendableOutputFunction): - name = "shake128" - block_size = None - - def __init__(self, digest_size: int): - if not isinstance(digest_size, int): - raise TypeError("digest_size must be an integer") - - if digest_size < 1: - raise ValueError("digest_size must be a positive integer") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class SHAKE256(HashAlgorithm, ExtendableOutputFunction): - name = "shake256" - block_size = None - - def __init__(self, digest_size: int): - if not isinstance(digest_size, int): - raise TypeError("digest_size must be an integer") - - if digest_size < 1: - raise ValueError("digest_size must be a positive integer") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class MD5(HashAlgorithm): - name = "md5" - digest_size = 16 - block_size = 64 - - -class BLAKE2b(HashAlgorithm): - name = "blake2b" - _max_digest_size = 64 - _min_digest_size = 1 - block_size = 128 - - def __init__(self, digest_size: int): - if digest_size != 64: - raise ValueError("Digest size must be 64") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class BLAKE2s(HashAlgorithm): - name = "blake2s" - block_size = 64 - _max_digest_size = 32 - _min_digest_size = 1 - - def __init__(self, digest_size: int): - if digest_size != 32: - raise ValueError("Digest size must be 32") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class SM3(HashAlgorithm): - name = "sm3" - digest_size = 32 - block_size = 64 diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py deleted file mode 100644 index a9442d59..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hmac.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import hashes - -__all__ = ["HMAC"] - -HMAC = rust_openssl.hmac.HMAC -hashes.HashContext.register(HMAC) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hpke.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hpke.py deleted file mode 100644 index c8028088..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/hpke.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -AEAD = rust_openssl.hpke.AEAD -KDF = rust_openssl.hpke.KDF -KEM = rust_openssl.hpke.KEM -MLKEM768X25519PrivateKey = rust_openssl.hpke.MLKEM768X25519PrivateKey -MLKEM768X25519PublicKey = rust_openssl.hpke.MLKEM768X25519PublicKey -MLKEM1024P384PrivateKey = rust_openssl.hpke.MLKEM1024P384PrivateKey -MLKEM1024P384PublicKey = rust_openssl.hpke.MLKEM1024P384PublicKey -Suite = rust_openssl.hpke.Suite - -__all__ = [ - "AEAD", - "KDF", - "KEM", - "MLKEM768X25519PrivateKey", - "MLKEM768X25519PublicKey", - "MLKEM1024P384PrivateKey", - "MLKEM1024P384PublicKey", - "Suite", -] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py deleted file mode 100644 index 26c45bd7..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.utils import Buffer - - -class KeyDerivationFunction(metaclass=abc.ABCMeta): - @abc.abstractmethod - def derive(self, key_material: bytes) -> bytes: - """ - Deterministically generates and returns a new key based on the existing - key material. - """ - - @abc.abstractmethod - def derive_into(self, key_material: bytes, buffer: Buffer) -> None: - """ - Deterministically generates a new key based on the existing key - material and stores it in the provided buffer. - """ - - @abc.abstractmethod - def verify(self, key_material: bytes, expected_key: bytes) -> None: - """ - Checks whether the key generated by the key material matches the - expected derived key. Raises an exception if they do not match. - """ diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/argon2.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/argon2.py deleted file mode 100644 index 03e84d48..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/argon2.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - -Argon2d = rust_openssl.kdf.Argon2d -Argon2i = rust_openssl.kdf.Argon2i -Argon2id = rust_openssl.kdf.Argon2id -KeyDerivationFunction.register(Argon2d) -KeyDerivationFunction.register(Argon2i) -KeyDerivationFunction.register(Argon2id) - -__all__ = ["Argon2d", "Argon2i", "Argon2id"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py deleted file mode 100644 index 398dc5dc..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py +++ /dev/null @@ -1,16 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - -ConcatKDFHash = rust_openssl.kdf.ConcatKDFHash -ConcatKDFHMAC = rust_openssl.kdf.ConcatKDFHMAC - -KeyDerivationFunction.register(ConcatKDFHash) -KeyDerivationFunction.register(ConcatKDFHMAC) - -__all__ = ["ConcatKDFHMAC", "ConcatKDFHash"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py deleted file mode 100644 index 1e162d9d..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py +++ /dev/null @@ -1,16 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - -HKDF = rust_openssl.kdf.HKDF -HKDFExpand = rust_openssl.kdf.HKDFExpand - -KeyDerivationFunction.register(HKDF) -KeyDerivationFunction.register(HKDFExpand) - -__all__ = ["HKDF", "HKDFExpand"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py deleted file mode 100644 index e559df8f..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography import utils -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -class Mode(utils.Enum): - CounterMode = "ctr" - - -class CounterLocation(utils.Enum): - BeforeFixed = "before_fixed" - AfterFixed = "after_fixed" - MiddleFixed = "middle_fixed" - - -KBKDFHMAC = rust_openssl.kdf.KBKDFHMAC -KeyDerivationFunction.register(KBKDFHMAC) - -KBKDFCMAC = rust_openssl.kdf.KBKDFCMAC -KeyDerivationFunction.register(KBKDFCMAC) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py deleted file mode 100644 index 771d80d4..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - -PBKDF2HMAC = rust_openssl.kdf.PBKDF2HMAC -KeyDerivationFunction.register(PBKDF2HMAC) - -__all__ = ["PBKDF2HMAC"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py deleted file mode 100644 index f791ceea..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py +++ /dev/null @@ -1,19 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import sys - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - -# This is used by the scrypt tests to skip tests that require more memory -# than the MEM_LIMIT -_MEM_LIMIT = sys.maxsize // 2 - -Scrypt = rust_openssl.kdf.Scrypt -KeyDerivationFunction.register(Scrypt) - -__all__ = ["Scrypt"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py deleted file mode 100644 index 8c4e2d31..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - -X963KDF = rust_openssl.kdf.X963KDF -KeyDerivationFunction.register(X963KDF) - -__all__ = ["X963KDF"] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py deleted file mode 100644 index a3e56b95..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/keywrap.py +++ /dev/null @@ -1,180 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography.hazmat.primitives.ciphers import Cipher -from cryptography.hazmat.primitives.ciphers.algorithms import AES -from cryptography.hazmat.primitives.ciphers.modes import ECB -from cryptography.hazmat.primitives.constant_time import bytes_eq - - -def _wrap_core( - wrapping_key: bytes, - a: bytes, - r: list[bytes], -) -> bytes: - # RFC 3394 Key Wrap - 2.2.1 (index method) - encryptor = Cipher(AES(wrapping_key), ECB()).encryptor() - n = len(r) - for j in range(6): - for i in range(n): - # every encryption operation is a discrete 16 byte chunk (because - # AES has a 128-bit block size) and since we're using ECB it is - # safe to reuse the encryptor for the entire operation - b = encryptor.update(a + r[i]) - a = ( - int.from_bytes(b[:8], byteorder="big") ^ ((n * j) + i + 1) - ).to_bytes(length=8, byteorder="big") - r[i] = b[-8:] - - assert encryptor.finalize() == b"" - - return a + b"".join(r) - - -def aes_key_wrap( - wrapping_key: bytes, - key_to_wrap: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - if len(key_to_wrap) < 16: - raise ValueError("The key to wrap must be at least 16 bytes") - - if len(key_to_wrap) % 8 != 0: - raise ValueError("The key to wrap must be a multiple of 8 bytes") - - a = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" - r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)] - return _wrap_core(wrapping_key, a, r) - - -def _unwrap_core( - wrapping_key: bytes, - a: bytes, - r: list[bytes], -) -> tuple[bytes, list[bytes]]: - # Implement RFC 3394 Key Unwrap - 2.2.2 (index method) - decryptor = Cipher(AES(wrapping_key), ECB()).decryptor() - n = len(r) - for j in reversed(range(6)): - for i in reversed(range(n)): - atr = ( - int.from_bytes(a, byteorder="big") ^ ((n * j) + i + 1) - ).to_bytes(length=8, byteorder="big") + r[i] - # every decryption operation is a discrete 16 byte chunk so - # it is safe to reuse the decryptor for the entire operation - b = decryptor.update(atr) - a = b[:8] - r[i] = b[-8:] - - assert decryptor.finalize() == b"" - return a, r - - -def aes_key_wrap_with_padding( - wrapping_key: bytes, - key_to_wrap: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - if not key_to_wrap or len(key_to_wrap) > 2**32: - raise ValueError("key_to_wrap must be between 1 and 2^32 bytes") - - aiv = b"\xa6\x59\x59\xa6" + len(key_to_wrap).to_bytes( - length=4, byteorder="big" - ) - # pad the key to wrap if necessary - pad = (8 - (len(key_to_wrap) % 8)) % 8 - key_to_wrap = key_to_wrap + b"\x00" * pad - if len(key_to_wrap) == 8: - # RFC 5649 - 4.1 - exactly 8 octets after padding - encryptor = Cipher(AES(wrapping_key), ECB()).encryptor() - b = encryptor.update(aiv + key_to_wrap) - assert encryptor.finalize() == b"" - return b - else: - r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)] - return _wrap_core(wrapping_key, aiv, r) - - -def aes_key_unwrap_with_padding( - wrapping_key: bytes, - wrapped_key: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapped_key) < 16: - raise InvalidUnwrap("Must be at least 16 bytes") - - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - if len(wrapped_key) == 16: - # RFC 5649 - 4.2 - exactly two 64-bit blocks - decryptor = Cipher(AES(wrapping_key), ECB()).decryptor() - out = decryptor.update(wrapped_key) - assert decryptor.finalize() == b"" - a = out[:8] - data = out[8:] - n = 1 - else: - r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] - encrypted_aiv = r.pop(0) - n = len(r) - a, r = _unwrap_core(wrapping_key, encrypted_aiv, r) - data = b"".join(r) - - # 1) Check that MSB(32,A) = A65959A6. - # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n. If so, let - # MLI = LSB(32,A). - # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of - # the output data are zero. - mli = int.from_bytes(a[4:], byteorder="big") - b = (8 * n) - mli - if ( - not bytes_eq(a[:4], b"\xa6\x59\x59\xa6") - or not 8 * (n - 1) < mli <= 8 * n - or (b != 0 and not bytes_eq(data[-b:], b"\x00" * b)) - ): - raise InvalidUnwrap() - - if b == 0: - return data - else: - return data[:-b] - - -def aes_key_unwrap( - wrapping_key: bytes, - wrapped_key: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapped_key) < 24: - raise InvalidUnwrap("Must be at least 24 bytes") - - if len(wrapped_key) % 8 != 0: - raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes") - - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" - r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] - a = r.pop(0) - a, r = _unwrap_core(wrapping_key, a, r) - if not bytes_eq(a, aiv): - raise InvalidUnwrap() - - return b"".join(r) - - -class InvalidUnwrap(Exception): - pass diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py deleted file mode 100644 index f9cd1f13..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/padding.py +++ /dev/null @@ -1,69 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography import utils -from cryptography.hazmat.bindings._rust import ( - ANSIX923PaddingContext, - ANSIX923UnpaddingContext, - PKCS7PaddingContext, - PKCS7UnpaddingContext, -) - - -class PaddingContext(metaclass=abc.ABCMeta): - @abc.abstractmethod - def update(self, data: utils.Buffer) -> bytes: - """ - Pads the provided bytes and returns any available data as bytes. - """ - - @abc.abstractmethod - def finalize(self) -> bytes: - """ - Finalize the padding, returns bytes. - """ - - -def _byte_padding_check(block_size: int) -> None: - if not (0 <= block_size <= 2040): - raise ValueError("block_size must be in range(0, 2041).") - - if block_size % 8 != 0: - raise ValueError("block_size must be a multiple of 8.") - - -class PKCS7: - def __init__(self, block_size: int): - _byte_padding_check(block_size) - self.block_size = block_size - - def padder(self) -> PaddingContext: - return PKCS7PaddingContext(self.block_size) - - def unpadder(self) -> PaddingContext: - return PKCS7UnpaddingContext(self.block_size) - - -PaddingContext.register(PKCS7PaddingContext) -PaddingContext.register(PKCS7UnpaddingContext) - - -class ANSIX923: - def __init__(self, block_size: int): - _byte_padding_check(block_size) - self.block_size = block_size - - def padder(self) -> PaddingContext: - return ANSIX923PaddingContext(self.block_size) - - def unpadder(self) -> PaddingContext: - return ANSIX923UnpaddingContext(self.block_size) - - -PaddingContext.register(ANSIX923PaddingContext) -PaddingContext.register(ANSIX923UnpaddingContext) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py deleted file mode 100644 index 7f5a77a5..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/poly1305.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -__all__ = ["Poly1305"] - -Poly1305 = rust_openssl.poly1305.Poly1305 diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py deleted file mode 100644 index 62283cc7..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/__init__.py +++ /dev/null @@ -1,65 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.primitives._serialization import ( - BestAvailableEncryption, - Encoding, - KeySerializationEncryption, - NoEncryption, - ParameterFormat, - PrivateFormat, - PublicFormat, - _KeySerializationEncryption, -) -from cryptography.hazmat.primitives.serialization.base import ( - load_der_parameters, - load_der_private_key, - load_der_public_key, - load_pem_parameters, - load_pem_private_key, - load_pem_public_key, -) -from cryptography.hazmat.primitives.serialization.ssh import ( - SSHCertificate, - SSHCertificateBuilder, - SSHCertificateType, - SSHCertPrivateKeyTypes, - SSHCertPublicKeyTypes, - SSHPrivateKeyTypes, - SSHPublicKeyTypes, - load_ssh_private_key, - load_ssh_public_identity, - load_ssh_public_key, - ssh_key_fingerprint, -) - -__all__ = [ - "BestAvailableEncryption", - "Encoding", - "KeySerializationEncryption", - "NoEncryption", - "ParameterFormat", - "PrivateFormat", - "PublicFormat", - "SSHCertPrivateKeyTypes", - "SSHCertPublicKeyTypes", - "SSHCertificate", - "SSHCertificateBuilder", - "SSHCertificateType", - "SSHPrivateKeyTypes", - "SSHPublicKeyTypes", - "_KeySerializationEncryption", - "load_der_parameters", - "load_der_private_key", - "load_der_public_key", - "load_pem_parameters", - "load_pem_private_key", - "load_pem_public_key", - "load_ssh_private_key", - "load_ssh_public_identity", - "load_ssh_public_key", - "ssh_key_fingerprint", -] diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py deleted file mode 100644 index e7c998b7..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/base.py +++ /dev/null @@ -1,14 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -load_pem_private_key = rust_openssl.keys.load_pem_private_key -load_der_private_key = rust_openssl.keys.load_der_private_key - -load_pem_public_key = rust_openssl.keys.load_pem_public_key -load_der_public_key = rust_openssl.keys.load_der_public_key - -load_pem_parameters = rust_openssl.dh.from_pem_parameters -load_der_parameters = rust_openssl.dh.from_der_parameters diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py deleted file mode 100644 index 58884ff6..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py +++ /dev/null @@ -1,176 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing -from collections.abc import Iterable - -from cryptography import x509 -from cryptography.hazmat.bindings._rust import pkcs12 as rust_pkcs12 -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives._serialization import PBES as PBES -from cryptography.hazmat.primitives.asymmetric import ( - dsa, - ec, - ed448, - ed25519, - rsa, -) -from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes - -__all__ = [ - "PBES", - "PKCS12Certificate", - "PKCS12KeyAndCertificates", - "PKCS12PrivateKeyTypes", - "load_key_and_certificates", - "load_pkcs12", - "serialize_java_truststore", - "serialize_key_and_certificates", -] - -PKCS12PrivateKeyTypes = typing.Union[ - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, -] - - -PKCS12Certificate = rust_pkcs12.PKCS12Certificate - - -class PKCS12KeyAndCertificates: - def __init__( - self, - key: PrivateKeyTypes | None, - cert: PKCS12Certificate | None, - additional_certs: list[PKCS12Certificate], - ): - if key is not None and not isinstance( - key, - ( - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - ), - ): - raise TypeError( - "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" - " private key, or None." - ) - if cert is not None and not isinstance(cert, PKCS12Certificate): - raise TypeError("cert must be a PKCS12Certificate object or None") - if not all( - isinstance(add_cert, PKCS12Certificate) - for add_cert in additional_certs - ): - raise TypeError( - "all values in additional_certs must be PKCS12Certificate" - " objects" - ) - self._key = key - self._cert = cert - self._additional_certs = additional_certs - - @property - def key(self) -> PrivateKeyTypes | None: - return self._key - - @property - def cert(self) -> PKCS12Certificate | None: - return self._cert - - @property - def additional_certs(self) -> list[PKCS12Certificate]: - return self._additional_certs - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PKCS12KeyAndCertificates): - return NotImplemented - - return ( - self.key == other.key - and self.cert == other.cert - and self.additional_certs == other.additional_certs - ) - - def __hash__(self) -> int: - return hash((self.key, self.cert, tuple(self.additional_certs))) - - def __repr__(self) -> str: - fmt = ( - "" - ) - return fmt.format(self.key, self.cert, self.additional_certs) - - -load_key_and_certificates = rust_pkcs12.load_key_and_certificates -load_pkcs12 = rust_pkcs12.load_pkcs12 - - -_PKCS12CATypes = typing.Union[ - x509.Certificate, - PKCS12Certificate, -] - - -def serialize_java_truststore( - certs: Iterable[PKCS12Certificate], - encryption_algorithm: serialization.KeySerializationEncryption, -) -> bytes: - if not certs: - raise ValueError("You must supply at least one cert") - - if not isinstance( - encryption_algorithm, serialization.KeySerializationEncryption - ): - raise TypeError( - "Key encryption algorithm must be a " - "KeySerializationEncryption instance" - ) - - return rust_pkcs12.serialize_java_truststore(certs, encryption_algorithm) - - -def serialize_key_and_certificates( - name: bytes | None, - key: PKCS12PrivateKeyTypes | None, - cert: x509.Certificate | None, - cas: Iterable[_PKCS12CATypes] | None, - encryption_algorithm: serialization.KeySerializationEncryption, -) -> bytes: - if key is not None and not isinstance( - key, - ( - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - ), - ): - raise TypeError( - "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" - " private key, or None." - ) - - if not isinstance( - encryption_algorithm, serialization.KeySerializationEncryption - ): - raise TypeError( - "Key encryption algorithm must be a " - "KeySerializationEncryption instance" - ) - - if key is None and cert is None and not cas: - raise ValueError("You must supply at least one of key, cert, or cas") - - return rust_pkcs12.serialize_key_and_certificates( - name, key, cert, cas, encryption_algorithm - ) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py deleted file mode 100644 index 76b667a2..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py +++ /dev/null @@ -1,412 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import email.base64mime -import email.generator -import email.message -import email.policy -import io -import typing -from collections.abc import Iterable - -from cryptography import utils, x509 -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import pkcs7 as rust_pkcs7 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa -from cryptography.hazmat.primitives.ciphers import ( - algorithms, -) -from cryptography.utils import _check_byteslike - -load_pem_pkcs7_certificates = rust_pkcs7.load_pem_pkcs7_certificates - -load_der_pkcs7_certificates = rust_pkcs7.load_der_pkcs7_certificates - -serialize_certificates = rust_pkcs7.serialize_certificates - -PKCS7HashTypes = typing.Union[ - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, -] - -PKCS7PrivateKeyTypes = typing.Union[ - rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey -] - -ContentEncryptionAlgorithm = typing.Union[ - type[algorithms.AES128], type[algorithms.AES256] -] - - -class PKCS7Options(utils.Enum): - Text = "Add text/plain MIME type" - Binary = "Don't translate input data into canonical MIME format" - DetachedSignature = "Don't embed data in the PKCS7 structure" - NoCapabilities = "Don't embed SMIME capabilities" - NoAttributes = "Don't embed authenticatedAttributes" - NoCerts = "Don't embed signer certificate" - - -class PKCS7SignatureBuilder: - def __init__( - self, - data: utils.Buffer | None = None, - signers: list[ - tuple[ - x509.Certificate, - PKCS7PrivateKeyTypes, - PKCS7HashTypes, - padding.PSS | padding.PKCS1v15 | None, - ] - ] = [], - additional_certs: list[x509.Certificate] = [], - ): - self._data = data - self._signers = signers - self._additional_certs = additional_certs - - def set_data(self, data: utils.Buffer) -> PKCS7SignatureBuilder: - _check_byteslike("data", data) - if self._data is not None: - raise ValueError("data may only be set once") - - return PKCS7SignatureBuilder(data, self._signers) - - def add_signer( - self, - certificate: x509.Certificate, - private_key: PKCS7PrivateKeyTypes, - hash_algorithm: PKCS7HashTypes, - *, - rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, - ) -> PKCS7SignatureBuilder: - if not isinstance( - hash_algorithm, - ( - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - ), - ): - raise TypeError( - "hash_algorithm must be one of hashes.SHA224, " - "SHA256, SHA384, or SHA512" - ) - if not isinstance(certificate, x509.Certificate): - raise TypeError("certificate must be a x509.Certificate") - - if not isinstance( - private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) - ): - raise TypeError("Only RSA & EC keys are supported at this time.") - - if rsa_padding is not None: - if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): - raise TypeError("Padding must be PSS or PKCS1v15") - if not isinstance(private_key, rsa.RSAPrivateKey): - raise TypeError("Padding is only supported for RSA keys") - - return PKCS7SignatureBuilder( - self._data, - [ - *self._signers, - (certificate, private_key, hash_algorithm, rsa_padding), - ], - ) - - def add_certificate( - self, certificate: x509.Certificate - ) -> PKCS7SignatureBuilder: - if not isinstance(certificate, x509.Certificate): - raise TypeError("certificate must be a x509.Certificate") - - return PKCS7SignatureBuilder( - self._data, self._signers, [*self._additional_certs, certificate] - ) - - def sign( - self, - encoding: serialization.Encoding, - options: Iterable[PKCS7Options], - backend: typing.Any = None, - ) -> bytes: - if len(self._signers) == 0: - raise ValueError("Must have at least one signer") - if self._data is None: - raise ValueError("You must add data to sign") - options = list(options) - if not all(isinstance(x, PKCS7Options) for x in options): - raise ValueError("options must be from the PKCS7Options enum") - if encoding not in ( - serialization.Encoding.PEM, - serialization.Encoding.DER, - serialization.Encoding.SMIME, - ): - raise ValueError( - "Must be PEM, DER, or SMIME from the Encoding enum" - ) - - # Text is a meaningless option unless it is accompanied by - # DetachedSignature - if ( - PKCS7Options.Text in options - and PKCS7Options.DetachedSignature not in options - ): - raise ValueError( - "When passing the Text option you must also pass " - "DetachedSignature" - ) - - if PKCS7Options.Text in options and encoding in ( - serialization.Encoding.DER, - serialization.Encoding.PEM, - ): - raise ValueError( - "The Text option is only available for SMIME serialization" - ) - - # No attributes implies no capabilities so we'll error if you try to - # pass both. - if ( - PKCS7Options.NoAttributes in options - and PKCS7Options.NoCapabilities in options - ): - raise ValueError( - "NoAttributes is a superset of NoCapabilities. Do not pass " - "both values." - ) - - return rust_pkcs7.sign_and_serialize(self, encoding, options) - - -class PKCS7EnvelopeBuilder: - def __init__( - self, - *, - _data: bytes | None = None, - _recipients: list[x509.Certificate] | None = None, - _content_encryption_algorithm: ContentEncryptionAlgorithm - | None = None, - ): - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - if not ossl.rsa_encryption_supported(padding=padding.PKCS1v15()): - raise UnsupportedAlgorithm( - "RSA with PKCS1 v1.5 padding is not supported by this version" - " of OpenSSL.", - _Reasons.UNSUPPORTED_PADDING, - ) - self._data = _data - self._recipients = _recipients if _recipients is not None else [] - self._content_encryption_algorithm = _content_encryption_algorithm - - def set_data(self, data: bytes) -> PKCS7EnvelopeBuilder: - _check_byteslike("data", data) - if self._data is not None: - raise ValueError("data may only be set once") - - return PKCS7EnvelopeBuilder( - _data=data, - _recipients=self._recipients, - _content_encryption_algorithm=self._content_encryption_algorithm, - ) - - def add_recipient( - self, - certificate: x509.Certificate, - ) -> PKCS7EnvelopeBuilder: - if not isinstance(certificate, x509.Certificate): - raise TypeError("certificate must be a x509.Certificate") - - if not isinstance(certificate.public_key(), rsa.RSAPublicKey): - raise TypeError("Only RSA keys are supported at this time.") - - return PKCS7EnvelopeBuilder( - _data=self._data, - _recipients=[ - *self._recipients, - certificate, - ], - _content_encryption_algorithm=self._content_encryption_algorithm, - ) - - def set_content_encryption_algorithm( - self, content_encryption_algorithm: ContentEncryptionAlgorithm - ) -> PKCS7EnvelopeBuilder: - if self._content_encryption_algorithm is not None: - raise ValueError("Content encryption algo may only be set once") - if content_encryption_algorithm not in { - algorithms.AES128, - algorithms.AES256, - }: - raise TypeError("Only AES128 and AES256 are supported") - - return PKCS7EnvelopeBuilder( - _data=self._data, - _recipients=self._recipients, - _content_encryption_algorithm=content_encryption_algorithm, - ) - - def encrypt( - self, - encoding: serialization.Encoding, - options: Iterable[PKCS7Options], - ) -> bytes: - if len(self._recipients) == 0: - raise ValueError("Must have at least one recipient") - if self._data is None: - raise ValueError("You must add data to encrypt") - - # The default content encryption algorithm is AES-128-CBC, which the - # S/MIME v3.2 RFC specifies as MUST support (https://datatracker.ietf.org/doc/html/rfc5751#section-2.7) - # however rest of S/MIME v3.2 is not currently supported - content_encryption_algorithm = ( - self._content_encryption_algorithm or algorithms.AES128 - ) - - options = list(options) - if not all(isinstance(x, PKCS7Options) for x in options): - raise ValueError("options must be from the PKCS7Options enum") - if encoding not in ( - serialization.Encoding.PEM, - serialization.Encoding.DER, - serialization.Encoding.SMIME, - ): - raise ValueError( - "Must be PEM, DER, or SMIME from the Encoding enum" - ) - - # Only allow options that make sense for encryption - if any( - opt not in [PKCS7Options.Text, PKCS7Options.Binary] - for opt in options - ): - raise ValueError( - "Only the following options are supported for encryption: " - "Text, Binary" - ) - elif PKCS7Options.Text in options and PKCS7Options.Binary in options: - # OpenSSL accepts both options at the same time, but ignores Text. - # We fail defensively to avoid unexpected outputs. - raise ValueError( - "Cannot use Binary and Text options at the same time" - ) - - return rust_pkcs7.encrypt_and_serialize( - self, content_encryption_algorithm, encoding, options - ) - - -pkcs7_decrypt_der = rust_pkcs7.decrypt_der -pkcs7_decrypt_pem = rust_pkcs7.decrypt_pem -pkcs7_decrypt_smime = rust_pkcs7.decrypt_smime - - -def _smime_signed_encode( - data: bytes, signature: bytes, micalg: str, text_mode: bool -) -> bytes: - # This function works pretty hard to replicate what OpenSSL does - # precisely. For good and for ill. - - m = email.message.Message() - m.add_header("MIME-Version", "1.0") - m.add_header( - "Content-Type", - "multipart/signed", - protocol="application/x-pkcs7-signature", - micalg=micalg, - ) - - m.preamble = "This is an S/MIME signed message\n" - - msg_part = OpenSSLMimePart() - msg_part.set_payload(data) - if text_mode: - msg_part.add_header("Content-Type", "text/plain") - m.attach(msg_part) - - sig_part = email.message.MIMEPart() - sig_part.add_header( - "Content-Type", "application/x-pkcs7-signature", name="smime.p7s" - ) - sig_part.add_header("Content-Transfer-Encoding", "base64") - sig_part.add_header( - "Content-Disposition", "attachment", filename="smime.p7s" - ) - sig_part.set_payload( - email.base64mime.body_encode(signature, maxlinelen=65) - ) - del sig_part["MIME-Version"] - m.attach(sig_part) - - fp = io.BytesIO() - g = email.generator.BytesGenerator( - fp, - maxheaderlen=0, - mangle_from_=False, - policy=m.policy.clone(linesep="\r\n"), - ) - g.flatten(m) - return fp.getvalue() - - -def _smime_enveloped_encode(data: bytes) -> bytes: - m = email.message.Message() - m.add_header("MIME-Version", "1.0") - m.add_header("Content-Disposition", "attachment", filename="smime.p7m") - m.add_header( - "Content-Type", - "application/pkcs7-mime", - smime_type="enveloped-data", - name="smime.p7m", - ) - m.add_header("Content-Transfer-Encoding", "base64") - - m.set_payload(email.base64mime.body_encode(data, maxlinelen=65)) - - return m.as_bytes(policy=m.policy.clone(linesep="\n", max_line_length=0)) - - -def _smime_enveloped_decode(data: bytes) -> bytes: - m = email.message_from_bytes(data) - if m.get_content_type() not in { - "application/x-pkcs7-mime", - "application/pkcs7-mime", - }: - raise ValueError("Not an S/MIME enveloped message") - return bytes(m.get_payload(decode=True)) - - -def _smime_remove_text_headers(data: bytes) -> bytes: - m = email.message_from_bytes(data) - # Using get() instead of get_content_type() since it has None as default, - # where the latter has "text/plain". Both methods are case-insensitive. - content_type = m.get("content-type") - if content_type is None: - raise ValueError( - "Decrypted MIME data has no 'Content-Type' header. " - "Please remove the 'Text' option to parse it manually." - ) - if "text/plain" not in content_type: - raise ValueError( - f"Decrypted MIME data content type is '{content_type}', not " - "'text/plain'. Remove the 'Text' option to parse it manually." - ) - return bytes(m.get_payload(decode=True)) - - -class OpenSSLMimePart(email.message.MIMEPart): - # A MIMEPart subclass that replicates OpenSSL's behavior of not including - # a newline if there are no headers. - def _write_headers(self, generator) -> None: - if list(self.raw_items()): - generator._write_headers(self) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py deleted file mode 100644 index 411113bc..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/serialization/ssh.py +++ /dev/null @@ -1,1621 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import binascii -import enum -import os -import re -import typing -import warnings -from base64 import encodebytes as _base64_encode -from dataclasses import dataclass - -from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import ( - dsa, - ec, - ed25519, - padding, - rsa, -) -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils -from cryptography.hazmat.primitives.ciphers import ( - AEADDecryptionContext, - Cipher, - algorithms, - modes, -) -from cryptography.hazmat.primitives.serialization import ( - Encoding, - KeySerializationEncryption, - NoEncryption, - PrivateFormat, - PublicFormat, - _KeySerializationEncryption, -) - -try: - from bcrypt import kdf as _bcrypt_kdf - - _bcrypt_supported = True -except ImportError: - _bcrypt_supported = False - - def _bcrypt_kdf( - password: bytes, - salt: bytes, - desired_key_bytes: int, - rounds: int, - ignore_few_rounds: bool = False, - ) -> bytes: - raise UnsupportedAlgorithm("Need bcrypt module") - - -_SSH_ED25519 = b"ssh-ed25519" -_SSH_RSA = b"ssh-rsa" -_SSH_DSA = b"ssh-dss" -_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256" -_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384" -_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521" -_CERT_SUFFIX = b"-cert-v01@openssh.com" - -# U2F application string suffixed pubkey -_SK_SSH_ED25519 = b"sk-ssh-ed25519@openssh.com" -_SK_SSH_ECDSA_NISTP256 = b"sk-ecdsa-sha2-nistp256@openssh.com" - -# These are not key types, only algorithms, so they cannot appear -# as a public key type -_SSH_RSA_SHA256 = b"rsa-sha2-256" -_SSH_RSA_SHA512 = b"rsa-sha2-512" - -_SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)") -_SK_MAGIC = b"openssh-key-v1\0" -_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----" -_SK_END = b"-----END OPENSSH PRIVATE KEY-----" -_BCRYPT = b"bcrypt" -_NONE = b"none" -_DEFAULT_CIPHER = b"aes256-ctr" -_DEFAULT_ROUNDS = 16 - -# re is only way to work on bytes-like data -_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL) - -# padding for max blocksize -_PADDING = memoryview(bytearray(range(1, 1 + 16))) - - -@dataclass -class _SSHCipher: - alg: type[algorithms.AES] - key_len: int - mode: type[modes.CTR] | type[modes.CBC] | type[modes.GCM] - block_len: int - iv_len: int - tag_len: int | None - is_aead: bool - - -# ciphers that are actually used in key wrapping -_SSH_CIPHERS: dict[bytes, _SSHCipher] = { - b"aes256-ctr": _SSHCipher( - alg=algorithms.AES, - key_len=32, - mode=modes.CTR, - block_len=16, - iv_len=16, - tag_len=None, - is_aead=False, - ), - b"aes256-cbc": _SSHCipher( - alg=algorithms.AES, - key_len=32, - mode=modes.CBC, - block_len=16, - iv_len=16, - tag_len=None, - is_aead=False, - ), - b"aes256-gcm@openssh.com": _SSHCipher( - alg=algorithms.AES, - key_len=32, - mode=modes.GCM, - block_len=16, - iv_len=12, - tag_len=16, - is_aead=True, - ), -} - -# map local curve name to key type -_ECDSA_KEY_TYPE = { - "secp256r1": _ECDSA_NISTP256, - "secp384r1": _ECDSA_NISTP384, - "secp521r1": _ECDSA_NISTP521, -} - - -def _get_ssh_key_type(key: SSHPrivateKeyTypes | SSHPublicKeyTypes) -> bytes: - if isinstance(key, ec.EllipticCurvePrivateKey): - key_type = _ecdsa_key_type(key.public_key()) - elif isinstance(key, ec.EllipticCurvePublicKey): - key_type = _ecdsa_key_type(key) - elif isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): - key_type = _SSH_RSA - elif isinstance(key, (dsa.DSAPrivateKey, dsa.DSAPublicKey)): - key_type = _SSH_DSA - elif isinstance( - key, (ed25519.Ed25519PrivateKey, ed25519.Ed25519PublicKey) - ): - key_type = _SSH_ED25519 - else: - raise ValueError("Unsupported key type") - - return key_type - - -def _ecdsa_key_type(public_key: ec.EllipticCurvePublicKey) -> bytes: - """Return SSH key_type and curve_name for private key.""" - curve = public_key.curve - if curve.name not in _ECDSA_KEY_TYPE: - raise ValueError( - f"Unsupported curve for ssh private key: {curve.name!r}" - ) - return _ECDSA_KEY_TYPE[curve.name] - - -def _ssh_pem_encode( - data: utils.Buffer, - prefix: bytes = _SK_START + b"\n", - suffix: bytes = _SK_END + b"\n", -) -> bytes: - return b"".join([prefix, _base64_encode(data), suffix]) - - -def _check_block_size(data: utils.Buffer, block_len: int) -> None: - """Require data to be full blocks""" - if not data or len(data) % block_len != 0: - raise ValueError("Corrupt data: missing padding") - - -def _check_empty(data: utils.Buffer) -> None: - """All data should have been parsed.""" - if data: - raise ValueError("Corrupt data: unparsed data") - - -def _init_cipher( - ciphername: bytes, - password: bytes | None, - salt: bytes, - rounds: int, -) -> Cipher[modes.CBC | modes.CTR | modes.GCM]: - """Generate key + iv and return cipher.""" - if not password: - raise TypeError( - "Key is password-protected, but password was not provided." - ) - - ciph = _SSH_CIPHERS[ciphername] - seed = _bcrypt_kdf( - password, salt, ciph.key_len + ciph.iv_len, rounds, True - ) - return Cipher( - ciph.alg(seed[: ciph.key_len]), - ciph.mode(seed[ciph.key_len :]), - ) - - -def _get_u32(data: memoryview) -> tuple[int, memoryview]: - """Uint32""" - if len(data) < 4: - raise ValueError("Invalid data") - return int.from_bytes(data[:4], byteorder="big"), data[4:] - - -def _get_u64(data: memoryview) -> tuple[int, memoryview]: - """Uint64""" - if len(data) < 8: - raise ValueError("Invalid data") - return int.from_bytes(data[:8], byteorder="big"), data[8:] - - -def _get_sshstr(data: memoryview) -> tuple[memoryview, memoryview]: - """Bytes with u32 length prefix""" - n, data = _get_u32(data) - if n > len(data): - raise ValueError("Invalid data") - return data[:n], data[n:] - - -def _get_mpint(data: memoryview) -> tuple[int, memoryview]: - """Big integer.""" - val, data = _get_sshstr(data) - if val and val[0] > 0x7F: - raise ValueError("Invalid data") - return int.from_bytes(val, "big"), data - - -def _to_mpint(val: int) -> bytes: - """Storage format for signed bigint.""" - if val < 0: - raise ValueError("negative mpint not allowed") - if not val: - return b"" - nbytes = (val.bit_length() + 8) // 8 - return utils.int_to_bytes(val, nbytes) - - -class _FragList: - """Build recursive structure without data copy.""" - - flist: list[utils.Buffer] - - def __init__(self, init: list[utils.Buffer] | None = None) -> None: - self.flist = [] - if init: - self.flist.extend(init) - - def put_raw(self, val: utils.Buffer) -> None: - """Add plain bytes""" - self.flist.append(val) - - def put_u32(self, val: int) -> None: - """Big-endian uint32""" - self.flist.append(val.to_bytes(length=4, byteorder="big")) - - def put_u64(self, val: int) -> None: - """Big-endian uint64""" - self.flist.append(val.to_bytes(length=8, byteorder="big")) - - def put_sshstr(self, val: bytes | _FragList) -> None: - """Bytes prefixed with u32 length""" - if isinstance(val, (bytes, memoryview, bytearray)): - self.put_u32(len(val)) - self.flist.append(val) - else: - self.put_u32(val.size()) - self.flist.extend(val.flist) - - def put_mpint(self, val: int) -> None: - """Big-endian bigint prefixed with u32 length""" - self.put_sshstr(_to_mpint(val)) - - def size(self) -> int: - """Current number of bytes""" - return sum(map(len, self.flist)) - - def render(self, dstbuf: memoryview, pos: int = 0) -> int: - """Write into bytearray""" - for frag in self.flist: - flen = len(frag) - start, pos = pos, pos + flen - dstbuf[start:pos] = frag - return pos - - def tobytes(self) -> bytes: - """Return as bytes""" - buf = memoryview(bytearray(self.size())) - self.render(buf) - return buf.tobytes() - - -class _SSHFormatRSA: - """Format for RSA keys. - - Public: - mpint e, n - Private: - mpint n, e, d, iqmp, p, q - """ - - def get_public( - self, data: memoryview - ) -> tuple[tuple[int, int], memoryview]: - """RSA public fields""" - e, data = _get_mpint(data) - n, data = _get_mpint(data) - return (e, n), data - - def load_public( - self, data: memoryview - ) -> tuple[rsa.RSAPublicKey, memoryview]: - """Make RSA public key from data.""" - (e, n), data = self.get_public(data) - public_numbers = rsa.RSAPublicNumbers(e, n) - public_key = public_numbers.public_key() - return public_key, data - - def load_private( - self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool - ) -> tuple[rsa.RSAPrivateKey, memoryview]: - """Make RSA private key from data.""" - n, data = _get_mpint(data) - e, data = _get_mpint(data) - d, data = _get_mpint(data) - iqmp, data = _get_mpint(data) - p, data = _get_mpint(data) - q, data = _get_mpint(data) - - if (e, n) != pubfields: - raise ValueError("Corrupt data: rsa field mismatch") - dmp1 = rsa.rsa_crt_dmp1(d, p) - dmq1 = rsa.rsa_crt_dmq1(d, q) - public_numbers = rsa.RSAPublicNumbers(e, n) - private_numbers = rsa.RSAPrivateNumbers( - p, q, d, dmp1, dmq1, iqmp, public_numbers - ) - private_key = private_numbers.private_key( - unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation - ) - return private_key, data - - def encode_public( - self, public_key: rsa.RSAPublicKey, f_pub: _FragList - ) -> None: - """Write RSA public key""" - pubn = public_key.public_numbers() - f_pub.put_mpint(pubn.e) - f_pub.put_mpint(pubn.n) - - def encode_private( - self, private_key: rsa.RSAPrivateKey, f_priv: _FragList - ) -> None: - """Write RSA private key""" - private_numbers = private_key.private_numbers() - public_numbers = private_numbers.public_numbers - - f_priv.put_mpint(public_numbers.n) - f_priv.put_mpint(public_numbers.e) - - f_priv.put_mpint(private_numbers.d) - f_priv.put_mpint(private_numbers.iqmp) - f_priv.put_mpint(private_numbers.p) - f_priv.put_mpint(private_numbers.q) - - -class _SSHFormatDSA: - """Format for DSA keys. - - Public: - mpint p, q, g, y - Private: - mpint p, q, g, y, x - """ - - def get_public(self, data: memoryview) -> tuple[tuple, memoryview]: - """DSA public fields""" - p, data = _get_mpint(data) - q, data = _get_mpint(data) - g, data = _get_mpint(data) - y, data = _get_mpint(data) - return (p, q, g, y), data - - def load_public( - self, data: memoryview - ) -> tuple[dsa.DSAPublicKey, memoryview]: - """Make DSA public key from data.""" - (p, q, g, y), data = self.get_public(data) - parameter_numbers = dsa.DSAParameterNumbers(p, q, g) - public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) - self._validate(public_numbers) - public_key = public_numbers.public_key() - return public_key, data - - def load_private( - self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool - ) -> tuple[dsa.DSAPrivateKey, memoryview]: - """Make DSA private key from data.""" - (p, q, g, y), data = self.get_public(data) - x, data = _get_mpint(data) - - if (p, q, g, y) != pubfields: - raise ValueError("Corrupt data: dsa field mismatch") - parameter_numbers = dsa.DSAParameterNumbers(p, q, g) - public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) - self._validate(public_numbers) - private_numbers = dsa.DSAPrivateNumbers(x, public_numbers) - private_key = private_numbers.private_key() - return private_key, data - - def encode_public( - self, public_key: dsa.DSAPublicKey, f_pub: _FragList - ) -> None: - """Write DSA public key""" - public_numbers = public_key.public_numbers() - parameter_numbers = public_numbers.parameter_numbers - self._validate(public_numbers) - - f_pub.put_mpint(parameter_numbers.p) - f_pub.put_mpint(parameter_numbers.q) - f_pub.put_mpint(parameter_numbers.g) - f_pub.put_mpint(public_numbers.y) - - def encode_private( - self, private_key: dsa.DSAPrivateKey, f_priv: _FragList - ) -> None: - """Write DSA private key""" - self.encode_public(private_key.public_key(), f_priv) - f_priv.put_mpint(private_key.private_numbers().x) - - def _validate(self, public_numbers: dsa.DSAPublicNumbers) -> None: - parameter_numbers = public_numbers.parameter_numbers - if parameter_numbers.p.bit_length() != 1024: - raise ValueError("SSH supports only 1024 bit DSA keys") - - -class _SSHFormatECDSA: - """Format for ECDSA keys. - - Public: - str curve - bytes point - Private: - str curve - bytes point - mpint secret - """ - - def __init__(self, ssh_curve_name: bytes, curve: ec.EllipticCurve): - self.ssh_curve_name = ssh_curve_name - self.curve = curve - - def get_public( - self, data: memoryview - ) -> tuple[tuple[memoryview, memoryview], memoryview]: - """ECDSA public fields""" - curve, data = _get_sshstr(data) - point, data = _get_sshstr(data) - if curve != self.ssh_curve_name: - raise ValueError("Curve name mismatch") - if len(point) == 0: - raise ValueError("Invalid EC point: empty data") - if point[0] != 4: - raise NotImplementedError("Need uncompressed point") - return (curve, point), data - - def load_public( - self, data: memoryview - ) -> tuple[ec.EllipticCurvePublicKey, memoryview]: - """Make ECDSA public key from data.""" - (_, point), data = self.get_public(data) - public_key = ec.EllipticCurvePublicKey.from_encoded_point( - self.curve, point.tobytes() - ) - return public_key, data - - def load_private( - self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool - ) -> tuple[ec.EllipticCurvePrivateKey, memoryview]: - """Make ECDSA private key from data.""" - (curve_name, point), data = self.get_public(data) - secret, data = _get_mpint(data) - - if (curve_name, point) != pubfields: - raise ValueError("Corrupt data: ecdsa field mismatch") - private_key = ec.derive_private_key(secret, self.curve) - return private_key, data - - def encode_public( - self, public_key: ec.EllipticCurvePublicKey, f_pub: _FragList - ) -> None: - """Write ECDSA public key""" - point = public_key.public_bytes( - Encoding.X962, PublicFormat.UncompressedPoint - ) - f_pub.put_sshstr(self.ssh_curve_name) - f_pub.put_sshstr(point) - - def encode_private( - self, private_key: ec.EllipticCurvePrivateKey, f_priv: _FragList - ) -> None: - """Write ECDSA private key""" - public_key = private_key.public_key() - private_numbers = private_key.private_numbers() - - self.encode_public(public_key, f_priv) - f_priv.put_mpint(private_numbers.private_value) - - -class _SSHFormatEd25519: - """Format for Ed25519 keys. - - Public: - bytes point - Private: - bytes point - bytes secret_and_point - """ - - def get_public( - self, data: memoryview - ) -> tuple[tuple[memoryview], memoryview]: - """Ed25519 public fields""" - point, data = _get_sshstr(data) - return (point,), data - - def load_public( - self, data: memoryview - ) -> tuple[ed25519.Ed25519PublicKey, memoryview]: - """Make Ed25519 public key from data.""" - (point,), data = self.get_public(data) - public_key = ed25519.Ed25519PublicKey.from_public_bytes( - point.tobytes() - ) - return public_key, data - - def load_private( - self, data: memoryview, pubfields, unsafe_skip_rsa_key_validation: bool - ) -> tuple[ed25519.Ed25519PrivateKey, memoryview]: - """Make Ed25519 private key from data.""" - (point,), data = self.get_public(data) - keypair, data = _get_sshstr(data) - - secret = keypair[:32] - point2 = keypair[32:] - if point != point2 or (point,) != pubfields: - raise ValueError("Corrupt data: ed25519 field mismatch") - private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret) - return private_key, data - - def encode_public( - self, public_key: ed25519.Ed25519PublicKey, f_pub: _FragList - ) -> None: - """Write Ed25519 public key""" - raw_public_key = public_key.public_bytes( - Encoding.Raw, PublicFormat.Raw - ) - f_pub.put_sshstr(raw_public_key) - - def encode_private( - self, private_key: ed25519.Ed25519PrivateKey, f_priv: _FragList - ) -> None: - """Write Ed25519 private key""" - public_key = private_key.public_key() - raw_private_key = private_key.private_bytes( - Encoding.Raw, PrivateFormat.Raw, NoEncryption() - ) - raw_public_key = public_key.public_bytes( - Encoding.Raw, PublicFormat.Raw - ) - f_keypair = _FragList([raw_private_key, raw_public_key]) - - self.encode_public(public_key, f_priv) - f_priv.put_sshstr(f_keypair) - - -def load_application(data) -> tuple[memoryview, memoryview]: - """ - U2F application strings - """ - application, data = _get_sshstr(data) - if not application.tobytes().startswith(b"ssh:"): - raise ValueError( - "U2F application string does not start with b'ssh:' " - f"({application})" - ) - return application, data - - -class _SSHFormatSKEd25519: - """ - The format of a sk-ssh-ed25519@openssh.com public key is: - - string "sk-ssh-ed25519@openssh.com" - string public key - string application (user-specified, but typically "ssh:") - """ - - def load_public( - self, data: memoryview - ) -> tuple[ed25519.Ed25519PublicKey, memoryview]: - """Make Ed25519 public key from data.""" - public_key, data = _lookup_kformat(_SSH_ED25519).load_public(data) - _, data = load_application(data) - return public_key, data - - def get_public(self, data: memoryview) -> typing.NoReturn: - # Confusingly `get_public` is an entry point used by private key - # loading. - raise UnsupportedAlgorithm( - "sk-ssh-ed25519 private keys cannot be loaded" - ) - - -class _SSHFormatSKECDSA: - """ - The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is: - - string "sk-ecdsa-sha2-nistp256@openssh.com" - string curve name - ec_point Q - string application (user-specified, but typically "ssh:") - """ - - def load_public( - self, data: memoryview - ) -> tuple[ec.EllipticCurvePublicKey, memoryview]: - """Make ECDSA public key from data.""" - public_key, data = _lookup_kformat(_ECDSA_NISTP256).load_public(data) - _, data = load_application(data) - return public_key, data - - def get_public(self, data: memoryview) -> typing.NoReturn: - # Confusingly `get_public` is an entry point used by private key - # loading. - raise UnsupportedAlgorithm( - "sk-ecdsa-sha2-nistp256 private keys cannot be loaded" - ) - - -_KEY_FORMATS = { - _SSH_RSA: _SSHFormatRSA(), - _SSH_DSA: _SSHFormatDSA(), - _SSH_ED25519: _SSHFormatEd25519(), - _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()), - _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()), - _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()), - _SK_SSH_ED25519: _SSHFormatSKEd25519(), - _SK_SSH_ECDSA_NISTP256: _SSHFormatSKECDSA(), -} - - -def _lookup_kformat(key_type: utils.Buffer): - """Return valid format or throw error""" - if not isinstance(key_type, bytes): - key_type = memoryview(key_type).tobytes() - if key_type in _KEY_FORMATS: - return _KEY_FORMATS[key_type] - raise UnsupportedAlgorithm(f"Unsupported key type: {key_type!r}") - - -SSHPrivateKeyTypes = typing.Union[ - ec.EllipticCurvePrivateKey, - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ed25519.Ed25519PrivateKey, -] - - -def load_ssh_private_key( - data: utils.Buffer, - password: bytes | None, - backend: typing.Any = None, - *, - unsafe_skip_rsa_key_validation: bool = False, -) -> SSHPrivateKeyTypes: - """Load private key from OpenSSH custom encoding.""" - utils._check_byteslike("data", data) - if password is not None: - utils._check_bytes("password", password) - - m = _PEM_RC.search(data) - if not m: - raise ValueError("Not OpenSSH private key format") - p1 = m.start(1) - p2 = m.end(1) - data = binascii.a2b_base64(memoryview(data)[p1:p2]) - if not data.startswith(_SK_MAGIC): - raise ValueError("Not OpenSSH private key format") - data = memoryview(data)[len(_SK_MAGIC) :] - - # parse header - ciphername, data = _get_sshstr(data) - kdfname, data = _get_sshstr(data) - kdfoptions, data = _get_sshstr(data) - nkeys, data = _get_u32(data) - if nkeys != 1: - raise ValueError("Only one key supported") - - # load public key data - pubdata, data = _get_sshstr(data) - pub_key_type, pubdata = _get_sshstr(pubdata) - kformat = _lookup_kformat(pub_key_type) - pubfields, pubdata = kformat.get_public(pubdata) - _check_empty(pubdata) - - if ciphername != _NONE or kdfname != _NONE: - ciphername_bytes = ciphername.tobytes() - if ciphername_bytes not in _SSH_CIPHERS: - raise UnsupportedAlgorithm( - f"Unsupported cipher: {ciphername_bytes!r}" - ) - if kdfname != _BCRYPT: - raise UnsupportedAlgorithm(f"Unsupported KDF: {kdfname!r}") - blklen = _SSH_CIPHERS[ciphername_bytes].block_len - tag_len = _SSH_CIPHERS[ciphername_bytes].tag_len - # load secret data - edata, data = _get_sshstr(data) - # see https://bugzilla.mindrot.org/show_bug.cgi?id=3553 for - # information about how OpenSSH handles AEAD tags - if _SSH_CIPHERS[ciphername_bytes].is_aead: - tag = bytes(data) - if len(tag) != tag_len: - raise ValueError("Corrupt data: invalid tag length for cipher") - else: - _check_empty(data) - _check_block_size(edata, blklen) - salt, kbuf = _get_sshstr(kdfoptions) - rounds, kbuf = _get_u32(kbuf) - _check_empty(kbuf) - ciph = _init_cipher(ciphername_bytes, password, salt.tobytes(), rounds) - dec = ciph.decryptor() - edata = memoryview(dec.update(edata)) - if _SSH_CIPHERS[ciphername_bytes].is_aead: - assert isinstance(dec, AEADDecryptionContext) - _check_empty(dec.finalize_with_tag(tag)) - else: - # _check_block_size requires data to be a full block so there - # should be no output from finalize - _check_empty(dec.finalize()) - else: - if password: - raise TypeError( - "Password was given but private key is not encrypted." - ) - # load secret data - edata, data = _get_sshstr(data) - _check_empty(data) - blklen = 8 - _check_block_size(edata, blklen) - ck1, edata = _get_u32(edata) - ck2, edata = _get_u32(edata) - if ck1 != ck2: - raise ValueError("Corrupt data: broken checksum") - - # load per-key struct - key_type, edata = _get_sshstr(edata) - if key_type != pub_key_type: - raise ValueError("Corrupt data: key type mismatch") - private_key, edata = kformat.load_private( - edata, - pubfields, - unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation, - ) - # We don't use the comment - _, edata = _get_sshstr(edata) - - # yes, SSH does padding check *after* all other parsing is done. - # need to follow as it writes zero-byte padding too. - if edata != _PADDING[: len(edata)]: - raise ValueError("Corrupt data: invalid padding") - - if isinstance(private_key, dsa.DSAPrivateKey): - warnings.warn( - "SSH DSA keys are deprecated and will be removed in a future " - "release.", - utils.DeprecatedIn40, - stacklevel=2, - ) - - return private_key - - -def _serialize_ssh_private_key( - private_key: SSHPrivateKeyTypes, - password: bytes, - encryption_algorithm: KeySerializationEncryption, -) -> bytes: - """Serialize private key with OpenSSH custom encoding.""" - utils._check_bytes("password", password) - if isinstance(private_key, dsa.DSAPrivateKey): - warnings.warn( - "SSH DSA key support is deprecated and will be " - "removed in a future release", - utils.DeprecatedIn40, - stacklevel=4, - ) - - key_type = _get_ssh_key_type(private_key) - kformat = _lookup_kformat(key_type) - - # setup parameters - f_kdfoptions = _FragList() - if password: - ciphername = _DEFAULT_CIPHER - blklen = _SSH_CIPHERS[ciphername].block_len - kdfname = _BCRYPT - rounds = _DEFAULT_ROUNDS - if ( - isinstance(encryption_algorithm, _KeySerializationEncryption) - and encryption_algorithm._kdf_rounds is not None - ): - rounds = encryption_algorithm._kdf_rounds - salt = os.urandom(16) - f_kdfoptions.put_sshstr(salt) - f_kdfoptions.put_u32(rounds) - ciph = _init_cipher(ciphername, password, salt, rounds) - else: - ciphername = kdfname = _NONE - blklen = 8 - ciph = None - nkeys = 1 - checkval = os.urandom(4) - comment = b"" - - # encode public and private parts together - f_public_key = _FragList() - f_public_key.put_sshstr(key_type) - kformat.encode_public(private_key.public_key(), f_public_key) - - f_secrets = _FragList([checkval, checkval]) - f_secrets.put_sshstr(key_type) - kformat.encode_private(private_key, f_secrets) - f_secrets.put_sshstr(comment) - f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)]) - - # top-level structure - f_main = _FragList() - f_main.put_raw(_SK_MAGIC) - f_main.put_sshstr(ciphername) - f_main.put_sshstr(kdfname) - f_main.put_sshstr(f_kdfoptions) - f_main.put_u32(nkeys) - f_main.put_sshstr(f_public_key) - f_main.put_sshstr(f_secrets) - - # copy result info bytearray - slen = f_secrets.size() - mlen = f_main.size() - buf = memoryview(bytearray(mlen + blklen)) - f_main.render(buf) - ofs = mlen - slen - - # encrypt in-place - if ciph is not None: - ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:]) - - return _ssh_pem_encode(buf[:mlen]) - - -SSHPublicKeyTypes = typing.Union[ - ec.EllipticCurvePublicKey, - rsa.RSAPublicKey, - dsa.DSAPublicKey, - ed25519.Ed25519PublicKey, -] - -SSHCertPublicKeyTypes = typing.Union[ - ec.EllipticCurvePublicKey, - rsa.RSAPublicKey, - ed25519.Ed25519PublicKey, -] - - -class SSHCertificateType(enum.Enum): - USER = 1 - HOST = 2 - - -class SSHCertificate: - def __init__( - self, - _nonce: memoryview, - _public_key: SSHPublicKeyTypes, - _serial: int, - _cctype: int, - _key_id: memoryview, - _valid_principals: list[bytes], - _valid_after: int, - _valid_before: int, - _critical_options: dict[bytes, bytes], - _extensions: dict[bytes, bytes], - _sig_type: memoryview, - _sig_key: memoryview, - _inner_sig_type: memoryview, - _signature: memoryview, - _tbs_cert_body: memoryview, - _cert_key_type: bytes, - _cert_body: memoryview, - ): - self._nonce = _nonce - self._public_key = _public_key - self._serial = _serial - try: - self._type = SSHCertificateType(_cctype) - except ValueError: - raise ValueError("Invalid certificate type") - self._key_id = _key_id - self._valid_principals = _valid_principals - self._valid_after = _valid_after - self._valid_before = _valid_before - self._critical_options = _critical_options - self._extensions = _extensions - self._sig_type = _sig_type - self._sig_key = _sig_key - self._inner_sig_type = _inner_sig_type - self._signature = _signature - self._cert_key_type = _cert_key_type - self._cert_body = _cert_body - self._tbs_cert_body = _tbs_cert_body - - @property - def nonce(self) -> bytes: - return bytes(self._nonce) - - def public_key(self) -> SSHCertPublicKeyTypes: - # make mypy happy until we remove DSA support entirely and - # the underlying union won't have a disallowed type - return typing.cast(SSHCertPublicKeyTypes, self._public_key) - - @property - def serial(self) -> int: - return self._serial - - @property - def type(self) -> SSHCertificateType: - return self._type - - @property - def key_id(self) -> bytes: - return bytes(self._key_id) - - @property - def valid_principals(self) -> list[bytes]: - return self._valid_principals - - @property - def valid_before(self) -> int: - return self._valid_before - - @property - def valid_after(self) -> int: - return self._valid_after - - @property - def critical_options(self) -> dict[bytes, bytes]: - return self._critical_options - - @property - def extensions(self) -> dict[bytes, bytes]: - return self._extensions - - def signature_key(self) -> SSHCertPublicKeyTypes: - sigformat = _lookup_kformat(self._sig_type) - signature_key, sigkey_rest = sigformat.load_public(self._sig_key) - _check_empty(sigkey_rest) - return signature_key - - def public_bytes(self) -> bytes: - return ( - bytes(self._cert_key_type) - + b" " - + binascii.b2a_base64(bytes(self._cert_body), newline=False) - ) - - def verify_cert_signature(self) -> None: - signature_key = self.signature_key() - if isinstance(signature_key, ed25519.Ed25519PublicKey): - signature_key.verify( - bytes(self._signature), bytes(self._tbs_cert_body) - ) - elif isinstance(signature_key, ec.EllipticCurvePublicKey): - # The signature is encoded as a pair of big-endian integers - r, data = _get_mpint(self._signature) - s, data = _get_mpint(data) - _check_empty(data) - computed_sig = asym_utils.encode_dss_signature(r, s) - hash_alg = _get_ec_hash_alg(signature_key.curve) - signature_key.verify( - computed_sig, bytes(self._tbs_cert_body), ec.ECDSA(hash_alg) - ) - else: - assert isinstance(signature_key, rsa.RSAPublicKey) - if self._inner_sig_type == _SSH_RSA: - hash_alg = hashes.SHA1() - elif self._inner_sig_type == _SSH_RSA_SHA256: - hash_alg = hashes.SHA256() - else: - assert self._inner_sig_type == _SSH_RSA_SHA512 - hash_alg = hashes.SHA512() - signature_key.verify( - bytes(self._signature), - bytes(self._tbs_cert_body), - padding.PKCS1v15(), - hash_alg, - ) - - -def _get_ec_hash_alg(curve: ec.EllipticCurve) -> hashes.HashAlgorithm: - if isinstance(curve, ec.SECP256R1): - return hashes.SHA256() - elif isinstance(curve, ec.SECP384R1): - return hashes.SHA384() - else: - assert isinstance(curve, ec.SECP521R1) - return hashes.SHA512() - - -def _load_ssh_public_identity( - data: utils.Buffer, - _legacy_dsa_allowed=False, -) -> SSHCertificate | SSHPublicKeyTypes: - utils._check_byteslike("data", data) - - m = _SSH_PUBKEY_RC.match(data) - if not m: - raise ValueError("Invalid line format") - key_type = orig_key_type = m.group(1) - key_body = m.group(2) - with_cert = False - if key_type.endswith(_CERT_SUFFIX): - with_cert = True - key_type = key_type[: -len(_CERT_SUFFIX)] - if key_type == _SSH_DSA and not _legacy_dsa_allowed: - raise UnsupportedAlgorithm( - "DSA keys aren't supported in SSH certificates" - ) - kformat = _lookup_kformat(key_type) - - try: - rest = memoryview(binascii.a2b_base64(key_body)) - except (TypeError, binascii.Error): - raise ValueError("Invalid format") - - if with_cert: - cert_body = rest - inner_key_type, rest = _get_sshstr(rest) - if inner_key_type != orig_key_type: - raise ValueError("Invalid key format") - if with_cert: - nonce, rest = _get_sshstr(rest) - public_key, rest = kformat.load_public(rest) - if with_cert: - serial, rest = _get_u64(rest) - cctype, rest = _get_u32(rest) - key_id, rest = _get_sshstr(rest) - principals, rest = _get_sshstr(rest) - valid_principals = [] - while principals: - principal, principals = _get_sshstr(principals) - valid_principals.append(bytes(principal)) - valid_after, rest = _get_u64(rest) - valid_before, rest = _get_u64(rest) - crit_options, rest = _get_sshstr(rest) - critical_options = _parse_exts_opts(crit_options) - exts, rest = _get_sshstr(rest) - extensions = _parse_exts_opts(exts) - # Get the reserved field, which is unused. - _, rest = _get_sshstr(rest) - sig_key_raw, rest = _get_sshstr(rest) - sig_type, sig_key = _get_sshstr(sig_key_raw) - if sig_type == _SSH_DSA and not _legacy_dsa_allowed: - raise UnsupportedAlgorithm( - "DSA signatures aren't supported in SSH certificates" - ) - # Get the entire cert body and subtract the signature - tbs_cert_body = cert_body[: -len(rest)] - signature_raw, rest = _get_sshstr(rest) - _check_empty(rest) - inner_sig_type, sig_rest = _get_sshstr(signature_raw) - # RSA certs can have multiple algorithm types - if ( - sig_type == _SSH_RSA - and inner_sig_type - not in [_SSH_RSA_SHA256, _SSH_RSA_SHA512, _SSH_RSA] - ) or (sig_type != _SSH_RSA and inner_sig_type != sig_type): - raise ValueError("Signature key type does not match") - signature, sig_rest = _get_sshstr(sig_rest) - _check_empty(sig_rest) - return SSHCertificate( - nonce, - public_key, - serial, - cctype, - key_id, - valid_principals, - valid_after, - valid_before, - critical_options, - extensions, - sig_type, - sig_key, - inner_sig_type, - signature, - tbs_cert_body, - orig_key_type, - cert_body, - ) - else: - _check_empty(rest) - return public_key - - -def load_ssh_public_identity( - data: utils.Buffer, -) -> SSHCertificate | SSHPublicKeyTypes: - return _load_ssh_public_identity(data) - - -def _parse_exts_opts(exts_opts: memoryview) -> dict[bytes, bytes]: - result: dict[bytes, bytes] = {} - last_name = None - while exts_opts: - name, exts_opts = _get_sshstr(exts_opts) - bname: bytes = bytes(name) - if bname in result: - raise ValueError("Duplicate name") - if last_name is not None and bname < last_name: - raise ValueError("Fields not lexically sorted") - value, exts_opts = _get_sshstr(exts_opts) - if len(value) > 0: - value, extra = _get_sshstr(value) - if len(extra) > 0: - raise ValueError("Unexpected extra data after value") - result[bname] = bytes(value) - last_name = bname - return result - - -def ssh_key_fingerprint( - key: SSHPublicKeyTypes, - hash_algorithm: hashes.MD5 | hashes.SHA256, -) -> bytes: - if not isinstance(hash_algorithm, (hashes.MD5, hashes.SHA256)): - raise TypeError("hash_algorithm must be either MD5 or SHA256") - - key_type = _get_ssh_key_type(key) - kformat = _lookup_kformat(key_type) - - f_pub = _FragList() - f_pub.put_sshstr(key_type) - kformat.encode_public(key, f_pub) - - ssh_binary_data = f_pub.tobytes() - - # Hash the binary data - hash_obj = hashes.Hash(hash_algorithm) - hash_obj.update(ssh_binary_data) - return hash_obj.finalize() - - -def load_ssh_public_key( - data: utils.Buffer, backend: typing.Any = None -) -> SSHPublicKeyTypes: - cert_or_key = _load_ssh_public_identity(data, _legacy_dsa_allowed=True) - public_key: SSHPublicKeyTypes - if isinstance(cert_or_key, SSHCertificate): - public_key = cert_or_key.public_key() - else: - public_key = cert_or_key - - if isinstance(public_key, dsa.DSAPublicKey): - warnings.warn( - "SSH DSA keys are deprecated and will be removed in a future " - "release.", - utils.DeprecatedIn40, - stacklevel=2, - ) - return public_key - - -def serialize_ssh_public_key(public_key: SSHPublicKeyTypes) -> bytes: - """One-line public key format for OpenSSH""" - if isinstance(public_key, dsa.DSAPublicKey): - warnings.warn( - "SSH DSA key support is deprecated and will be " - "removed in a future release", - utils.DeprecatedIn40, - stacklevel=4, - ) - key_type = _get_ssh_key_type(public_key) - kformat = _lookup_kformat(key_type) - - f_pub = _FragList() - f_pub.put_sshstr(key_type) - kformat.encode_public(public_key, f_pub) - - pub = binascii.b2a_base64(f_pub.tobytes()).strip() - return b"".join([key_type, b" ", pub]) - - -SSHCertPrivateKeyTypes = typing.Union[ - ec.EllipticCurvePrivateKey, - rsa.RSAPrivateKey, - ed25519.Ed25519PrivateKey, -] - - -# This is an undocumented limit enforced in the openssh codebase for sshd and -# ssh-keygen, but it is undefined in the ssh certificates spec. -_SSHKEY_CERT_MAX_PRINCIPALS = 256 - - -class SSHCertificateBuilder: - def __init__( - self, - _public_key: SSHCertPublicKeyTypes | None = None, - _serial: int | None = None, - _type: SSHCertificateType | None = None, - _key_id: bytes | None = None, - _valid_principals: list[bytes] = [], - _valid_for_all_principals: bool = False, - _valid_before: int | None = None, - _valid_after: int | None = None, - _critical_options: list[tuple[bytes, bytes]] = [], - _extensions: list[tuple[bytes, bytes]] = [], - ): - self._public_key = _public_key - self._serial = _serial - self._type = _type - self._key_id = _key_id - self._valid_principals = _valid_principals - self._valid_for_all_principals = _valid_for_all_principals - self._valid_before = _valid_before - self._valid_after = _valid_after - self._critical_options = _critical_options - self._extensions = _extensions - - def public_key( - self, public_key: SSHCertPublicKeyTypes - ) -> SSHCertificateBuilder: - if not isinstance( - public_key, - ( - ec.EllipticCurvePublicKey, - rsa.RSAPublicKey, - ed25519.Ed25519PublicKey, - ), - ): - raise TypeError("Unsupported key type") - if self._public_key is not None: - raise ValueError("public_key already set") - - return SSHCertificateBuilder( - _public_key=public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def serial(self, serial: int) -> SSHCertificateBuilder: - if not isinstance(serial, int): - raise TypeError("serial must be an integer") - if not 0 <= serial < 2**64: - raise ValueError("serial must be between 0 and 2**64") - if self._serial is not None: - raise ValueError("serial already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def type(self, type: SSHCertificateType) -> SSHCertificateBuilder: - if not isinstance(type, SSHCertificateType): - raise TypeError("type must be an SSHCertificateType") - if self._type is not None: - raise ValueError("type already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def key_id(self, key_id: bytes) -> SSHCertificateBuilder: - if not isinstance(key_id, bytes): - raise TypeError("key_id must be bytes") - if self._key_id is not None: - raise ValueError("key_id already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def valid_principals( - self, valid_principals: list[bytes] - ) -> SSHCertificateBuilder: - if self._valid_for_all_principals: - raise ValueError( - "Principals can't be set because the cert is valid " - "for all principals" - ) - if ( - not all(isinstance(x, bytes) for x in valid_principals) - or not valid_principals - ): - raise TypeError( - "principals must be a list of bytes and can't be empty" - ) - if self._valid_principals: - raise ValueError("valid_principals already set") - - if len(valid_principals) > _SSHKEY_CERT_MAX_PRINCIPALS: - raise ValueError( - "Reached or exceeded the maximum number of valid_principals" - ) - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def valid_for_all_principals(self): - if self._valid_principals: - raise ValueError( - "valid_principals already set, can't set " - "valid_for_all_principals" - ) - if self._valid_for_all_principals: - raise ValueError("valid_for_all_principals already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=True, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def valid_before(self, valid_before: int | float) -> SSHCertificateBuilder: - if not isinstance(valid_before, (int, float)): - raise TypeError("valid_before must be an int or float") - valid_before = int(valid_before) - if valid_before < 0 or valid_before >= 2**64: - raise ValueError("valid_before must [0, 2**64)") - if self._valid_before is not None: - raise ValueError("valid_before already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def valid_after(self, valid_after: int | float) -> SSHCertificateBuilder: - if not isinstance(valid_after, (int, float)): - raise TypeError("valid_after must be an int or float") - valid_after = int(valid_after) - if valid_after < 0 or valid_after >= 2**64: - raise ValueError("valid_after must [0, 2**64)") - if self._valid_after is not None: - raise ValueError("valid_after already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def add_critical_option( - self, name: bytes, value: bytes - ) -> SSHCertificateBuilder: - if not isinstance(name, bytes) or not isinstance(value, bytes): - raise TypeError("name and value must be bytes") - # This is O(n**2) - if name in [name for name, _ in self._critical_options]: - raise ValueError("Duplicate critical option name") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=[*self._critical_options, (name, value)], - _extensions=self._extensions, - ) - - def add_extension( - self, name: bytes, value: bytes - ) -> SSHCertificateBuilder: - if not isinstance(name, bytes) or not isinstance(value, bytes): - raise TypeError("name and value must be bytes") - # This is O(n**2) - if name in [name for name, _ in self._extensions]: - raise ValueError("Duplicate extension name") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=[*self._extensions, (name, value)], - ) - - def sign(self, private_key: SSHCertPrivateKeyTypes) -> SSHCertificate: - if not isinstance( - private_key, - ( - ec.EllipticCurvePrivateKey, - rsa.RSAPrivateKey, - ed25519.Ed25519PrivateKey, - ), - ): - raise TypeError("Unsupported private key type") - - if self._public_key is None: - raise ValueError("public_key must be set") - - # Not required - serial = 0 if self._serial is None else self._serial - - if self._type is None: - raise ValueError("type must be set") - - # Not required - key_id = b"" if self._key_id is None else self._key_id - - # A zero length list is valid, but means the certificate - # is valid for any principal of the specified type. We require - # the user to explicitly set valid_for_all_principals to get - # that behavior. - if not self._valid_principals and not self._valid_for_all_principals: - raise ValueError( - "valid_principals must be set if valid_for_all_principals " - "is False" - ) - - if self._valid_before is None: - raise ValueError("valid_before must be set") - - if self._valid_after is None: - raise ValueError("valid_after must be set") - - if self._valid_after > self._valid_before: - raise ValueError("valid_after must be earlier than valid_before") - - # lexically sort our byte strings - self._critical_options.sort(key=lambda x: x[0]) - self._extensions.sort(key=lambda x: x[0]) - - key_type = _get_ssh_key_type(self._public_key) - cert_prefix = key_type + _CERT_SUFFIX - - # Marshal the bytes to be signed - nonce = os.urandom(32) - kformat = _lookup_kformat(key_type) - f = _FragList() - f.put_sshstr(cert_prefix) - f.put_sshstr(nonce) - kformat.encode_public(self._public_key, f) - f.put_u64(serial) - f.put_u32(self._type.value) - f.put_sshstr(key_id) - fprincipals = _FragList() - for p in self._valid_principals: - fprincipals.put_sshstr(p) - f.put_sshstr(fprincipals.tobytes()) - f.put_u64(self._valid_after) - f.put_u64(self._valid_before) - fcrit = _FragList() - for name, value in self._critical_options: - fcrit.put_sshstr(name) - if len(value) > 0: - foptval = _FragList() - foptval.put_sshstr(value) - fcrit.put_sshstr(foptval.tobytes()) - else: - fcrit.put_sshstr(value) - f.put_sshstr(fcrit.tobytes()) - fext = _FragList() - for name, value in self._extensions: - fext.put_sshstr(name) - if len(value) > 0: - fextval = _FragList() - fextval.put_sshstr(value) - fext.put_sshstr(fextval.tobytes()) - else: - fext.put_sshstr(value) - f.put_sshstr(fext.tobytes()) - f.put_sshstr(b"") # RESERVED FIELD - # encode CA public key - ca_type = _get_ssh_key_type(private_key) - caformat = _lookup_kformat(ca_type) - caf = _FragList() - caf.put_sshstr(ca_type) - caformat.encode_public(private_key.public_key(), caf) - f.put_sshstr(caf.tobytes()) - # Sigs according to the rules defined for the CA's public key - # (RFC4253 section 6.6 for ssh-rsa, RFC5656 for ECDSA, - # and RFC8032 for Ed25519). - if isinstance(private_key, ed25519.Ed25519PrivateKey): - signature = private_key.sign(f.tobytes()) - fsig = _FragList() - fsig.put_sshstr(ca_type) - fsig.put_sshstr(signature) - f.put_sshstr(fsig.tobytes()) - elif isinstance(private_key, ec.EllipticCurvePrivateKey): - hash_alg = _get_ec_hash_alg(private_key.curve) - signature = private_key.sign(f.tobytes(), ec.ECDSA(hash_alg)) - r, s = asym_utils.decode_dss_signature(signature) - fsig = _FragList() - fsig.put_sshstr(ca_type) - fsigblob = _FragList() - fsigblob.put_mpint(r) - fsigblob.put_mpint(s) - fsig.put_sshstr(fsigblob.tobytes()) - f.put_sshstr(fsig.tobytes()) - - else: - assert isinstance(private_key, rsa.RSAPrivateKey) - # Just like Golang, we're going to use SHA512 for RSA - # https://cs.opensource.google/go/x/crypto/+/refs/tags/ - # v0.4.0:ssh/certs.go;l=445 - # RFC 8332 defines SHA256 and 512 as options - fsig = _FragList() - fsig.put_sshstr(_SSH_RSA_SHA512) - signature = private_key.sign( - f.tobytes(), padding.PKCS1v15(), hashes.SHA512() - ) - fsig.put_sshstr(signature) - f.put_sshstr(fsig.tobytes()) - - cert_data = binascii.b2a_base64(f.tobytes()).strip() - # load_ssh_public_identity returns a union, but this is - # guaranteed to be an SSHCertificate, so we cast to make - # mypy happy. - return typing.cast( - SSHCertificate, - load_ssh_public_identity(b"".join([cert_prefix, b" ", cert_data])), - ) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py deleted file mode 100644 index c1af4230..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - - -class InvalidToken(Exception): - pass diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py deleted file mode 100644 index 21fb0004..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py +++ /dev/null @@ -1,101 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import base64 -import typing -from urllib.parse import quote, urlencode - -from cryptography.hazmat.primitives import constant_time, hmac -from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512 -from cryptography.hazmat.primitives.twofactor import InvalidToken -from cryptography.utils import Buffer - -HOTPHashTypes = typing.Union[SHA1, SHA256, SHA512] - - -def _generate_uri( - hotp: HOTP, - type_name: str, - account_name: str, - issuer: str | None, - extra_parameters: list[tuple[str, int]], -) -> str: - parameters = [ - ("digits", hotp._length), - ("secret", base64.b32encode(hotp._key)), - ("algorithm", hotp._algorithm.name.upper()), - ] - - if issuer is not None: - parameters.append(("issuer", issuer)) - - parameters.extend(extra_parameters) - - label = ( - f"{quote(issuer)}:{quote(account_name)}" - if issuer - else quote(account_name) - ) - return f"otpauth://{type_name}/{label}?{urlencode(parameters)}" - - -class HOTP: - def __init__( - self, - key: Buffer, - length: int, - algorithm: HOTPHashTypes, - backend: typing.Any = None, - enforce_key_length: bool = True, - ) -> None: - if len(key) < 16 and enforce_key_length is True: - raise ValueError("Key length has to be at least 128 bits.") - - if not isinstance(length, int): - raise TypeError("Length parameter must be an integer type.") - - if length < 6 or length > 8: - raise ValueError("Length of HOTP has to be between 6 and 8.") - - if not isinstance(algorithm, (SHA1, SHA256, SHA512)): - raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.") - - self._key = key - self._length = length - self._algorithm = algorithm - - def generate(self, counter: int) -> bytes: - if not isinstance(counter, int): - raise TypeError("Counter parameter must be an integer type.") - - truncated_value = self._dynamic_truncate(counter) - hotp = truncated_value % (10**self._length) - return "{0:0{1}}".format(hotp, self._length).encode() - - def verify(self, hotp: bytes, counter: int) -> None: - if not constant_time.bytes_eq(self.generate(counter), hotp): - raise InvalidToken("Supplied HOTP value does not match.") - - def _dynamic_truncate(self, counter: int) -> int: - ctx = hmac.HMAC(self._key, self._algorithm) - - try: - ctx.update(counter.to_bytes(length=8, byteorder="big")) - except OverflowError: - raise ValueError(f"Counter must be between 0 and {2**64 - 1}.") - - hmac_value = ctx.finalize() - - offset = hmac_value[len(hmac_value) - 1] & 0b1111 - p = hmac_value[offset : offset + 4] - return int.from_bytes(p, byteorder="big") & 0x7FFFFFFF - - def get_provisioning_uri( - self, account_name: str, counter: int, issuer: str | None - ) -> str: - return _generate_uri( - self, "hotp", account_name, issuer, [("counter", int(counter))] - ) diff --git a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py b/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py deleted file mode 100644 index 10c725cc..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/hazmat/primitives/twofactor/totp.py +++ /dev/null @@ -1,56 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography.hazmat.primitives import constant_time -from cryptography.hazmat.primitives.twofactor import InvalidToken -from cryptography.hazmat.primitives.twofactor.hotp import ( - HOTP, - HOTPHashTypes, - _generate_uri, -) -from cryptography.utils import Buffer - - -class TOTP: - def __init__( - self, - key: Buffer, - length: int, - algorithm: HOTPHashTypes, - time_step: int, - backend: typing.Any = None, - enforce_key_length: bool = True, - ): - self._time_step = time_step - self._hotp = HOTP( - key, length, algorithm, enforce_key_length=enforce_key_length - ) - - def generate(self, time: int | float) -> bytes: - if not isinstance(time, (int, float)): - raise TypeError( - "Time parameter must be an integer type or float type." - ) - - counter = int(time / self._time_step) - return self._hotp.generate(counter) - - def verify(self, totp: bytes, time: int) -> None: - if not constant_time.bytes_eq(self.generate(time), totp): - raise InvalidToken("Supplied TOTP value does not match.") - - def get_provisioning_uri( - self, account_name: str, issuer: str | None - ) -> str: - return _generate_uri( - self._hotp, - "totp", - account_name, - issuer, - [("period", int(self._time_step))], - ) diff --git a/.venv/lib/python3.12/site-packages/cryptography/py.typed b/.venv/lib/python3.12/site-packages/cryptography/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv/lib/python3.12/site-packages/cryptography/utils.py b/.venv/lib/python3.12/site-packages/cryptography/utils.py deleted file mode 100644 index 9cfc9927..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/utils.py +++ /dev/null @@ -1,135 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import enum -import sys -import types -import typing -import warnings -from collections.abc import Callable, Sequence - - -# We use a UserWarning subclass, instead of DeprecationWarning, because CPython -# decided deprecation warnings should be invisible by default. -class CryptographyDeprecationWarning(UserWarning): - pass - - -# Several APIs were deprecated with no specific end-of-life date because of the -# ubiquity of their use. They should not be removed until we agree on when that -# cycle ends. -DeprecatedIn36 = CryptographyDeprecationWarning -DeprecatedIn40 = CryptographyDeprecationWarning -DeprecatedIn41 = CryptographyDeprecationWarning -DeprecatedIn42 = CryptographyDeprecationWarning -DeprecatedIn43 = CryptographyDeprecationWarning -DeprecatedIn47 = CryptographyDeprecationWarning - - -# If you're wondering why we don't use `Buffer`, it's because `Buffer` would -# be more accurately named: Bufferable. It means something which has an -# `__buffer__`. Which means you can't actually treat the result as a buffer -# (and do things like take a `len()`). -Buffer = typing.Union[bytes, bytearray, memoryview] - - -def _check_bytes(name: str, value: bytes) -> None: - if not isinstance(value, bytes): - raise TypeError(f"{name} must be bytes") - - -def _check_byteslike(name: str, value: Buffer) -> None: - try: - memoryview(value) - except TypeError: - raise TypeError(f"{name} must be bytes-like") - - -def int_to_bytes(integer: int, length: int | None = None) -> bytes: - if length == 0: - raise ValueError("length argument can't be 0") - return integer.to_bytes( - length or (integer.bit_length() + 7) // 8 or 1, "big" - ) - - -class InterfaceNotImplemented(Exception): - pass - - -class _DeprecatedValue: - def __init__(self, value: object, message: str, warning_class): - self.value = value - self.message = message - self.warning_class = warning_class - - -class _ModuleWithDeprecations(types.ModuleType): - def __init__(self, module: types.ModuleType): - super().__init__(module.__name__) - self.__dict__["_module"] = module - - def __getattr__(self, name: str) -> typing.Any: - obj = getattr(self._module, name) - if isinstance(obj, _DeprecatedValue): - warnings.warn(obj.message, obj.warning_class, stacklevel=2) - obj = obj.value - return obj - - def __setattr__(self, attr: str, value: object) -> None: - setattr(self._module, attr, value) - - def __delattr__(self, attr: str) -> None: - obj = getattr(self._module, attr) - if isinstance(obj, _DeprecatedValue): - warnings.warn(obj.message, obj.warning_class, stacklevel=2) - - delattr(self._module, attr) - - def __dir__(self) -> Sequence[str]: - return ["_module", *dir(self._module)] - - -def deprecated( - value: object, - module_name: str, - message: str, - warning_class: type[Warning], - name: str | None = None, -) -> _DeprecatedValue: - module = sys.modules[module_name] - if not isinstance(module, _ModuleWithDeprecations): - sys.modules[module_name] = module = _ModuleWithDeprecations(module) - dv = _DeprecatedValue(value, message, warning_class) - # Maintain backwards compatibility with `name is None` for pyOpenSSL. - if name is not None: - setattr(module, name, dv) - return dv - - -def cached_property(func: Callable) -> property: - cached_name = f"_cached_{func}" - sentinel = object() - - def inner(instance: object): - cache = getattr(instance, cached_name, sentinel) - if cache is not sentinel: - return cache - result = func(instance) - setattr(instance, cached_name, result) - return result - - return property(inner) - - -# Python 3.10 changed representation of enums. We use well-defined object -# representation and string representation from Python 3.9. -class Enum(enum.Enum): - def __repr__(self) -> str: - return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>" - - def __str__(self) -> str: - return f"{self.__class__.__name__}.{self._name_}" diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/__init__.py b/.venv/lib/python3.12/site-packages/cryptography/x509/__init__.py deleted file mode 100644 index cb348335..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/x509/__init__.py +++ /dev/null @@ -1,271 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.x509 import certificate_transparency, oid, verification -from cryptography.x509.base import ( - Attribute, - AttributeNotFound, - Attributes, - Certificate, - CertificateBuilder, - CertificateRevocationList, - CertificateRevocationListBuilder, - CertificateSigningRequest, - CertificateSigningRequestBuilder, - InvalidVersion, - RevokedCertificate, - RevokedCertificateBuilder, - Version, - load_der_x509_certificate, - load_der_x509_crl, - load_der_x509_csr, - load_pem_x509_certificate, - load_pem_x509_certificates, - load_pem_x509_crl, - load_pem_x509_csr, - random_serial_number, -) -from cryptography.x509.extensions import ( - AccessDescription, - Admission, - Admissions, - AuthorityInformationAccess, - AuthorityKeyIdentifier, - BasicConstraints, - CertificateIssuer, - CertificatePolicies, - CRLDistributionPoints, - CRLNumber, - CRLReason, - DeltaCRLIndicator, - DistributionPoint, - DuplicateExtension, - ExtendedKeyUsage, - Extension, - ExtensionNotFound, - Extensions, - ExtensionType, - FreshestCRL, - GeneralNames, - InhibitAnyPolicy, - InvalidityDate, - IssuerAlternativeName, - IssuingDistributionPoint, - KeyUsage, - MSCertificateTemplate, - NameConstraints, - NamingAuthority, - NoticeReference, - OCSPAcceptableResponses, - OCSPNoCheck, - OCSPNonce, - PolicyConstraints, - PolicyInformation, - PrecertificateSignedCertificateTimestamps, - PrecertPoison, - PrivateKeyUsagePeriod, - ProfessionInfo, - ReasonFlags, - SignedCertificateTimestamps, - SubjectAlternativeName, - SubjectInformationAccess, - SubjectKeyIdentifier, - TLSFeature, - TLSFeatureType, - UnrecognizedExtension, - UserNotice, -) -from cryptography.x509.general_name import ( - DirectoryName, - DNSName, - GeneralName, - IPAddress, - OtherName, - RegisteredID, - RFC822Name, - UniformResourceIdentifier, - UnsupportedGeneralNameType, -) -from cryptography.x509.name import ( - Name, - NameAttribute, - RelativeDistinguishedName, -) -from cryptography.x509.oid import ( - AuthorityInformationAccessOID, - CertificatePoliciesOID, - CRLEntryExtensionOID, - ExtendedKeyUsageOID, - ExtensionOID, - NameOID, - ObjectIdentifier, - PublicKeyAlgorithmOID, - SignatureAlgorithmOID, -) - -OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS -OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER -OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS -OID_CERTIFICATE_POLICIES = ExtensionOID.CERTIFICATE_POLICIES -OID_CRL_DISTRIBUTION_POINTS = ExtensionOID.CRL_DISTRIBUTION_POINTS -OID_EXTENDED_KEY_USAGE = ExtensionOID.EXTENDED_KEY_USAGE -OID_FRESHEST_CRL = ExtensionOID.FRESHEST_CRL -OID_INHIBIT_ANY_POLICY = ExtensionOID.INHIBIT_ANY_POLICY -OID_ISSUER_ALTERNATIVE_NAME = ExtensionOID.ISSUER_ALTERNATIVE_NAME -OID_KEY_USAGE = ExtensionOID.KEY_USAGE -OID_PRIVATE_KEY_USAGE_PERIOD = ExtensionOID.PRIVATE_KEY_USAGE_PERIOD -OID_NAME_CONSTRAINTS = ExtensionOID.NAME_CONSTRAINTS -OID_OCSP_NO_CHECK = ExtensionOID.OCSP_NO_CHECK -OID_POLICY_CONSTRAINTS = ExtensionOID.POLICY_CONSTRAINTS -OID_POLICY_MAPPINGS = ExtensionOID.POLICY_MAPPINGS -OID_SUBJECT_ALTERNATIVE_NAME = ExtensionOID.SUBJECT_ALTERNATIVE_NAME -OID_SUBJECT_DIRECTORY_ATTRIBUTES = ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES -OID_SUBJECT_INFORMATION_ACCESS = ExtensionOID.SUBJECT_INFORMATION_ACCESS -OID_SUBJECT_KEY_IDENTIFIER = ExtensionOID.SUBJECT_KEY_IDENTIFIER - -OID_DSA_WITH_SHA1 = SignatureAlgorithmOID.DSA_WITH_SHA1 -OID_DSA_WITH_SHA224 = SignatureAlgorithmOID.DSA_WITH_SHA224 -OID_DSA_WITH_SHA256 = SignatureAlgorithmOID.DSA_WITH_SHA256 -OID_ECDSA_WITH_SHA1 = SignatureAlgorithmOID.ECDSA_WITH_SHA1 -OID_ECDSA_WITH_SHA224 = SignatureAlgorithmOID.ECDSA_WITH_SHA224 -OID_ECDSA_WITH_SHA256 = SignatureAlgorithmOID.ECDSA_WITH_SHA256 -OID_ECDSA_WITH_SHA384 = SignatureAlgorithmOID.ECDSA_WITH_SHA384 -OID_ECDSA_WITH_SHA512 = SignatureAlgorithmOID.ECDSA_WITH_SHA512 -OID_RSA_WITH_MD5 = SignatureAlgorithmOID.RSA_WITH_MD5 -OID_RSA_WITH_SHA1 = SignatureAlgorithmOID.RSA_WITH_SHA1 -OID_RSA_WITH_SHA224 = SignatureAlgorithmOID.RSA_WITH_SHA224 -OID_RSA_WITH_SHA256 = SignatureAlgorithmOID.RSA_WITH_SHA256 -OID_RSA_WITH_SHA384 = SignatureAlgorithmOID.RSA_WITH_SHA384 -OID_RSA_WITH_SHA512 = SignatureAlgorithmOID.RSA_WITH_SHA512 -OID_RSASSA_PSS = SignatureAlgorithmOID.RSASSA_PSS - -OID_COMMON_NAME = NameOID.COMMON_NAME -OID_COUNTRY_NAME = NameOID.COUNTRY_NAME -OID_DOMAIN_COMPONENT = NameOID.DOMAIN_COMPONENT -OID_DN_QUALIFIER = NameOID.DN_QUALIFIER -OID_EMAIL_ADDRESS = NameOID.EMAIL_ADDRESS -OID_GENERATION_QUALIFIER = NameOID.GENERATION_QUALIFIER -OID_GIVEN_NAME = NameOID.GIVEN_NAME -OID_LOCALITY_NAME = NameOID.LOCALITY_NAME -OID_ORGANIZATIONAL_UNIT_NAME = NameOID.ORGANIZATIONAL_UNIT_NAME -OID_ORGANIZATION_NAME = NameOID.ORGANIZATION_NAME -OID_PSEUDONYM = NameOID.PSEUDONYM -OID_SERIAL_NUMBER = NameOID.SERIAL_NUMBER -OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME -OID_SURNAME = NameOID.SURNAME -OID_TITLE = NameOID.TITLE - -OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH -OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING -OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION -OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING -OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH -OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING - -OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY -OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER -OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE - -OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER -OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON -OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE - -OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS -OID_OCSP = AuthorityInformationAccessOID.OCSP - -__all__ = [ - "OID_CA_ISSUERS", - "OID_OCSP", - "AccessDescription", - "Admission", - "Admissions", - "Attribute", - "AttributeNotFound", - "Attributes", - "AuthorityInformationAccess", - "AuthorityKeyIdentifier", - "BasicConstraints", - "CRLDistributionPoints", - "CRLNumber", - "CRLReason", - "Certificate", - "CertificateBuilder", - "CertificateIssuer", - "CertificatePolicies", - "CertificateRevocationList", - "CertificateRevocationListBuilder", - "CertificateSigningRequest", - "CertificateSigningRequestBuilder", - "DNSName", - "DeltaCRLIndicator", - "DirectoryName", - "DistributionPoint", - "DuplicateExtension", - "ExtendedKeyUsage", - "Extension", - "ExtensionNotFound", - "ExtensionType", - "Extensions", - "FreshestCRL", - "GeneralName", - "GeneralNames", - "IPAddress", - "InhibitAnyPolicy", - "InvalidVersion", - "InvalidityDate", - "IssuerAlternativeName", - "IssuingDistributionPoint", - "KeyUsage", - "MSCertificateTemplate", - "Name", - "NameAttribute", - "NameConstraints", - "NameOID", - "NamingAuthority", - "NoticeReference", - "OCSPAcceptableResponses", - "OCSPNoCheck", - "OCSPNonce", - "ObjectIdentifier", - "OtherName", - "PolicyConstraints", - "PolicyInformation", - "PrecertPoison", - "PrecertificateSignedCertificateTimestamps", - "PrivateKeyUsagePeriod", - "ProfessionInfo", - "PublicKeyAlgorithmOID", - "RFC822Name", - "ReasonFlags", - "RegisteredID", - "RelativeDistinguishedName", - "RevokedCertificate", - "RevokedCertificateBuilder", - "SignatureAlgorithmOID", - "SignedCertificateTimestamps", - "SubjectAlternativeName", - "SubjectInformationAccess", - "SubjectKeyIdentifier", - "TLSFeature", - "TLSFeatureType", - "UniformResourceIdentifier", - "UnrecognizedExtension", - "UnsupportedGeneralNameType", - "UserNotice", - "Version", - "certificate_transparency", - "load_der_x509_certificate", - "load_der_x509_crl", - "load_der_x509_csr", - "load_pem_x509_certificate", - "load_pem_x509_certificates", - "load_pem_x509_crl", - "load_pem_x509_csr", - "oid", - "random_serial_number", - "verification", - "verification", -] diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/base.py b/.venv/lib/python3.12/site-packages/cryptography/x509/base.py deleted file mode 100644 index 72efc2d1..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/x509/base.py +++ /dev/null @@ -1,798 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import datetime -import os -import typing -from collections.abc import Iterable - -from cryptography import utils -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import ( - dsa, - ec, - ed448, - ed25519, - mldsa, - padding, - rsa, - x448, - x25519, -) -from cryptography.hazmat.primitives.asymmetric.types import ( - CertificateIssuerPrivateKeyTypes, - CertificatePublicKeyTypes, -) -from cryptography.x509.extensions import ( - Extension, - ExtensionType, - _make_sequence_methods, -) -from cryptography.x509.name import Name, _ASN1Type -from cryptography.x509.oid import ObjectIdentifier - -_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1) - -# This must be kept in sync with sign.rs's list of allowable types in -# identify_hash_type -_AllowedHashTypes = typing.Union[ - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - hashes.SHA3_224, - hashes.SHA3_256, - hashes.SHA3_384, - hashes.SHA3_512, -] - - -class AttributeNotFound(Exception): - def __init__(self, msg: str, oid: ObjectIdentifier) -> None: - super().__init__(msg) - self.oid = oid - - -def _reject_duplicate_extension( - extension: Extension[ExtensionType], - extensions: list[Extension[ExtensionType]], -) -> None: - # This is quadratic in the number of extensions - for e in extensions: - if e.oid == extension.oid: - raise ValueError("This extension has already been set.") - - -def _reject_duplicate_attribute( - oid: ObjectIdentifier, - attributes: list[tuple[ObjectIdentifier, bytes, int | None]], -) -> None: - # This is quadratic in the number of attributes - for attr_oid, _, _ in attributes: - if attr_oid == oid: - raise ValueError("This attribute has already been set.") - - -def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime: - """Normalizes a datetime to a naive datetime in UTC. - - time -- datetime to normalize. Assumed to be in UTC if not timezone - aware. - """ - if time.tzinfo is not None: - offset = time.utcoffset() - offset = offset if offset else datetime.timedelta() - return time.replace(tzinfo=None) - offset - else: - return time - - -class Attribute: - def __init__( - self, - oid: ObjectIdentifier, - value: bytes, - _type: int = _ASN1Type.UTF8String.value, - ) -> None: - self._oid = oid - self._value = value - self._type = _type - - @property - def oid(self) -> ObjectIdentifier: - return self._oid - - @property - def value(self) -> bytes: - return self._value - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Attribute): - return NotImplemented - - return ( - self.oid == other.oid - and self.value == other.value - and self._type == other._type - ) - - def __hash__(self) -> int: - return hash((self.oid, self.value, self._type)) - - -class Attributes: - def __init__( - self, - attributes: Iterable[Attribute], - ) -> None: - self._attributes = list(attributes) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_attributes") - - def __repr__(self) -> str: - return f"" - - def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute: - for attr in self: - if attr.oid == oid: - return attr - - raise AttributeNotFound(f"No {oid} attribute was found", oid) - - -class Version(utils.Enum): - v1 = 0 - v3 = 2 - - -class InvalidVersion(Exception): - def __init__(self, msg: str, parsed_version: int) -> None: - super().__init__(msg) - self.parsed_version = parsed_version - - -Certificate = rust_x509.Certificate -RevokedCertificate = rust_x509.RevokedCertificate - - -CertificateRevocationList = rust_x509.CertificateRevocationList -CertificateSigningRequest = rust_x509.CertificateSigningRequest - - -load_pem_x509_certificate = rust_x509.load_pem_x509_certificate -load_der_x509_certificate = rust_x509.load_der_x509_certificate - -load_pem_x509_certificates = rust_x509.load_pem_x509_certificates - -load_pem_x509_csr = rust_x509.load_pem_x509_csr -load_der_x509_csr = rust_x509.load_der_x509_csr - -load_pem_x509_crl = rust_x509.load_pem_x509_crl -load_der_x509_crl = rust_x509.load_der_x509_crl - - -class CertificateSigningRequestBuilder: - def __init__( - self, - subject_name: Name | None = None, - extensions: list[Extension[ExtensionType]] = [], - attributes: list[tuple[ObjectIdentifier, bytes, int | None]] = [], - ): - """ - Creates an empty X.509 certificate request (v1). - """ - self._subject_name = subject_name - self._extensions = extensions - self._attributes = attributes - - def subject_name(self, name: Name) -> CertificateSigningRequestBuilder: - """ - Sets the certificate requestor's distinguished name. - """ - if not isinstance(name, Name): - raise TypeError("Expecting x509.Name object.") - if self._subject_name is not None: - raise ValueError("The subject name may only be set once.") - return CertificateSigningRequestBuilder( - name, self._extensions, self._attributes - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> CertificateSigningRequestBuilder: - """ - Adds an X.509 extension to the certificate request. - """ - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return CertificateSigningRequestBuilder( - self._subject_name, - [*self._extensions, extension], - self._attributes, - ) - - def add_attribute( - self, - oid: ObjectIdentifier, - value: bytes, - *, - _tag: _ASN1Type | None = None, - ) -> CertificateSigningRequestBuilder: - """ - Adds an X.509 attribute with an OID and associated value. - """ - if not isinstance(oid, ObjectIdentifier): - raise TypeError("oid must be an ObjectIdentifier") - - if not isinstance(value, bytes): - raise TypeError("value must be bytes") - - if _tag is not None and not isinstance(_tag, _ASN1Type): - raise TypeError("tag must be _ASN1Type") - - _reject_duplicate_attribute(oid, self._attributes) - - if _tag is not None: - tag = _tag.value - else: - tag = None - - return CertificateSigningRequestBuilder( - self._subject_name, - self._extensions, - [*self._attributes, (oid, value, tag)], - ) - - def sign( - self, - private_key: CertificateIssuerPrivateKeyTypes, - algorithm: _AllowedHashTypes | None, - backend: typing.Any = None, - *, - rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, - ecdsa_deterministic: bool | None = None, - ) -> CertificateSigningRequest: - """ - Signs the request using the requestor's private key. - """ - if self._subject_name is None: - raise ValueError("A CertificateSigningRequest must have a subject") - - if rsa_padding is not None: - if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): - raise TypeError("Padding must be PSS or PKCS1v15") - if not isinstance(private_key, rsa.RSAPrivateKey): - raise TypeError("Padding is only supported for RSA keys") - - if ecdsa_deterministic is not None: - if not isinstance(private_key, ec.EllipticCurvePrivateKey): - raise TypeError( - "Deterministic ECDSA is only supported for EC keys" - ) - - return rust_x509.create_x509_csr( - self, - private_key, - algorithm, - rsa_padding, - ecdsa_deterministic, - ) - - -class CertificateBuilder: - _extensions: list[Extension[ExtensionType]] - - def __init__( - self, - issuer_name: Name | None = None, - subject_name: Name | None = None, - public_key: CertificatePublicKeyTypes | None = None, - serial_number: int | None = None, - not_valid_before: datetime.datetime | None = None, - not_valid_after: datetime.datetime | None = None, - extensions: list[Extension[ExtensionType]] = [], - public_key_rsa_padding: type[padding.PSS] | None = None, - ) -> None: - self._version = Version.v3 - self._issuer_name = issuer_name - self._subject_name = subject_name - self._public_key = public_key - self._serial_number = serial_number - self._not_valid_before = not_valid_before - self._not_valid_after = not_valid_after - self._extensions = extensions - self._public_key_rsa_padding = public_key_rsa_padding - - def issuer_name(self, name: Name) -> CertificateBuilder: - """ - Sets the CA's distinguished name. - """ - if not isinstance(name, Name): - raise TypeError("Expecting x509.Name object.") - if self._issuer_name is not None: - raise ValueError("The issuer name may only be set once.") - return CertificateBuilder( - name, - self._subject_name, - self._public_key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - self._public_key_rsa_padding, - ) - - def subject_name(self, name: Name) -> CertificateBuilder: - """ - Sets the requestor's distinguished name. - """ - if not isinstance(name, Name): - raise TypeError("Expecting x509.Name object.") - if self._subject_name is not None: - raise ValueError("The subject name may only be set once.") - return CertificateBuilder( - self._issuer_name, - name, - self._public_key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - self._public_key_rsa_padding, - ) - - def public_key( - self, - key: CertificatePublicKeyTypes, - *, - rsa_padding: type[padding.PSS] | None = None, - ) -> CertificateBuilder: - """ - Sets the requestor's public key (as found in the signing request). - """ - if not isinstance( - key, - ( - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - mldsa.MLDSA44PublicKey, - mldsa.MLDSA65PublicKey, - mldsa.MLDSA87PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, - ), - ): - raise TypeError( - "Expecting one of DSAPublicKey, RSAPublicKey," - " EllipticCurvePublicKey, Ed25519PublicKey," - " Ed448PublicKey, MLDSA44PublicKey, MLDSA65PublicKey," - " MLDSA87PublicKey, X25519PublicKey, or " - "X448PublicKey." - ) - if rsa_padding is not None: - if rsa_padding is not padding.PSS: - raise TypeError( - "rsa_padding must be the PSS class, not an instance" - ) - if not isinstance(key, rsa.RSAPublicKey): - raise TypeError( - "rsa_padding is only supported with RSA public keys" - ) - if self._public_key is not None: - raise ValueError("The public key may only be set once.") - return CertificateBuilder( - self._issuer_name, - self._subject_name, - key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - rsa_padding, - ) - - def serial_number(self, number: int) -> CertificateBuilder: - """ - Sets the certificate serial number. - """ - if not isinstance(number, int): - raise TypeError("Serial number must be of integral type.") - if self._serial_number is not None: - raise ValueError("The serial number may only be set once.") - if number <= 0: - raise ValueError("The serial number should be positive.") - - # ASN.1 integers are always signed, so most significant bit must be - # zero. - if number.bit_length() >= 160: # As defined in RFC 5280 - raise ValueError( - "The serial number should not be more than 159 bits." - ) - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - self._public_key_rsa_padding, - ) - - def not_valid_before(self, time: datetime.datetime) -> CertificateBuilder: - """ - Sets the certificate activation time. - """ - if not isinstance(time, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._not_valid_before is not None: - raise ValueError("The not valid before may only be set once.") - time = _convert_to_naive_utc_time(time) - if time < _EARLIEST_UTC_TIME: - raise ValueError( - "The not valid before date must be on or after" - " 1950 January 1)." - ) - if self._not_valid_after is not None and time > self._not_valid_after: - raise ValueError( - "The not valid before date must be before the not valid after " - "date." - ) - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - self._serial_number, - time, - self._not_valid_after, - self._extensions, - self._public_key_rsa_padding, - ) - - def not_valid_after(self, time: datetime.datetime) -> CertificateBuilder: - """ - Sets the certificate expiration time. - """ - if not isinstance(time, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._not_valid_after is not None: - raise ValueError("The not valid after may only be set once.") - time = _convert_to_naive_utc_time(time) - if time < _EARLIEST_UTC_TIME: - raise ValueError( - "The not valid after date must be on or after 1950 January 1." - ) - if ( - self._not_valid_before is not None - and time < self._not_valid_before - ): - raise ValueError( - "The not valid after date must be after the not valid before " - "date." - ) - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - self._serial_number, - self._not_valid_before, - time, - self._extensions, - self._public_key_rsa_padding, - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> CertificateBuilder: - """ - Adds an X.509 extension to the certificate. - """ - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - [*self._extensions, extension], - self._public_key_rsa_padding, - ) - - def sign( - self, - private_key: CertificateIssuerPrivateKeyTypes, - algorithm: _AllowedHashTypes | None, - backend: typing.Any = None, - *, - rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, - ecdsa_deterministic: bool | None = None, - ) -> Certificate: - """ - Signs the certificate using the CA's private key. - """ - if self._subject_name is None: - raise ValueError("A certificate must have a subject name") - - if self._issuer_name is None: - raise ValueError("A certificate must have an issuer name") - - if self._serial_number is None: - raise ValueError("A certificate must have a serial number") - - if self._not_valid_before is None: - raise ValueError("A certificate must have a not valid before time") - - if self._not_valid_after is None: - raise ValueError("A certificate must have a not valid after time") - - if self._public_key is None: - raise ValueError("A certificate must have a public key") - - if rsa_padding is not None: - if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): - raise TypeError("Padding must be PSS or PKCS1v15") - if not isinstance(private_key, rsa.RSAPrivateKey): - raise TypeError("Padding is only supported for RSA keys") - - if ecdsa_deterministic is not None: - if not isinstance(private_key, ec.EllipticCurvePrivateKey): - raise TypeError( - "Deterministic ECDSA is only supported for EC keys" - ) - - return rust_x509.create_x509_certificate( - self, - private_key, - algorithm, - rsa_padding, - ecdsa_deterministic, - ) - - -class CertificateRevocationListBuilder: - _extensions: list[Extension[ExtensionType]] - _revoked_certificates: list[RevokedCertificate] - - def __init__( - self, - issuer_name: Name | None = None, - last_update: datetime.datetime | None = None, - next_update: datetime.datetime | None = None, - extensions: list[Extension[ExtensionType]] = [], - revoked_certificates: list[RevokedCertificate] = [], - ): - self._issuer_name = issuer_name - self._last_update = last_update - self._next_update = next_update - self._extensions = extensions - self._revoked_certificates = revoked_certificates - - def issuer_name( - self, issuer_name: Name - ) -> CertificateRevocationListBuilder: - if not isinstance(issuer_name, Name): - raise TypeError("Expecting x509.Name object.") - if self._issuer_name is not None: - raise ValueError("The issuer name may only be set once.") - return CertificateRevocationListBuilder( - issuer_name, - self._last_update, - self._next_update, - self._extensions, - self._revoked_certificates, - ) - - def last_update( - self, last_update: datetime.datetime - ) -> CertificateRevocationListBuilder: - if not isinstance(last_update, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._last_update is not None: - raise ValueError("Last update may only be set once.") - last_update = _convert_to_naive_utc_time(last_update) - if last_update < _EARLIEST_UTC_TIME: - raise ValueError( - "The last update date must be on or after 1950 January 1." - ) - if self._next_update is not None and last_update > self._next_update: - raise ValueError( - "The last update date must be before the next update date." - ) - return CertificateRevocationListBuilder( - self._issuer_name, - last_update, - self._next_update, - self._extensions, - self._revoked_certificates, - ) - - def next_update( - self, next_update: datetime.datetime - ) -> CertificateRevocationListBuilder: - if not isinstance(next_update, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._next_update is not None: - raise ValueError("Last update may only be set once.") - next_update = _convert_to_naive_utc_time(next_update) - if next_update < _EARLIEST_UTC_TIME: - raise ValueError( - "The last update date must be on or after 1950 January 1." - ) - if self._last_update is not None and next_update < self._last_update: - raise ValueError( - "The next update date must be after the last update date." - ) - return CertificateRevocationListBuilder( - self._issuer_name, - self._last_update, - next_update, - self._extensions, - self._revoked_certificates, - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> CertificateRevocationListBuilder: - """ - Adds an X.509 extension to the certificate revocation list. - """ - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - return CertificateRevocationListBuilder( - self._issuer_name, - self._last_update, - self._next_update, - [*self._extensions, extension], - self._revoked_certificates, - ) - - def add_revoked_certificate( - self, revoked_certificate: RevokedCertificate - ) -> CertificateRevocationListBuilder: - """ - Adds a revoked certificate to the CRL. - """ - if not isinstance(revoked_certificate, RevokedCertificate): - raise TypeError("Must be an instance of RevokedCertificate") - - return CertificateRevocationListBuilder( - self._issuer_name, - self._last_update, - self._next_update, - self._extensions, - [*self._revoked_certificates, revoked_certificate], - ) - - def sign( - self, - private_key: CertificateIssuerPrivateKeyTypes, - algorithm: _AllowedHashTypes | None, - backend: typing.Any = None, - *, - rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, - ecdsa_deterministic: bool | None = None, - ) -> CertificateRevocationList: - if self._issuer_name is None: - raise ValueError("A CRL must have an issuer name") - - if self._last_update is None: - raise ValueError("A CRL must have a last update time") - - if self._next_update is None: - raise ValueError("A CRL must have a next update time") - - if rsa_padding is not None: - if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): - raise TypeError("Padding must be PSS or PKCS1v15") - if not isinstance(private_key, rsa.RSAPrivateKey): - raise TypeError("Padding is only supported for RSA keys") - - if ecdsa_deterministic is not None: - if not isinstance(private_key, ec.EllipticCurvePrivateKey): - raise TypeError( - "Deterministic ECDSA is only supported for EC keys" - ) - - return rust_x509.create_x509_crl( - self, - private_key, - algorithm, - rsa_padding, - ecdsa_deterministic, - ) - - -class RevokedCertificateBuilder: - def __init__( - self, - serial_number: int | None = None, - revocation_date: datetime.datetime | None = None, - extensions: list[Extension[ExtensionType]] = [], - ): - self._serial_number = serial_number - self._revocation_date = revocation_date - self._extensions = extensions - - def serial_number(self, number: int) -> RevokedCertificateBuilder: - if not isinstance(number, int): - raise TypeError("Serial number must be of integral type.") - if self._serial_number is not None: - raise ValueError("The serial number may only be set once.") - if number <= 0: - raise ValueError("The serial number should be positive") - - # ASN.1 integers are always signed, so most significant bit must be - # zero. - if number.bit_length() >= 160: # As defined in RFC 5280 - raise ValueError( - "The serial number should not be more than 159 bits." - ) - return RevokedCertificateBuilder( - number, self._revocation_date, self._extensions - ) - - def revocation_date( - self, time: datetime.datetime - ) -> RevokedCertificateBuilder: - if not isinstance(time, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._revocation_date is not None: - raise ValueError("The revocation date may only be set once.") - time = _convert_to_naive_utc_time(time) - if time < _EARLIEST_UTC_TIME: - raise ValueError( - "The revocation date must be on or after 1950 January 1." - ) - return RevokedCertificateBuilder( - self._serial_number, time, self._extensions - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> RevokedCertificateBuilder: - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - return RevokedCertificateBuilder( - self._serial_number, - self._revocation_date, - [*self._extensions, extension], - ) - - def build(self, backend: typing.Any = None) -> RevokedCertificate: - if self._serial_number is None: - raise ValueError("A revoked certificate must have a serial number") - if self._revocation_date is None: - raise ValueError( - "A revoked certificate must have a revocation date" - ) - return rust_x509.create_revoked_certificate(self) - - -def random_serial_number() -> int: - return int.from_bytes(os.urandom(20), "big") >> 1 diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py b/.venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py deleted file mode 100644 index fb66cc60..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/x509/certificate_transparency.py +++ /dev/null @@ -1,35 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography import utils -from cryptography.hazmat.bindings._rust import x509 as rust_x509 - - -class LogEntryType(utils.Enum): - X509_CERTIFICATE = 0 - PRE_CERTIFICATE = 1 - - -class Version(utils.Enum): - v1 = 0 - - -class SignatureAlgorithm(utils.Enum): - """ - Signature algorithms that are valid for SCTs. - - These are exactly the same as SignatureAlgorithm in RFC 5246 (TLS 1.2). - - See: - """ - - ANONYMOUS = 0 - RSA = 1 - DSA = 2 - ECDSA = 3 - - -SignedCertificateTimestamp = rust_x509.Sct diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/extensions.py b/.venv/lib/python3.12/site-packages/cryptography/x509/extensions.py deleted file mode 100644 index 7b78e9e2..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/x509/extensions.py +++ /dev/null @@ -1,2533 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import datetime -import hashlib -import ipaddress -import typing -from collections.abc import Iterable, Iterator - -from cryptography import utils -from cryptography.hazmat.bindings._rust import asn1 -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.hazmat.primitives import constant_time, serialization -from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey -from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey -from cryptography.hazmat.primitives.asymmetric.types import ( - CertificateIssuerPublicKeyTypes, - CertificatePublicKeyTypes, -) -from cryptography.x509.certificate_transparency import ( - SignedCertificateTimestamp, -) -from cryptography.x509.general_name import ( - DirectoryName, - DNSName, - GeneralName, - IPAddress, - OtherName, - RegisteredID, - RFC822Name, - UniformResourceIdentifier, - _IPAddressTypes, -) -from cryptography.x509.name import Name, RelativeDistinguishedName -from cryptography.x509.oid import ( - CRLEntryExtensionOID, - ExtensionOID, - ObjectIdentifier, - OCSPExtensionOID, -) - -ExtensionTypeVar = typing.TypeVar( - "ExtensionTypeVar", bound="ExtensionType", covariant=True -) - - -def _key_identifier_from_public_key( - public_key: CertificatePublicKeyTypes, -) -> bytes: - if isinstance(public_key, RSAPublicKey): - data = public_key.public_bytes( - serialization.Encoding.DER, - serialization.PublicFormat.PKCS1, - ) - elif isinstance(public_key, EllipticCurvePublicKey): - data = public_key.public_bytes( - serialization.Encoding.X962, - serialization.PublicFormat.UncompressedPoint, - ) - else: - # This is a very slow way to do this. - serialized = public_key.public_bytes( - serialization.Encoding.DER, - serialization.PublicFormat.SubjectPublicKeyInfo, - ) - data = asn1.parse_spki_for_data(serialized) - - return hashlib.sha1(data).digest() - - -def _make_sequence_methods(field_name: str): - def len_method(self) -> int: - return len(getattr(self, field_name)) - - def iter_method(self): - return iter(getattr(self, field_name)) - - def getitem_method(self, idx): - return getattr(self, field_name)[idx] - - return len_method, iter_method, getitem_method - - -class DuplicateExtension(Exception): - def __init__(self, msg: str, oid: ObjectIdentifier) -> None: - super().__init__(msg) - self.oid = oid - - -class ExtensionNotFound(Exception): - def __init__(self, msg: str, oid: ObjectIdentifier) -> None: - super().__init__(msg) - self.oid = oid - - -class ExtensionType(metaclass=abc.ABCMeta): - oid: typing.ClassVar[ObjectIdentifier] - - def public_bytes(self) -> bytes: - """ - Serializes the extension type to DER. - """ - raise NotImplementedError( - f"public_bytes is not implemented for extension type {self!r}" - ) - - -class Extensions: - def __init__(self, extensions: Iterable[Extension[ExtensionType]]) -> None: - self._extensions = list(extensions) - - def get_extension_for_oid( - self, oid: ObjectIdentifier - ) -> Extension[ExtensionType]: - for ext in self: - if ext.oid == oid: - return ext - - raise ExtensionNotFound(f"No {oid} extension was found", oid) - - def get_extension_for_class( - self, extclass: type[ExtensionTypeVar] - ) -> Extension[ExtensionTypeVar]: - if extclass is UnrecognizedExtension: - raise TypeError( - "UnrecognizedExtension can't be used with " - "get_extension_for_class because more than one instance of the" - " class may be present." - ) - - for ext in self: - if isinstance(ext.value, extclass): - return ext - - raise ExtensionNotFound( - f"No {extclass} extension was found", extclass.oid - ) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions") - - def __repr__(self) -> str: - return f"" - - -class CRLNumber(ExtensionType): - oid = ExtensionOID.CRL_NUMBER - - def __init__(self, crl_number: int) -> None: - if not isinstance(crl_number, int): - raise TypeError("crl_number must be an integer") - - self._crl_number = crl_number - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CRLNumber): - return NotImplemented - - return self.crl_number == other.crl_number - - def __hash__(self) -> int: - return hash(self.crl_number) - - def __repr__(self) -> str: - return f"" - - @property - def crl_number(self) -> int: - return self._crl_number - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class AuthorityKeyIdentifier(ExtensionType): - oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER - - def __init__( - self, - key_identifier: bytes | None, - authority_cert_issuer: Iterable[GeneralName] | None, - authority_cert_serial_number: int | None, - ) -> None: - if (authority_cert_issuer is None) != ( - authority_cert_serial_number is None - ): - raise ValueError( - "authority_cert_issuer and authority_cert_serial_number " - "must both be present or both None" - ) - - if authority_cert_issuer is not None: - authority_cert_issuer = list(authority_cert_issuer) - if not all( - isinstance(x, GeneralName) for x in authority_cert_issuer - ): - raise TypeError( - "authority_cert_issuer must be a list of GeneralName " - "objects" - ) - - if authority_cert_serial_number is not None and not isinstance( - authority_cert_serial_number, int - ): - raise TypeError("authority_cert_serial_number must be an integer") - - self._key_identifier = key_identifier - self._authority_cert_issuer = authority_cert_issuer - self._authority_cert_serial_number = authority_cert_serial_number - - # This takes a subset of CertificatePublicKeyTypes because an issuer - # cannot have an X25519/X448 key. This introduces some unfortunate - # asymmetry that requires typing users to explicitly - # narrow their type, but we should make this accurate and not just - # convenient. - @classmethod - def from_issuer_public_key( - cls, public_key: CertificateIssuerPublicKeyTypes - ) -> AuthorityKeyIdentifier: - digest = _key_identifier_from_public_key(public_key) - return cls( - key_identifier=digest, - authority_cert_issuer=None, - authority_cert_serial_number=None, - ) - - @classmethod - def from_issuer_subject_key_identifier( - cls, ski: SubjectKeyIdentifier - ) -> AuthorityKeyIdentifier: - return cls( - key_identifier=ski.digest, - authority_cert_issuer=None, - authority_cert_serial_number=None, - ) - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, AuthorityKeyIdentifier): - return NotImplemented - - return ( - self.key_identifier == other.key_identifier - and self.authority_cert_issuer == other.authority_cert_issuer - and self.authority_cert_serial_number - == other.authority_cert_serial_number - ) - - def __hash__(self) -> int: - if self.authority_cert_issuer is None: - aci = None - else: - aci = tuple(self.authority_cert_issuer) - return hash( - (self.key_identifier, aci, self.authority_cert_serial_number) - ) - - @property - def key_identifier(self) -> bytes | None: - return self._key_identifier - - @property - def authority_cert_issuer( - self, - ) -> list[GeneralName] | None: - return self._authority_cert_issuer - - @property - def authority_cert_serial_number(self) -> int | None: - return self._authority_cert_serial_number - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class SubjectKeyIdentifier(ExtensionType): - oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER - - def __init__(self, digest: bytes) -> None: - self._digest = digest - - @classmethod - def from_public_key( - cls, public_key: CertificatePublicKeyTypes - ) -> SubjectKeyIdentifier: - return cls(_key_identifier_from_public_key(public_key)) - - @property - def digest(self) -> bytes: - return self._digest - - @property - def key_identifier(self) -> bytes: - return self._digest - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SubjectKeyIdentifier): - return NotImplemented - - return constant_time.bytes_eq(self.digest, other.digest) - - def __hash__(self) -> int: - return hash(self.digest) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class AuthorityInformationAccess(ExtensionType): - oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS - - def __init__(self, descriptions: Iterable[AccessDescription]) -> None: - descriptions = list(descriptions) - if not all(isinstance(x, AccessDescription) for x in descriptions): - raise TypeError( - "Every item in the descriptions list must be an " - "AccessDescription" - ) - - self._descriptions = descriptions - - __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, AuthorityInformationAccess): - return NotImplemented - - return self._descriptions == other._descriptions - - def __hash__(self) -> int: - return hash(tuple(self._descriptions)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class SubjectInformationAccess(ExtensionType): - oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS - - def __init__(self, descriptions: Iterable[AccessDescription]) -> None: - descriptions = list(descriptions) - if not all(isinstance(x, AccessDescription) for x in descriptions): - raise TypeError( - "Every item in the descriptions list must be an " - "AccessDescription" - ) - - self._descriptions = descriptions - - __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SubjectInformationAccess): - return NotImplemented - - return self._descriptions == other._descriptions - - def __hash__(self) -> int: - return hash(tuple(self._descriptions)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class AccessDescription: - def __init__( - self, access_method: ObjectIdentifier, access_location: GeneralName - ) -> None: - if not isinstance(access_method, ObjectIdentifier): - raise TypeError("access_method must be an ObjectIdentifier") - - if not isinstance(access_location, GeneralName): - raise TypeError("access_location must be a GeneralName") - - self._access_method = access_method - self._access_location = access_location - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, AccessDescription): - return NotImplemented - - return ( - self.access_method == other.access_method - and self.access_location == other.access_location - ) - - def __hash__(self) -> int: - return hash((self.access_method, self.access_location)) - - @property - def access_method(self) -> ObjectIdentifier: - return self._access_method - - @property - def access_location(self) -> GeneralName: - return self._access_location - - -class BasicConstraints(ExtensionType): - oid = ExtensionOID.BASIC_CONSTRAINTS - - def __init__(self, ca: bool, path_length: int | None) -> None: - if not isinstance(ca, bool): - raise TypeError("ca must be a boolean value") - - if path_length is not None and not ca: - raise ValueError("path_length must be None when ca is False") - - if path_length is not None and ( - not isinstance(path_length, int) or path_length < 0 - ): - raise TypeError( - "path_length must be a non-negative integer or None" - ) - - self._ca = ca - self._path_length = path_length - - @property - def ca(self) -> bool: - return self._ca - - @property - def path_length(self) -> int | None: - return self._path_length - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, BasicConstraints): - return NotImplemented - - return self.ca == other.ca and self.path_length == other.path_length - - def __hash__(self) -> int: - return hash((self.ca, self.path_length)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class DeltaCRLIndicator(ExtensionType): - oid = ExtensionOID.DELTA_CRL_INDICATOR - - def __init__(self, crl_number: int) -> None: - if not isinstance(crl_number, int): - raise TypeError("crl_number must be an integer") - - self._crl_number = crl_number - - @property - def crl_number(self) -> int: - return self._crl_number - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DeltaCRLIndicator): - return NotImplemented - - return self.crl_number == other.crl_number - - def __hash__(self) -> int: - return hash(self.crl_number) - - def __repr__(self) -> str: - return f"" - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CRLDistributionPoints(ExtensionType): - oid = ExtensionOID.CRL_DISTRIBUTION_POINTS - - def __init__( - self, distribution_points: Iterable[DistributionPoint] - ) -> None: - distribution_points = list(distribution_points) - if not all( - isinstance(x, DistributionPoint) for x in distribution_points - ): - raise TypeError( - "distribution_points must be a list of DistributionPoint " - "objects" - ) - - self._distribution_points = distribution_points - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_distribution_points" - ) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CRLDistributionPoints): - return NotImplemented - - return self._distribution_points == other._distribution_points - - def __hash__(self) -> int: - return hash(tuple(self._distribution_points)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class FreshestCRL(ExtensionType): - oid = ExtensionOID.FRESHEST_CRL - - def __init__( - self, distribution_points: Iterable[DistributionPoint] - ) -> None: - distribution_points = list(distribution_points) - if not all( - isinstance(x, DistributionPoint) for x in distribution_points - ): - raise TypeError( - "distribution_points must be a list of DistributionPoint " - "objects" - ) - - self._distribution_points = distribution_points - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_distribution_points" - ) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, FreshestCRL): - return NotImplemented - - return self._distribution_points == other._distribution_points - - def __hash__(self) -> int: - return hash(tuple(self._distribution_points)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class DistributionPoint: - def __init__( - self, - full_name: Iterable[GeneralName] | None, - relative_name: RelativeDistinguishedName | None, - reasons: frozenset[ReasonFlags] | None, - crl_issuer: Iterable[GeneralName] | None, - ) -> None: - if full_name and relative_name: - raise ValueError( - "You cannot provide both full_name and relative_name, at " - "least one must be None." - ) - if not full_name and not relative_name and not crl_issuer: - raise ValueError( - "Either full_name, relative_name or crl_issuer must be " - "provided." - ) - - if full_name is not None: - full_name = list(full_name) - if not all(isinstance(x, GeneralName) for x in full_name): - raise TypeError( - "full_name must be a list of GeneralName objects" - ) - - if relative_name: - if not isinstance(relative_name, RelativeDistinguishedName): - raise TypeError( - "relative_name must be a RelativeDistinguishedName" - ) - - if crl_issuer is not None: - crl_issuer = list(crl_issuer) - if not all(isinstance(x, GeneralName) for x in crl_issuer): - raise TypeError( - "crl_issuer must be None or a list of general names" - ) - - if reasons and ( - not isinstance(reasons, frozenset) - or not all(isinstance(x, ReasonFlags) for x in reasons) - ): - raise TypeError("reasons must be None or frozenset of ReasonFlags") - - if reasons and ( - ReasonFlags.unspecified in reasons - or ReasonFlags.remove_from_crl in reasons - ): - raise ValueError( - "unspecified and remove_from_crl are not valid reasons in a " - "DistributionPoint" - ) - - self._full_name = full_name - self._relative_name = relative_name - self._reasons = reasons - self._crl_issuer = crl_issuer - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DistributionPoint): - return NotImplemented - - return ( - self.full_name == other.full_name - and self.relative_name == other.relative_name - and self.reasons == other.reasons - and self.crl_issuer == other.crl_issuer - ) - - def __hash__(self) -> int: - if self.full_name is not None: - fn: tuple[GeneralName, ...] | None = tuple(self.full_name) - else: - fn = None - - if self.crl_issuer is not None: - crl_issuer: tuple[GeneralName, ...] | None = tuple(self.crl_issuer) - else: - crl_issuer = None - - return hash((fn, self.relative_name, self.reasons, crl_issuer)) - - @property - def full_name(self) -> list[GeneralName] | None: - return self._full_name - - @property - def relative_name(self) -> RelativeDistinguishedName | None: - return self._relative_name - - @property - def reasons(self) -> frozenset[ReasonFlags] | None: - return self._reasons - - @property - def crl_issuer(self) -> list[GeneralName] | None: - return self._crl_issuer - - -class ReasonFlags(utils.Enum): - unspecified = "unspecified" - key_compromise = "keyCompromise" - ca_compromise = "cACompromise" - affiliation_changed = "affiliationChanged" - superseded = "superseded" - cessation_of_operation = "cessationOfOperation" - certificate_hold = "certificateHold" - privilege_withdrawn = "privilegeWithdrawn" - aa_compromise = "aACompromise" - remove_from_crl = "removeFromCRL" - - -# These are distribution point bit string mappings. Not to be confused with -# CRLReason reason flags bit string mappings. -# ReasonFlags ::= BIT STRING { -# unused (0), -# keyCompromise (1), -# cACompromise (2), -# affiliationChanged (3), -# superseded (4), -# cessationOfOperation (5), -# certificateHold (6), -# privilegeWithdrawn (7), -# aACompromise (8) } -_REASON_BIT_MAPPING = { - 1: ReasonFlags.key_compromise, - 2: ReasonFlags.ca_compromise, - 3: ReasonFlags.affiliation_changed, - 4: ReasonFlags.superseded, - 5: ReasonFlags.cessation_of_operation, - 6: ReasonFlags.certificate_hold, - 7: ReasonFlags.privilege_withdrawn, - 8: ReasonFlags.aa_compromise, -} - -_CRLREASONFLAGS = { - ReasonFlags.key_compromise: 1, - ReasonFlags.ca_compromise: 2, - ReasonFlags.affiliation_changed: 3, - ReasonFlags.superseded: 4, - ReasonFlags.cessation_of_operation: 5, - ReasonFlags.certificate_hold: 6, - ReasonFlags.privilege_withdrawn: 7, - ReasonFlags.aa_compromise: 8, -} - -# CRLReason ::= ENUMERATED { -# unspecified (0), -# keyCompromise (1), -# cACompromise (2), -# affiliationChanged (3), -# superseded (4), -# cessationOfOperation (5), -# certificateHold (6), -# -- value 7 is not used -# removeFromCRL (8), -# privilegeWithdrawn (9), -# aACompromise (10) } -_CRL_ENTRY_REASON_ENUM_TO_CODE = { - ReasonFlags.unspecified: 0, - ReasonFlags.key_compromise: 1, - ReasonFlags.ca_compromise: 2, - ReasonFlags.affiliation_changed: 3, - ReasonFlags.superseded: 4, - ReasonFlags.cessation_of_operation: 5, - ReasonFlags.certificate_hold: 6, - ReasonFlags.remove_from_crl: 8, - ReasonFlags.privilege_withdrawn: 9, - ReasonFlags.aa_compromise: 10, -} - - -class PolicyConstraints(ExtensionType): - oid = ExtensionOID.POLICY_CONSTRAINTS - - def __init__( - self, - require_explicit_policy: int | None, - inhibit_policy_mapping: int | None, - ) -> None: - if require_explicit_policy is not None and not isinstance( - require_explicit_policy, int - ): - raise TypeError( - "require_explicit_policy must be a non-negative integer or " - "None" - ) - - if inhibit_policy_mapping is not None and not isinstance( - inhibit_policy_mapping, int - ): - raise TypeError( - "inhibit_policy_mapping must be a non-negative integer or None" - ) - - if inhibit_policy_mapping is None and require_explicit_policy is None: - raise ValueError( - "At least one of require_explicit_policy and " - "inhibit_policy_mapping must not be None" - ) - - self._require_explicit_policy = require_explicit_policy - self._inhibit_policy_mapping = inhibit_policy_mapping - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PolicyConstraints): - return NotImplemented - - return ( - self.require_explicit_policy == other.require_explicit_policy - and self.inhibit_policy_mapping == other.inhibit_policy_mapping - ) - - def __hash__(self) -> int: - return hash( - (self.require_explicit_policy, self.inhibit_policy_mapping) - ) - - @property - def require_explicit_policy(self) -> int | None: - return self._require_explicit_policy - - @property - def inhibit_policy_mapping(self) -> int | None: - return self._inhibit_policy_mapping - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CertificatePolicies(ExtensionType): - oid = ExtensionOID.CERTIFICATE_POLICIES - - def __init__(self, policies: Iterable[PolicyInformation]) -> None: - policies = list(policies) - if not all(isinstance(x, PolicyInformation) for x in policies): - raise TypeError( - "Every item in the policies list must be a PolicyInformation" - ) - - self._policies = policies - - __len__, __iter__, __getitem__ = _make_sequence_methods("_policies") - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CertificatePolicies): - return NotImplemented - - return self._policies == other._policies - - def __hash__(self) -> int: - return hash(tuple(self._policies)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class PolicyInformation: - def __init__( - self, - policy_identifier: ObjectIdentifier, - policy_qualifiers: Iterable[str | UserNotice] | None, - ) -> None: - if not isinstance(policy_identifier, ObjectIdentifier): - raise TypeError("policy_identifier must be an ObjectIdentifier") - - self._policy_identifier = policy_identifier - - if policy_qualifiers is not None: - policy_qualifiers = list(policy_qualifiers) - if not all( - isinstance(x, (str, UserNotice)) for x in policy_qualifiers - ): - raise TypeError( - "policy_qualifiers must be a list of strings and/or " - "UserNotice objects or None" - ) - - self._policy_qualifiers = policy_qualifiers - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PolicyInformation): - return NotImplemented - - return ( - self.policy_identifier == other.policy_identifier - and self.policy_qualifiers == other.policy_qualifiers - ) - - def __hash__(self) -> int: - if self.policy_qualifiers is not None: - pq = tuple(self.policy_qualifiers) - else: - pq = None - - return hash((self.policy_identifier, pq)) - - @property - def policy_identifier(self) -> ObjectIdentifier: - return self._policy_identifier - - @property - def policy_qualifiers( - self, - ) -> list[str | UserNotice] | None: - return self._policy_qualifiers - - -class UserNotice: - def __init__( - self, - notice_reference: NoticeReference | None, - explicit_text: str | None, - ) -> None: - if notice_reference and not isinstance( - notice_reference, NoticeReference - ): - raise TypeError( - "notice_reference must be None or a NoticeReference" - ) - - self._notice_reference = notice_reference - self._explicit_text = explicit_text - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, UserNotice): - return NotImplemented - - return ( - self.notice_reference == other.notice_reference - and self.explicit_text == other.explicit_text - ) - - def __hash__(self) -> int: - return hash((self.notice_reference, self.explicit_text)) - - @property - def notice_reference(self) -> NoticeReference | None: - return self._notice_reference - - @property - def explicit_text(self) -> str | None: - return self._explicit_text - - -class NoticeReference: - def __init__( - self, - organization: str | None, - notice_numbers: Iterable[int], - ) -> None: - self._organization = organization - notice_numbers = list(notice_numbers) - if not all(isinstance(x, int) for x in notice_numbers): - raise TypeError("notice_numbers must be a list of integers") - - self._notice_numbers = notice_numbers - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NoticeReference): - return NotImplemented - - return ( - self.organization == other.organization - and self.notice_numbers == other.notice_numbers - ) - - def __hash__(self) -> int: - return hash((self.organization, tuple(self.notice_numbers))) - - @property - def organization(self) -> str | None: - return self._organization - - @property - def notice_numbers(self) -> list[int]: - return self._notice_numbers - - -class ExtendedKeyUsage(ExtensionType): - oid = ExtensionOID.EXTENDED_KEY_USAGE - - def __init__(self, usages: Iterable[ObjectIdentifier]) -> None: - usages = list(usages) - if not all(isinstance(x, ObjectIdentifier) for x in usages): - raise TypeError( - "Every item in the usages list must be an ObjectIdentifier" - ) - - self._usages = usages - - __len__, __iter__, __getitem__ = _make_sequence_methods("_usages") - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, ExtendedKeyUsage): - return NotImplemented - - return self._usages == other._usages - - def __hash__(self) -> int: - return hash(tuple(self._usages)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class OCSPNoCheck(ExtensionType): - oid = ExtensionOID.OCSP_NO_CHECK - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OCSPNoCheck): - return NotImplemented - - return True - - def __hash__(self) -> int: - return hash(OCSPNoCheck) - - def __repr__(self) -> str: - return "" - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class PrecertPoison(ExtensionType): - oid = ExtensionOID.PRECERT_POISON - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PrecertPoison): - return NotImplemented - - return True - - def __hash__(self) -> int: - return hash(PrecertPoison) - - def __repr__(self) -> str: - return "" - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class TLSFeature(ExtensionType): - oid = ExtensionOID.TLS_FEATURE - - def __init__(self, features: Iterable[TLSFeatureType]) -> None: - features = list(features) - if ( - not all(isinstance(x, TLSFeatureType) for x in features) - or len(features) == 0 - ): - raise TypeError( - "features must be a list of elements from the TLSFeatureType " - "enum" - ) - - self._features = features - - __len__, __iter__, __getitem__ = _make_sequence_methods("_features") - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, TLSFeature): - return NotImplemented - - return self._features == other._features - - def __hash__(self) -> int: - return hash(tuple(self._features)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class TLSFeatureType(utils.Enum): - # status_request is defined in RFC 6066 and is used for what is commonly - # called OCSP Must-Staple when present in the TLS Feature extension in an - # X.509 certificate. - status_request = 5 - # status_request_v2 is defined in RFC 6961 and allows multiple OCSP - # responses to be provided. It is not currently in use by clients or - # servers. - status_request_v2 = 17 - - -_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType} - - -class InhibitAnyPolicy(ExtensionType): - oid = ExtensionOID.INHIBIT_ANY_POLICY - - def __init__(self, skip_certs: int) -> None: - if not isinstance(skip_certs, int): - raise TypeError("skip_certs must be an integer") - - if skip_certs < 0: - raise ValueError("skip_certs must be a non-negative integer") - - self._skip_certs = skip_certs - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, InhibitAnyPolicy): - return NotImplemented - - return self.skip_certs == other.skip_certs - - def __hash__(self) -> int: - return hash(self.skip_certs) - - @property - def skip_certs(self) -> int: - return self._skip_certs - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class KeyUsage(ExtensionType): - oid = ExtensionOID.KEY_USAGE - - def __init__( - self, - digital_signature: bool, - content_commitment: bool, - key_encipherment: bool, - data_encipherment: bool, - key_agreement: bool, - key_cert_sign: bool, - crl_sign: bool, - encipher_only: bool, - decipher_only: bool, - ) -> None: - if not key_agreement and (encipher_only or decipher_only): - raise ValueError( - "encipher_only and decipher_only can only be true when " - "key_agreement is true" - ) - - self._digital_signature = digital_signature - self._content_commitment = content_commitment - self._key_encipherment = key_encipherment - self._data_encipherment = data_encipherment - self._key_agreement = key_agreement - self._key_cert_sign = key_cert_sign - self._crl_sign = crl_sign - self._encipher_only = encipher_only - self._decipher_only = decipher_only - - @property - def digital_signature(self) -> bool: - return self._digital_signature - - @property - def content_commitment(self) -> bool: - return self._content_commitment - - @property - def key_encipherment(self) -> bool: - return self._key_encipherment - - @property - def data_encipherment(self) -> bool: - return self._data_encipherment - - @property - def key_agreement(self) -> bool: - return self._key_agreement - - @property - def key_cert_sign(self) -> bool: - return self._key_cert_sign - - @property - def crl_sign(self) -> bool: - return self._crl_sign - - @property - def encipher_only(self) -> bool: - if not self.key_agreement: - raise ValueError( - "encipher_only is undefined unless key_agreement is true" - ) - else: - return self._encipher_only - - @property - def decipher_only(self) -> bool: - if not self.key_agreement: - raise ValueError( - "decipher_only is undefined unless key_agreement is true" - ) - else: - return self._decipher_only - - def __repr__(self) -> str: - try: - encipher_only = self.encipher_only - decipher_only = self.decipher_only - except ValueError: - # Users found None confusing because even though encipher/decipher - # have no meaning unless key_agreement is true, to construct an - # instance of the class you still need to pass False. - encipher_only = False - decipher_only = False - - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, KeyUsage): - return NotImplemented - - return ( - self.digital_signature == other.digital_signature - and self.content_commitment == other.content_commitment - and self.key_encipherment == other.key_encipherment - and self.data_encipherment == other.data_encipherment - and self.key_agreement == other.key_agreement - and self.key_cert_sign == other.key_cert_sign - and self.crl_sign == other.crl_sign - and self._encipher_only == other._encipher_only - and self._decipher_only == other._decipher_only - ) - - def __hash__(self) -> int: - return hash( - ( - self.digital_signature, - self.content_commitment, - self.key_encipherment, - self.data_encipherment, - self.key_agreement, - self.key_cert_sign, - self.crl_sign, - self._encipher_only, - self._decipher_only, - ) - ) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class PrivateKeyUsagePeriod(ExtensionType): - oid = ExtensionOID.PRIVATE_KEY_USAGE_PERIOD - - def __init__( - self, - not_before: datetime.datetime | None, - not_after: datetime.datetime | None, - ) -> None: - if ( - not isinstance(not_before, datetime.datetime) - and not_before is not None - ): - raise TypeError("not_before must be a datetime.datetime or None") - - if ( - not isinstance(not_after, datetime.datetime) - and not_after is not None - ): - raise TypeError("not_after must be a datetime.datetime or None") - - if not_before is None and not_after is None: - raise ValueError( - "At least one of not_before and not_after must not be None" - ) - - if ( - not_before is not None - and not_after is not None - and not_before > not_after - ): - raise ValueError("not_before must be before not_after") - - self._not_before = not_before - self._not_after = not_after - - @property - def not_before(self) -> datetime.datetime | None: - return self._not_before - - @property - def not_after(self) -> datetime.datetime | None: - return self._not_after - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PrivateKeyUsagePeriod): - return NotImplemented - - return ( - self.not_before == other.not_before - and self.not_after == other.not_after - ) - - def __hash__(self) -> int: - return hash((self.not_before, self.not_after)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class NameConstraints(ExtensionType): - oid = ExtensionOID.NAME_CONSTRAINTS - - def __init__( - self, - permitted_subtrees: Iterable[GeneralName] | None, - excluded_subtrees: Iterable[GeneralName] | None, - ) -> None: - if permitted_subtrees is not None: - permitted_subtrees = list(permitted_subtrees) - if not permitted_subtrees: - raise ValueError( - "permitted_subtrees must be a non-empty list or None" - ) - if not all(isinstance(x, GeneralName) for x in permitted_subtrees): - raise TypeError( - "permitted_subtrees must be a list of GeneralName objects " - "or None" - ) - - self._validate_tree(permitted_subtrees) - - if excluded_subtrees is not None: - excluded_subtrees = list(excluded_subtrees) - if not excluded_subtrees: - raise ValueError( - "excluded_subtrees must be a non-empty list or None" - ) - if not all(isinstance(x, GeneralName) for x in excluded_subtrees): - raise TypeError( - "excluded_subtrees must be a list of GeneralName objects " - "or None" - ) - - self._validate_tree(excluded_subtrees) - - if permitted_subtrees is None and excluded_subtrees is None: - raise ValueError( - "At least one of permitted_subtrees and excluded_subtrees " - "must not be None" - ) - - self._permitted_subtrees = permitted_subtrees - self._excluded_subtrees = excluded_subtrees - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NameConstraints): - return NotImplemented - - return ( - self.excluded_subtrees == other.excluded_subtrees - and self.permitted_subtrees == other.permitted_subtrees - ) - - def _validate_tree(self, tree: Iterable[GeneralName]) -> None: - self._validate_ip_name(tree) - self._validate_dns_name(tree) - - def _validate_ip_name(self, tree: Iterable[GeneralName]) -> None: - if any( - isinstance(name, IPAddress) - and not isinstance( - name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) - ) - for name in tree - ): - raise TypeError( - "IPAddress name constraints must be an IPv4Network or" - " IPv6Network object" - ) - - def _validate_dns_name(self, tree: Iterable[GeneralName]) -> None: - if any( - isinstance(name, DNSName) and "*" in name.value for name in tree - ): - raise ValueError( - "DNSName name constraints must not contain the '*' wildcard" - " character" - ) - - def __repr__(self) -> str: - return ( - f"" - ) - - def __hash__(self) -> int: - if self.permitted_subtrees is not None: - ps: tuple[GeneralName, ...] | None = tuple(self.permitted_subtrees) - else: - ps = None - - if self.excluded_subtrees is not None: - es: tuple[GeneralName, ...] | None = tuple(self.excluded_subtrees) - else: - es = None - - return hash((ps, es)) - - @property - def permitted_subtrees( - self, - ) -> list[GeneralName] | None: - return self._permitted_subtrees - - @property - def excluded_subtrees( - self, - ) -> list[GeneralName] | None: - return self._excluded_subtrees - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class Extension(typing.Generic[ExtensionTypeVar]): - def __init__( - self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar - ) -> None: - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "oid argument must be an ObjectIdentifier instance." - ) - - if not isinstance(critical, bool): - raise TypeError("critical must be a boolean value") - - self._oid = oid - self._critical = critical - self._value = value - - @property - def oid(self) -> ObjectIdentifier: - return self._oid - - @property - def critical(self) -> bool: - return self._critical - - @property - def value(self) -> ExtensionTypeVar: - return self._value - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Extension): - return NotImplemented - - return ( - self.oid == other.oid - and self.critical == other.critical - and self.value == other.value - ) - - def __hash__(self) -> int: - return hash((self.oid, self.critical, self.value)) - - -class GeneralNames: - def __init__(self, general_names: Iterable[GeneralName]) -> None: - general_names = list(general_names) - if not all(isinstance(x, GeneralName) for x in general_names): - raise TypeError( - "Every item in the general_names list must be an " - "object conforming to the GeneralName interface" - ) - - self._general_names = general_names - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: type[DNSName] - | type[UniformResourceIdentifier] - | type[RFC822Name], - ) -> list[str]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[DirectoryName], - ) -> list[Name]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[RegisteredID], - ) -> list[ObjectIdentifier]: ... - - @typing.overload - def get_values_for_type( - self, type: type[IPAddress] - ) -> list[_IPAddressTypes]: ... - - @typing.overload - def get_values_for_type( - self, type: type[OtherName] - ) -> list[OtherName]: ... - - def get_values_for_type( - self, - type: type[DNSName] - | type[DirectoryName] - | type[IPAddress] - | type[OtherName] - | type[RFC822Name] - | type[RegisteredID] - | type[UniformResourceIdentifier], - ) -> ( - list[_IPAddressTypes] - | list[str] - | list[OtherName] - | list[Name] - | list[ObjectIdentifier] - ): - # Return the value of each GeneralName, except for OtherName instances - # which we return directly because it has two important properties not - # just one value. - objs = (i for i in self if isinstance(i, type)) - if type != OtherName: - return [i.value for i in objs] # type: ignore[return-value,unused-ignore] - return list(objs) # type: ignore[return-value,unused-ignore] - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, GeneralNames): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(tuple(self._general_names)) - - -class SubjectAlternativeName(ExtensionType): - oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME - - def __init__(self, general_names: Iterable[GeneralName]) -> None: - self._general_names = GeneralNames(general_names) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: type[DNSName] - | type[UniformResourceIdentifier] - | type[RFC822Name], - ) -> list[str]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[DirectoryName], - ) -> list[Name]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[RegisteredID], - ) -> list[ObjectIdentifier]: ... - - @typing.overload - def get_values_for_type( - self, type: type[IPAddress] - ) -> list[_IPAddressTypes]: ... - - @typing.overload - def get_values_for_type( - self, type: type[OtherName] - ) -> list[OtherName]: ... - - def get_values_for_type( - self, - type: type[DNSName] - | type[DirectoryName] - | type[IPAddress] - | type[OtherName] - | type[RFC822Name] - | type[RegisteredID] - | type[UniformResourceIdentifier], - ) -> ( - list[_IPAddressTypes] - | list[str] - | list[OtherName] - | list[Name] - | list[ObjectIdentifier] - ): - return self._general_names.get_values_for_type(type) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SubjectAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(self._general_names) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class IssuerAlternativeName(ExtensionType): - oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME - - def __init__(self, general_names: Iterable[GeneralName]) -> None: - self._general_names = GeneralNames(general_names) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: type[DNSName] - | type[UniformResourceIdentifier] - | type[RFC822Name], - ) -> list[str]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[DirectoryName], - ) -> list[Name]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[RegisteredID], - ) -> list[ObjectIdentifier]: ... - - @typing.overload - def get_values_for_type( - self, type: type[IPAddress] - ) -> list[_IPAddressTypes]: ... - - @typing.overload - def get_values_for_type( - self, type: type[OtherName] - ) -> list[OtherName]: ... - - def get_values_for_type( - self, - type: type[DNSName] - | type[DirectoryName] - | type[IPAddress] - | type[OtherName] - | type[RFC822Name] - | type[RegisteredID] - | type[UniformResourceIdentifier], - ) -> ( - list[_IPAddressTypes] - | list[str] - | list[OtherName] - | list[Name] - | list[ObjectIdentifier] - ): - return self._general_names.get_values_for_type(type) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, IssuerAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(self._general_names) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CertificateIssuer(ExtensionType): - oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER - - def __init__(self, general_names: Iterable[GeneralName]) -> None: - self._general_names = GeneralNames(general_names) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: type[DNSName] - | type[UniformResourceIdentifier] - | type[RFC822Name], - ) -> list[str]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[DirectoryName], - ) -> list[Name]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[RegisteredID], - ) -> list[ObjectIdentifier]: ... - - @typing.overload - def get_values_for_type( - self, type: type[IPAddress] - ) -> list[_IPAddressTypes]: ... - - @typing.overload - def get_values_for_type( - self, type: type[OtherName] - ) -> list[OtherName]: ... - - def get_values_for_type( - self, - type: type[DNSName] - | type[DirectoryName] - | type[IPAddress] - | type[OtherName] - | type[RFC822Name] - | type[RegisteredID] - | type[UniformResourceIdentifier], - ) -> ( - list[_IPAddressTypes] - | list[str] - | list[OtherName] - | list[Name] - | list[ObjectIdentifier] - ): - return self._general_names.get_values_for_type(type) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CertificateIssuer): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(self._general_names) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CRLReason(ExtensionType): - oid = CRLEntryExtensionOID.CRL_REASON - - def __init__(self, reason: ReasonFlags) -> None: - if not isinstance(reason, ReasonFlags): - raise TypeError("reason must be an element from ReasonFlags") - - self._reason = reason - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CRLReason): - return NotImplemented - - return self.reason == other.reason - - def __hash__(self) -> int: - return hash(self.reason) - - @property - def reason(self) -> ReasonFlags: - return self._reason - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class InvalidityDate(ExtensionType): - oid = CRLEntryExtensionOID.INVALIDITY_DATE - - def __init__(self, invalidity_date: datetime.datetime) -> None: - if not isinstance(invalidity_date, datetime.datetime): - raise TypeError("invalidity_date must be a datetime.datetime") - - self._invalidity_date = invalidity_date - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, InvalidityDate): - return NotImplemented - - return self.invalidity_date == other.invalidity_date - - def __hash__(self) -> int: - return hash(self.invalidity_date) - - @property - def invalidity_date(self) -> datetime.datetime: - return self._invalidity_date - - @property - def invalidity_date_utc(self) -> datetime.datetime: - if self._invalidity_date.tzinfo is None: - return self._invalidity_date.replace(tzinfo=datetime.timezone.utc) - else: - return self._invalidity_date.astimezone(tz=datetime.timezone.utc) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class PrecertificateSignedCertificateTimestamps(ExtensionType): - oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS - - def __init__( - self, - signed_certificate_timestamps: Iterable[SignedCertificateTimestamp], - ) -> None: - signed_certificate_timestamps = list(signed_certificate_timestamps) - if not all( - isinstance(sct, SignedCertificateTimestamp) - for sct in signed_certificate_timestamps - ): - raise TypeError( - "Every item in the signed_certificate_timestamps list must be " - "a SignedCertificateTimestamp" - ) - self._signed_certificate_timestamps = signed_certificate_timestamps - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_signed_certificate_timestamps" - ) - - def __repr__(self) -> str: - return f"" - - def __hash__(self) -> int: - return hash(tuple(self._signed_certificate_timestamps)) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PrecertificateSignedCertificateTimestamps): - return NotImplemented - - return ( - self._signed_certificate_timestamps - == other._signed_certificate_timestamps - ) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class SignedCertificateTimestamps(ExtensionType): - oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS - - def __init__( - self, - signed_certificate_timestamps: Iterable[SignedCertificateTimestamp], - ) -> None: - signed_certificate_timestamps = list(signed_certificate_timestamps) - if not all( - isinstance(sct, SignedCertificateTimestamp) - for sct in signed_certificate_timestamps - ): - raise TypeError( - "Every item in the signed_certificate_timestamps list must be " - "a SignedCertificateTimestamp" - ) - self._signed_certificate_timestamps = signed_certificate_timestamps - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_signed_certificate_timestamps" - ) - - def __repr__(self) -> str: - return f"" - - def __hash__(self) -> int: - return hash(tuple(self._signed_certificate_timestamps)) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SignedCertificateTimestamps): - return NotImplemented - - return ( - self._signed_certificate_timestamps - == other._signed_certificate_timestamps - ) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class OCSPNonce(ExtensionType): - oid = OCSPExtensionOID.NONCE - - def __init__(self, nonce: bytes) -> None: - if not isinstance(nonce, bytes): - raise TypeError("nonce must be bytes") - - self._nonce = nonce - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OCSPNonce): - return NotImplemented - - return self.nonce == other.nonce - - def __hash__(self) -> int: - return hash(self.nonce) - - def __repr__(self) -> str: - return f"" - - @property - def nonce(self) -> bytes: - return self._nonce - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class OCSPAcceptableResponses(ExtensionType): - oid = OCSPExtensionOID.ACCEPTABLE_RESPONSES - - def __init__(self, responses: Iterable[ObjectIdentifier]) -> None: - responses = list(responses) - if any(not isinstance(r, ObjectIdentifier) for r in responses): - raise TypeError("All responses must be ObjectIdentifiers") - - self._responses = responses - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OCSPAcceptableResponses): - return NotImplemented - - return self._responses == other._responses - - def __hash__(self) -> int: - return hash(tuple(self._responses)) - - def __repr__(self) -> str: - return f"" - - def __iter__(self) -> Iterator[ObjectIdentifier]: - return iter(self._responses) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class IssuingDistributionPoint(ExtensionType): - oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT - - def __init__( - self, - full_name: Iterable[GeneralName] | None, - relative_name: RelativeDistinguishedName | None, - only_contains_user_certs: bool, - only_contains_ca_certs: bool, - only_some_reasons: frozenset[ReasonFlags] | None, - indirect_crl: bool, - only_contains_attribute_certs: bool, - ) -> None: - if full_name is not None: - full_name = list(full_name) - - if only_some_reasons and ( - not isinstance(only_some_reasons, frozenset) - or not all(isinstance(x, ReasonFlags) for x in only_some_reasons) - ): - raise TypeError( - "only_some_reasons must be None or frozenset of ReasonFlags" - ) - - if only_some_reasons and ( - ReasonFlags.unspecified in only_some_reasons - or ReasonFlags.remove_from_crl in only_some_reasons - ): - raise ValueError( - "unspecified and remove_from_crl are not valid reasons in an " - "IssuingDistributionPoint" - ) - - if not ( - isinstance(only_contains_user_certs, bool) - and isinstance(only_contains_ca_certs, bool) - and isinstance(indirect_crl, bool) - and isinstance(only_contains_attribute_certs, bool) - ): - raise TypeError( - "only_contains_user_certs, only_contains_ca_certs, " - "indirect_crl and only_contains_attribute_certs " - "must all be boolean." - ) - - # Per RFC5280 Section 5.2.5, the Issuing Distribution Point extension - # in a CRL can have only one of onlyContainsUserCerts, - # onlyContainsCACerts, onlyContainsAttributeCerts set to TRUE. - crl_constraints = [ - only_contains_user_certs, - only_contains_ca_certs, - only_contains_attribute_certs, - ] - - if len([x for x in crl_constraints if x]) > 1: - raise ValueError( - "Only one of the following can be set to True: " - "only_contains_user_certs, only_contains_ca_certs, " - "only_contains_attribute_certs" - ) - - if not any( - [ - only_contains_user_certs, - only_contains_ca_certs, - indirect_crl, - only_contains_attribute_certs, - full_name, - relative_name, - only_some_reasons, - ] - ): - raise ValueError( - "Cannot create empty extension: " - "if only_contains_user_certs, only_contains_ca_certs, " - "indirect_crl, and only_contains_attribute_certs are all False" - ", then either full_name, relative_name, or only_some_reasons " - "must have a value." - ) - - self._only_contains_user_certs = only_contains_user_certs - self._only_contains_ca_certs = only_contains_ca_certs - self._indirect_crl = indirect_crl - self._only_contains_attribute_certs = only_contains_attribute_certs - self._only_some_reasons = only_some_reasons - self._full_name = full_name - self._relative_name = relative_name - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, IssuingDistributionPoint): - return NotImplemented - - return ( - self.full_name == other.full_name - and self.relative_name == other.relative_name - and self.only_contains_user_certs == other.only_contains_user_certs - and self.only_contains_ca_certs == other.only_contains_ca_certs - and self.only_some_reasons == other.only_some_reasons - and self.indirect_crl == other.indirect_crl - and self.only_contains_attribute_certs - == other.only_contains_attribute_certs - ) - - def __hash__(self) -> int: - if self.full_name is not None: - full_name: tuple[GeneralName, ...] | None = tuple(self.full_name) - else: - full_name = None - - return hash( - ( - full_name, - self.relative_name, - self.only_contains_user_certs, - self.only_contains_ca_certs, - self.only_some_reasons, - self.indirect_crl, - self.only_contains_attribute_certs, - ) - ) - - @property - def full_name(self) -> list[GeneralName] | None: - return self._full_name - - @property - def relative_name(self) -> RelativeDistinguishedName | None: - return self._relative_name - - @property - def only_contains_user_certs(self) -> bool: - return self._only_contains_user_certs - - @property - def only_contains_ca_certs(self) -> bool: - return self._only_contains_ca_certs - - @property - def only_some_reasons( - self, - ) -> frozenset[ReasonFlags] | None: - return self._only_some_reasons - - @property - def indirect_crl(self) -> bool: - return self._indirect_crl - - @property - def only_contains_attribute_certs(self) -> bool: - return self._only_contains_attribute_certs - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class MSCertificateTemplate(ExtensionType): - oid = ExtensionOID.MS_CERTIFICATE_TEMPLATE - - def __init__( - self, - template_id: ObjectIdentifier, - major_version: int | None, - minor_version: int | None, - ) -> None: - if not isinstance(template_id, ObjectIdentifier): - raise TypeError("oid must be an ObjectIdentifier") - self._template_id = template_id - if ( - major_version is not None and not isinstance(major_version, int) - ) or ( - minor_version is not None and not isinstance(minor_version, int) - ): - raise TypeError( - "major_version and minor_version must be integers or None" - ) - self._major_version = major_version - self._minor_version = minor_version - - @property - def template_id(self) -> ObjectIdentifier: - return self._template_id - - @property - def major_version(self) -> int | None: - return self._major_version - - @property - def minor_version(self) -> int | None: - return self._minor_version - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, MSCertificateTemplate): - return NotImplemented - - return ( - self.template_id == other.template_id - and self.major_version == other.major_version - and self.minor_version == other.minor_version - ) - - def __hash__(self) -> int: - return hash((self.template_id, self.major_version, self.minor_version)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class NamingAuthority: - def __init__( - self, - id: ObjectIdentifier | None, - url: str | None, - text: str | None, - ) -> None: - if id is not None and not isinstance(id, ObjectIdentifier): - raise TypeError("id must be an ObjectIdentifier") - - if url is not None and not isinstance(url, str): - raise TypeError("url must be a str") - - if text is not None and not isinstance(text, str): - raise TypeError("text must be a str") - - self._id = id - self._url = url - self._text = text - - @property - def id(self) -> ObjectIdentifier | None: - return self._id - - @property - def url(self) -> str | None: - return self._url - - @property - def text(self) -> str | None: - return self._text - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NamingAuthority): - return NotImplemented - - return ( - self.id == other.id - and self.url == other.url - and self.text == other.text - ) - - def __hash__(self) -> int: - return hash( - ( - self.id, - self.url, - self.text, - ) - ) - - -class ProfessionInfo: - def __init__( - self, - naming_authority: NamingAuthority | None, - profession_items: Iterable[str], - profession_oids: Iterable[ObjectIdentifier] | None, - registration_number: str | None, - add_profession_info: bytes | None, - ) -> None: - if naming_authority is not None and not isinstance( - naming_authority, NamingAuthority - ): - raise TypeError("naming_authority must be a NamingAuthority") - - profession_items = list(profession_items) - if not all(isinstance(item, str) for item in profession_items): - raise TypeError( - "Every item in the profession_items list must be a str" - ) - - if profession_oids is not None: - profession_oids = list(profession_oids) - if not all( - isinstance(oid, ObjectIdentifier) for oid in profession_oids - ): - raise TypeError( - "Every item in the profession_oids list must be an " - "ObjectIdentifier" - ) - - if registration_number is not None and not isinstance( - registration_number, str - ): - raise TypeError("registration_number must be a str") - - if add_profession_info is not None and not isinstance( - add_profession_info, bytes - ): - raise TypeError("add_profession_info must be bytes") - - self._naming_authority = naming_authority - self._profession_items = profession_items - self._profession_oids = profession_oids - self._registration_number = registration_number - self._add_profession_info = add_profession_info - - @property - def naming_authority(self) -> NamingAuthority | None: - return self._naming_authority - - @property - def profession_items(self) -> list[str]: - return self._profession_items - - @property - def profession_oids(self) -> list[ObjectIdentifier] | None: - return self._profession_oids - - @property - def registration_number(self) -> str | None: - return self._registration_number - - @property - def add_profession_info(self) -> bytes | None: - return self._add_profession_info - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, ProfessionInfo): - return NotImplemented - - return ( - self.naming_authority == other.naming_authority - and self.profession_items == other.profession_items - and self.profession_oids == other.profession_oids - and self.registration_number == other.registration_number - and self.add_profession_info == other.add_profession_info - ) - - def __hash__(self) -> int: - if self.profession_oids is not None: - profession_oids = tuple(self.profession_oids) - else: - profession_oids = None - return hash( - ( - self.naming_authority, - tuple(self.profession_items), - profession_oids, - self.registration_number, - self.add_profession_info, - ) - ) - - -class Admission: - def __init__( - self, - admission_authority: GeneralName | None, - naming_authority: NamingAuthority | None, - profession_infos: Iterable[ProfessionInfo], - ) -> None: - if admission_authority is not None and not isinstance( - admission_authority, GeneralName - ): - raise TypeError("admission_authority must be a GeneralName") - - if naming_authority is not None and not isinstance( - naming_authority, NamingAuthority - ): - raise TypeError("naming_authority must be a NamingAuthority") - - profession_infos = list(profession_infos) - if not all( - isinstance(info, ProfessionInfo) for info in profession_infos - ): - raise TypeError( - "Every item in the profession_infos list must be a " - "ProfessionInfo" - ) - - self._admission_authority = admission_authority - self._naming_authority = naming_authority - self._profession_infos = profession_infos - - @property - def admission_authority(self) -> GeneralName | None: - return self._admission_authority - - @property - def naming_authority(self) -> NamingAuthority | None: - return self._naming_authority - - @property - def profession_infos(self) -> list[ProfessionInfo]: - return self._profession_infos - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Admission): - return NotImplemented - - return ( - self.admission_authority == other.admission_authority - and self.naming_authority == other.naming_authority - and self.profession_infos == other.profession_infos - ) - - def __hash__(self) -> int: - return hash( - ( - self.admission_authority, - self.naming_authority, - tuple(self.profession_infos), - ) - ) - - -class Admissions(ExtensionType): - oid = ExtensionOID.ADMISSIONS - - def __init__( - self, - authority: GeneralName | None, - admissions: Iterable[Admission], - ) -> None: - if authority is not None and not isinstance(authority, GeneralName): - raise TypeError("authority must be a GeneralName") - - admissions = list(admissions) - if not all( - isinstance(admission, Admission) for admission in admissions - ): - raise TypeError( - "Every item in the contents_of_admissions list must be an " - "Admission" - ) - - self._authority = authority - self._admissions = admissions - - __len__, __iter__, __getitem__ = _make_sequence_methods("_admissions") - - @property - def authority(self) -> GeneralName | None: - return self._authority - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Admissions): - return NotImplemented - - return ( - self.authority == other.authority - and self._admissions == other._admissions - ) - - def __hash__(self) -> int: - return hash((self.authority, tuple(self._admissions))) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class UnrecognizedExtension(ExtensionType): - def __init__(self, oid: ObjectIdentifier, value: bytes) -> None: - if not isinstance(oid, ObjectIdentifier): - raise TypeError("oid must be an ObjectIdentifier") - self._oid = oid - self._value = value - - @property - def oid(self) -> ObjectIdentifier: # type: ignore[override] - return self._oid - - @property - def value(self) -> bytes: - return self._value - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, UnrecognizedExtension): - return NotImplemented - - return self.oid == other.oid and self.value == other.value - - def __hash__(self) -> int: - return hash((self.oid, self.value)) - - def public_bytes(self) -> bytes: - return self.value diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/general_name.py b/.venv/lib/python3.12/site-packages/cryptography/x509/general_name.py deleted file mode 100644 index 672f2875..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/x509/general_name.py +++ /dev/null @@ -1,281 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import ipaddress -import typing -from email.utils import parseaddr - -from cryptography.x509.name import Name -from cryptography.x509.oid import ObjectIdentifier - -_IPAddressTypes = typing.Union[ - ipaddress.IPv4Address, - ipaddress.IPv6Address, - ipaddress.IPv4Network, - ipaddress.IPv6Network, -] - - -class UnsupportedGeneralNameType(Exception): - pass - - -class GeneralName(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def value(self) -> typing.Any: - """ - Return the value of the object - """ - - -class RFC822Name(GeneralName): - def __init__(self, value: str) -> None: - if isinstance(value, str): - try: - value.encode("ascii") - except UnicodeEncodeError: - raise ValueError( - "RFC822Name values should be passed as an A-label string. " - "This means unicode characters should be encoded via " - "a library like idna." - ) - else: - raise TypeError("value must be string") - - name, address = parseaddr(value) - if name or not address: - # parseaddr has found a name (e.g. Name ) or the entire - # value is an empty string. - raise ValueError("Invalid rfc822name value") - - self._value = value - - @property - def value(self) -> str: - return self._value - - @classmethod - def _init_without_validation(cls, value: str) -> RFC822Name: - instance = cls.__new__(cls) - instance._value = value - return instance - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RFC822Name): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class DNSName(GeneralName): - def __init__(self, value: str) -> None: - if isinstance(value, str): - try: - value.encode("ascii") - except UnicodeEncodeError: - raise ValueError( - "DNSName values should be passed as an A-label string. " - "This means unicode characters should be encoded via " - "a library like idna." - ) - else: - raise TypeError("value must be string") - - self._value = value - - @property - def value(self) -> str: - return self._value - - @classmethod - def _init_without_validation(cls, value: str) -> DNSName: - instance = cls.__new__(cls) - instance._value = value - return instance - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DNSName): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class UniformResourceIdentifier(GeneralName): - def __init__(self, value: str) -> None: - if isinstance(value, str): - try: - value.encode("ascii") - except UnicodeEncodeError: - raise ValueError( - "URI values should be passed as an A-label string. " - "This means unicode characters should be encoded via " - "a library like idna." - ) - else: - raise TypeError("value must be string") - - self._value = value - - @property - def value(self) -> str: - return self._value - - @classmethod - def _init_without_validation(cls, value: str) -> UniformResourceIdentifier: - instance = cls.__new__(cls) - instance._value = value - return instance - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, UniformResourceIdentifier): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class DirectoryName(GeneralName): - def __init__(self, value: Name) -> None: - if not isinstance(value, Name): - raise TypeError("value must be a Name") - - self._value = value - - @property - def value(self) -> Name: - return self._value - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DirectoryName): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class RegisteredID(GeneralName): - def __init__(self, value: ObjectIdentifier) -> None: - if not isinstance(value, ObjectIdentifier): - raise TypeError("value must be an ObjectIdentifier") - - self._value = value - - @property - def value(self) -> ObjectIdentifier: - return self._value - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RegisteredID): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class IPAddress(GeneralName): - def __init__(self, value: _IPAddressTypes) -> None: - if not isinstance( - value, - ( - ipaddress.IPv4Address, - ipaddress.IPv6Address, - ipaddress.IPv4Network, - ipaddress.IPv6Network, - ), - ): - raise TypeError( - "value must be an instance of ipaddress.IPv4Address, " - "ipaddress.IPv6Address, ipaddress.IPv4Network, or " - "ipaddress.IPv6Network" - ) - - self._value = value - - @property - def value(self) -> _IPAddressTypes: - return self._value - - def _packed(self) -> bytes: - if isinstance( - self.value, (ipaddress.IPv4Address, ipaddress.IPv6Address) - ): - return self.value.packed - else: - return ( - self.value.network_address.packed + self.value.netmask.packed - ) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, IPAddress): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class OtherName(GeneralName): - def __init__(self, type_id: ObjectIdentifier, value: bytes) -> None: - if not isinstance(type_id, ObjectIdentifier): - raise TypeError("type_id must be an ObjectIdentifier") - if not isinstance(value, bytes): - raise TypeError("value must be a binary string") - - self._type_id = type_id - self._value = value - - @property - def type_id(self) -> ObjectIdentifier: - return self._type_id - - @property - def value(self) -> bytes: - return self._value - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OtherName): - return NotImplemented - - return self.type_id == other.type_id and self.value == other.value - - def __hash__(self) -> int: - return hash((self.type_id, self.value)) diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/name.py b/.venv/lib/python3.12/site-packages/cryptography/x509/name.py deleted file mode 100644 index 2e54edc4..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/x509/name.py +++ /dev/null @@ -1,489 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import binascii -import re -import sys -import typing -import warnings -from collections.abc import Iterable, Iterator - -from cryptography import utils -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.x509.oid import NameOID, ObjectIdentifier - - -class _ASN1Type(utils.Enum): - BitString = 3 - OctetString = 4 - UTF8String = 12 - NumericString = 18 - PrintableString = 19 - T61String = 20 - IA5String = 22 - UTCTime = 23 - GeneralizedTime = 24 - VisibleString = 26 - UniversalString = 28 - BMPString = 30 - - -_ASN1_TYPE_TO_ENUM = {i.value: i for i in _ASN1Type} -_NAMEOID_DEFAULT_TYPE: dict[ObjectIdentifier, _ASN1Type] = { - NameOID.COUNTRY_NAME: _ASN1Type.PrintableString, - NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString, - NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString, - NameOID.DN_QUALIFIER: _ASN1Type.PrintableString, - NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String, - NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String, -} - -# Type alias -_OidNameMap = typing.Mapping[ObjectIdentifier, str] -_NameOidMap = typing.Mapping[str, ObjectIdentifier] - -#: Short attribute names from RFC 4514: -#: https://tools.ietf.org/html/rfc4514#page-7 -_NAMEOID_TO_NAME: _OidNameMap = { - NameOID.COMMON_NAME: "CN", - NameOID.LOCALITY_NAME: "L", - NameOID.STATE_OR_PROVINCE_NAME: "ST", - NameOID.ORGANIZATION_NAME: "O", - NameOID.ORGANIZATIONAL_UNIT_NAME: "OU", - NameOID.COUNTRY_NAME: "C", - NameOID.STREET_ADDRESS: "STREET", - NameOID.DOMAIN_COMPONENT: "DC", - NameOID.USER_ID: "UID", -} -_NAME_TO_NAMEOID = {v: k for k, v in _NAMEOID_TO_NAME.items()} - -_NAMEOID_LENGTH_LIMIT = { - NameOID.COUNTRY_NAME: (2, 2), - NameOID.JURISDICTION_COUNTRY_NAME: (2, 2), - NameOID.COMMON_NAME: (1, 64), -} - - -def _escape_dn_value(val: str | bytes) -> str: - """Escape special characters in RFC4514 Distinguished Name value.""" - - if not val: - return "" - - # RFC 4514 Section 2.4 defines the value as being the # (U+0023) character - # followed by the hexadecimal encoding of the octets. - if isinstance(val, bytes): - return "#" + binascii.hexlify(val).decode("utf8") - - # See https://tools.ietf.org/html/rfc4514#section-2.4 - val = val.replace("\\", "\\\\") - val = val.replace('"', '\\"') - val = val.replace("+", "\\+") - val = val.replace(",", "\\,") - val = val.replace(";", "\\;") - val = val.replace("<", "\\<") - val = val.replace(">", "\\>") - val = val.replace("\0", "\\00") - - if val[0] == "#" or (val[0] == " " and len(val) > 1): - val = "\\" + val - if val[-1] == " ": - val = val[:-1] + "\\ " - - return val - - -def _unescape_dn_value(val: str) -> str: - if not val: - return "" - - # See https://tools.ietf.org/html/rfc4514#section-3 - - # special = escaped / SPACE / SHARP / EQUALS - # escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE - def sub(m): - val = m.group(0) - # Special character escape - if len(val) == 2: - return val[1:] - - # Unicode string of hex - return binascii.unhexlify(val.replace("\\", "")).decode() - - return _RFC4514NameParser._PAIR_MULTI_RE.sub(sub, val) - - -NameAttributeValueType = typing.TypeVar( - "NameAttributeValueType", - typing.Union[str, bytes], - str, - bytes, - covariant=True, -) - - -class NameAttribute(typing.Generic[NameAttributeValueType]): - def __init__( - self, - oid: ObjectIdentifier, - value: NameAttributeValueType, - _type: _ASN1Type | None = None, - *, - _validate: bool = True, - ) -> None: - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "oid argument must be an ObjectIdentifier instance." - ) - if _type == _ASN1Type.BitString: - if oid != NameOID.X500_UNIQUE_IDENTIFIER: - raise TypeError( - "oid must be X500_UNIQUE_IDENTIFIER for BitString type." - ) - if not isinstance(value, bytes): - raise TypeError("value must be bytes for BitString") - elif not isinstance(value, str): - raise TypeError("value argument must be a str") - - length_limits = _NAMEOID_LENGTH_LIMIT.get(oid) - if length_limits is not None: - min_length, max_length = length_limits - assert isinstance(value, str) - c_len = len(value.encode("utf8")) - if c_len < min_length or c_len > max_length: - msg = ( - f"Attribute's length must be >= {min_length} and " - f"<= {max_length}, but it was {c_len}" - ) - if _validate is True: - raise ValueError(msg) - else: - warnings.warn(msg, stacklevel=2) - - # The appropriate ASN1 string type varies by OID and is defined across - # multiple RFCs including 2459, 3280, and 5280. In general UTF8String - # is preferred (2459), but 3280 and 5280 specify several OIDs with - # alternate types. This means when we see the sentinel value we need - # to look up whether the OID has a non-UTF8 type. If it does, set it - # to that. Otherwise, UTF8! - if _type is None: - _type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String) - - if not isinstance(_type, _ASN1Type): - raise TypeError("_type must be from the _ASN1Type enum") - - self._oid = oid - self._value: NameAttributeValueType = value - self._type: _ASN1Type = _type - - @property - def oid(self) -> ObjectIdentifier: - return self._oid - - @property - def value(self) -> NameAttributeValueType: - return self._value - - @property - def rfc4514_attribute_name(self) -> str: - """ - The short attribute name (for example "CN") if available, - otherwise the OID dotted string. - """ - return _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string) - - def rfc4514_string( - self, attr_name_overrides: _OidNameMap | None = None - ) -> str: - """ - Format as RFC4514 Distinguished Name string. - - Use short attribute name if available, otherwise fall back to OID - dotted string. - """ - attr_name = ( - attr_name_overrides.get(self.oid) if attr_name_overrides else None - ) - if attr_name is None: - attr_name = self.rfc4514_attribute_name - - return f"{attr_name}={_escape_dn_value(self.value)}" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NameAttribute): - return NotImplemented - - return self.oid == other.oid and self.value == other.value - - def __hash__(self) -> int: - return hash((self.oid, self.value)) - - def __repr__(self) -> str: - return f"" - - -class RelativeDistinguishedName: - def __init__(self, attributes: Iterable[NameAttribute[str | bytes]]): - attributes = list(attributes) - if not attributes: - raise ValueError("a relative distinguished name cannot be empty") - if not all(isinstance(x, NameAttribute) for x in attributes): - raise TypeError("attributes must be an iterable of NameAttribute") - - # Keep list and frozenset to preserve attribute order where it matters - self._attributes = attributes - self._attribute_set = frozenset(attributes) - - if len(self._attribute_set) != len(attributes): - raise ValueError("duplicate attributes are not allowed") - - def get_attributes_for_oid( - self, - oid: ObjectIdentifier, - ) -> list[NameAttribute[str | bytes]]: - return [i for i in self if i.oid == oid] - - def rfc4514_string( - self, attr_name_overrides: _OidNameMap | None = None - ) -> str: - """ - Format as RFC4514 Distinguished Name string. - - Within each RDN, attributes are joined by '+', although that is rarely - used in certificates. - """ - return "+".join( - attr.rfc4514_string(attr_name_overrides) - for attr in self._attributes - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RelativeDistinguishedName): - return NotImplemented - - return self._attribute_set == other._attribute_set - - def __hash__(self) -> int: - return hash(self._attribute_set) - - def __iter__(self) -> Iterator[NameAttribute[str | bytes]]: - return iter(self._attributes) - - def __len__(self) -> int: - return len(self._attributes) - - def __repr__(self) -> str: - return f"" - - -class Name: - @typing.overload - def __init__( - self, attributes: Iterable[NameAttribute[str | bytes]] - ) -> None: ... - - @typing.overload - def __init__( - self, attributes: Iterable[RelativeDistinguishedName] - ) -> None: ... - - def __init__( - self, - attributes: Iterable[ - NameAttribute[str | bytes] | RelativeDistinguishedName - ], - ) -> None: - attributes = list(attributes) - if all(isinstance(x, NameAttribute) for x in attributes): - self._attributes = [ - RelativeDistinguishedName([typing.cast(NameAttribute, x)]) - for x in attributes - ] - elif all(isinstance(x, RelativeDistinguishedName) for x in attributes): - self._attributes = typing.cast( - list[RelativeDistinguishedName], attributes - ) - else: - raise TypeError( - "attributes must be a list of NameAttribute" - " or a list RelativeDistinguishedName" - ) - - @classmethod - def from_rfc4514_string( - cls, - data: str, - attr_name_overrides: _NameOidMap | None = None, - ) -> Name: - return _RFC4514NameParser(data, attr_name_overrides or {}).parse() - - @classmethod - def from_bytes(cls, data: bytes) -> Name: - """ - Parse a DER encoded X.509 name (the inverse of ``public_bytes``). - """ - return rust_x509.parse_name_bytes(data) - - def rfc4514_string( - self, attr_name_overrides: _OidNameMap | None = None - ) -> str: - """ - Format as RFC4514 Distinguished Name string. - For example 'CN=foobar.com,O=Foo Corp,C=US' - - An X.509 name is a two-level structure: a list of sets of attributes. - Each list element is separated by ',' and within each list element, set - elements are separated by '+'. The latter is almost never used in - real world certificates. According to RFC4514 section 2.1 the - RDNSequence must be reversed when converting to string representation. - """ - return ",".join( - attr.rfc4514_string(attr_name_overrides) - for attr in reversed(self._attributes) - ) - - def get_attributes_for_oid( - self, - oid: ObjectIdentifier, - ) -> list[NameAttribute[str | bytes]]: - return [i for i in self if i.oid == oid] - - @property - def rdns(self) -> list[RelativeDistinguishedName]: - return self._attributes - - def public_bytes(self, backend: typing.Any = None) -> bytes: - return rust_x509.encode_name_bytes(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Name): - return NotImplemented - - return self._attributes == other._attributes - - def __hash__(self) -> int: - # TODO: this is relatively expensive, if this looks like a bottleneck - # for you, consider optimizing! - return hash(tuple(self._attributes)) - - def __iter__(self) -> Iterator[NameAttribute[str | bytes]]: - for rdn in self._attributes: - yield from rdn - - def __len__(self) -> int: - return sum(len(rdn) for rdn in self._attributes) - - def __repr__(self) -> str: - return f"" - - -class _RFC4514NameParser: - _OID_RE = re.compile(r"(0|([1-9]\d*))(\.(0|([1-9]\d*)))+") - _DESCR_RE = re.compile(r"[a-zA-Z][a-zA-Z\d-]*") - - _ESCAPE_SPECIAL = r"[\\ #=\"\+,;<>]" - _ESCAPE_HEX = r"[\da-zA-Z]{2}" - _PAIR = rf"\\({_ESCAPE_SPECIAL}|{_ESCAPE_HEX})" - _PAIR_MULTI_RE = re.compile(rf"(\\{_ESCAPE_SPECIAL})|((\\{_ESCAPE_HEX})+)") - _LUTF1 = r"[\x01-\x1f\x21\x24-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" - _SUTF1 = r"[\x01-\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" - _TUTF1 = r"[\x01-\x1F\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" - _UTFMB = rf"[\x80-{chr(sys.maxunicode)}]" - _LEADCHAR = rf"{_LUTF1}|{_UTFMB}" - _STRINGCHAR = rf"{_SUTF1}|{_UTFMB}" - _TRAILCHAR = rf"{_TUTF1}|{_UTFMB}" - _STRING_RE = re.compile( - rf""" - ( - ({_LEADCHAR}|{_PAIR}) - ( - ({_STRINGCHAR}|{_PAIR})* - ({_TRAILCHAR}|{_PAIR}) - )? - )? - """, - re.VERBOSE, - ) - _HEXSTRING_RE = re.compile(r"#([\da-zA-Z]{2})+") - - def __init__(self, data: str, attr_name_overrides: _NameOidMap) -> None: - self._data = data - self._idx = 0 - - self._attr_name_overrides = attr_name_overrides - - def _has_data(self) -> bool: - return self._idx < len(self._data) - - def _peek(self) -> str | None: - if self._has_data(): - return self._data[self._idx] - return None - - def _read_char(self, ch: str) -> None: - if self._peek() != ch: - raise ValueError - self._idx += 1 - - def _read_re(self, pat) -> str: - match = pat.match(self._data, pos=self._idx) - if match is None: - raise ValueError - val = match.group() - self._idx += len(val) - return val - - def parse(self) -> Name: - """ - Parses the `data` string and converts it to a Name. - - According to RFC4514 section 2.1 the RDNSequence must be - reversed when converting to string representation. So, when - we parse it, we need to reverse again to get the RDNs on the - correct order. - """ - - if not self._has_data(): - return Name([]) - - rdns = [self._parse_rdn()] - - while self._has_data(): - self._read_char(",") - rdns.append(self._parse_rdn()) - - return Name(reversed(rdns)) - - def _parse_rdn(self) -> RelativeDistinguishedName: - nas = [self._parse_na()] - while self._peek() == "+": - self._read_char("+") - nas.append(self._parse_na()) - - return RelativeDistinguishedName(nas) - - def _parse_na(self) -> NameAttribute[str]: - try: - oid_value = self._read_re(self._OID_RE) - except ValueError: - name = self._read_re(self._DESCR_RE) - oid = self._attr_name_overrides.get( - name, _NAME_TO_NAMEOID.get(name) - ) - if oid is None: - raise ValueError - else: - oid = ObjectIdentifier(oid_value) - - self._read_char("=") - if self._peek() == "#": - value = self._read_re(self._HEXSTRING_RE) - value = binascii.unhexlify(value[1:]).decode() - else: - raw_value = self._read_re(self._STRING_RE) - value = _unescape_dn_value(raw_value) - - return NameAttribute(oid, value) diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py b/.venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py deleted file mode 100644 index f61ed80b..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/x509/ocsp.py +++ /dev/null @@ -1,379 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import datetime -from collections.abc import Iterable - -from cryptography import utils, x509 -from cryptography.hazmat.bindings._rust import ocsp -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric.types import ( - CertificateIssuerPrivateKeyTypes, -) -from cryptography.x509.base import _reject_duplicate_extension - - -class OCSPResponderEncoding(utils.Enum): - HASH = "By Hash" - NAME = "By Name" - - -class OCSPResponseStatus(utils.Enum): - SUCCESSFUL = 0 - MALFORMED_REQUEST = 1 - INTERNAL_ERROR = 2 - TRY_LATER = 3 - SIG_REQUIRED = 5 - UNAUTHORIZED = 6 - - -_ALLOWED_HASHES = ( - hashes.SHA1, - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, -) - - -def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None: - if not isinstance(algorithm, _ALLOWED_HASHES): - raise ValueError( - "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512" - ) - - -class OCSPCertStatus(utils.Enum): - GOOD = 0 - REVOKED = 1 - UNKNOWN = 2 - - -class _SingleResponse: - def __init__( - self, - resp: tuple[x509.Certificate, x509.Certificate] | None, - resp_hash: tuple[bytes, bytes, int] | None, - algorithm: hashes.HashAlgorithm, - cert_status: OCSPCertStatus, - this_update: datetime.datetime, - next_update: datetime.datetime | None, - revocation_time: datetime.datetime | None, - revocation_reason: x509.ReasonFlags | None, - ): - _verify_algorithm(algorithm) - if not isinstance(this_update, datetime.datetime): - raise TypeError("this_update must be a datetime object") - if next_update is not None and not isinstance( - next_update, datetime.datetime - ): - raise TypeError("next_update must be a datetime object or None") - - self._resp = resp - self._resp_hash = resp_hash - self._algorithm = algorithm - self._this_update = this_update - self._next_update = next_update - - if not isinstance(cert_status, OCSPCertStatus): - raise TypeError( - "cert_status must be an item from the OCSPCertStatus enum" - ) - if cert_status is not OCSPCertStatus.REVOKED: - if revocation_time is not None: - raise ValueError( - "revocation_time can only be provided if the certificate " - "is revoked" - ) - if revocation_reason is not None: - raise ValueError( - "revocation_reason can only be provided if the certificate" - " is revoked" - ) - else: - if not isinstance(revocation_time, datetime.datetime): - raise TypeError("revocation_time must be a datetime object") - - if revocation_reason is not None and not isinstance( - revocation_reason, x509.ReasonFlags - ): - raise TypeError( - "revocation_reason must be an item from the ReasonFlags " - "enum or None" - ) - - self._cert_status = cert_status - self._revocation_time = revocation_time - self._revocation_reason = revocation_reason - - -OCSPRequest = ocsp.OCSPRequest -OCSPResponse = ocsp.OCSPResponse -OCSPSingleResponse = ocsp.OCSPSingleResponse - - -class OCSPRequestBuilder: - def __init__( - self, - request: tuple[ - x509.Certificate, x509.Certificate, hashes.HashAlgorithm - ] - | None = None, - request_hash: tuple[bytes, bytes, int, hashes.HashAlgorithm] - | None = None, - extensions: list[x509.Extension[x509.ExtensionType]] = [], - ) -> None: - self._request = request - self._request_hash = request_hash - self._extensions = extensions - - def add_certificate( - self, - cert: x509.Certificate, - issuer: x509.Certificate, - algorithm: hashes.HashAlgorithm, - ) -> OCSPRequestBuilder: - if self._request is not None or self._request_hash is not None: - raise ValueError("Only one certificate can be added to a request") - - _verify_algorithm(algorithm) - if not isinstance(cert, x509.Certificate) or not isinstance( - issuer, x509.Certificate - ): - raise TypeError("cert and issuer must be a Certificate") - - return OCSPRequestBuilder( - (cert, issuer, algorithm), self._request_hash, self._extensions - ) - - def add_certificate_by_hash( - self, - issuer_name_hash: bytes, - issuer_key_hash: bytes, - serial_number: int, - algorithm: hashes.HashAlgorithm, - ) -> OCSPRequestBuilder: - if self._request is not None or self._request_hash is not None: - raise ValueError("Only one certificate can be added to a request") - - if not isinstance(serial_number, int): - raise TypeError("serial_number must be an integer") - - _verify_algorithm(algorithm) - utils._check_bytes("issuer_name_hash", issuer_name_hash) - utils._check_bytes("issuer_key_hash", issuer_key_hash) - if algorithm.digest_size != len( - issuer_name_hash - ) or algorithm.digest_size != len(issuer_key_hash): - raise ValueError( - "issuer_name_hash and issuer_key_hash must be the same length " - "as the digest size of the algorithm" - ) - - return OCSPRequestBuilder( - self._request, - (issuer_name_hash, issuer_key_hash, serial_number, algorithm), - self._extensions, - ) - - def add_extension( - self, extval: x509.ExtensionType, critical: bool - ) -> OCSPRequestBuilder: - if not isinstance(extval, x509.ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = x509.Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return OCSPRequestBuilder( - self._request, self._request_hash, [*self._extensions, extension] - ) - - def build(self) -> OCSPRequest: - if self._request is None and self._request_hash is None: - raise ValueError("You must add a certificate before building") - - return ocsp.create_ocsp_request(self) - - -class OCSPResponseBuilder: - def __init__( - self, - response: _SingleResponse | None = None, - responder_id: tuple[x509.Certificate, OCSPResponderEncoding] - | None = None, - certs: list[x509.Certificate] | None = None, - extensions: list[x509.Extension[x509.ExtensionType]] = [], - ): - self._response = response - self._responder_id = responder_id - self._certs = certs - self._extensions = extensions - - def add_response( - self, - cert: x509.Certificate, - issuer: x509.Certificate, - algorithm: hashes.HashAlgorithm, - cert_status: OCSPCertStatus, - this_update: datetime.datetime, - next_update: datetime.datetime | None, - revocation_time: datetime.datetime | None, - revocation_reason: x509.ReasonFlags | None, - ) -> OCSPResponseBuilder: - if self._response is not None: - raise ValueError("Only one response per OCSPResponse.") - - if not isinstance(cert, x509.Certificate) or not isinstance( - issuer, x509.Certificate - ): - raise TypeError("cert and issuer must be a Certificate") - - singleresp = _SingleResponse( - (cert, issuer), - None, - algorithm, - cert_status, - this_update, - next_update, - revocation_time, - revocation_reason, - ) - return OCSPResponseBuilder( - singleresp, - self._responder_id, - self._certs, - self._extensions, - ) - - def add_response_by_hash( - self, - issuer_name_hash: bytes, - issuer_key_hash: bytes, - serial_number: int, - algorithm: hashes.HashAlgorithm, - cert_status: OCSPCertStatus, - this_update: datetime.datetime, - next_update: datetime.datetime | None, - revocation_time: datetime.datetime | None, - revocation_reason: x509.ReasonFlags | None, - ) -> OCSPResponseBuilder: - if self._response is not None: - raise ValueError("Only one response per OCSPResponse.") - - if not isinstance(serial_number, int): - raise TypeError("serial_number must be an integer") - - utils._check_bytes("issuer_name_hash", issuer_name_hash) - utils._check_bytes("issuer_key_hash", issuer_key_hash) - _verify_algorithm(algorithm) - if algorithm.digest_size != len( - issuer_name_hash - ) or algorithm.digest_size != len(issuer_key_hash): - raise ValueError( - "issuer_name_hash and issuer_key_hash must be the same length " - "as the digest size of the algorithm" - ) - - singleresp = _SingleResponse( - None, - (issuer_name_hash, issuer_key_hash, serial_number), - algorithm, - cert_status, - this_update, - next_update, - revocation_time, - revocation_reason, - ) - return OCSPResponseBuilder( - singleresp, - self._responder_id, - self._certs, - self._extensions, - ) - - def responder_id( - self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate - ) -> OCSPResponseBuilder: - if self._responder_id is not None: - raise ValueError("responder_id can only be set once") - if not isinstance(responder_cert, x509.Certificate): - raise TypeError("responder_cert must be a Certificate") - if not isinstance(encoding, OCSPResponderEncoding): - raise TypeError( - "encoding must be an element from OCSPResponderEncoding" - ) - - return OCSPResponseBuilder( - self._response, - (responder_cert, encoding), - self._certs, - self._extensions, - ) - - def certificates( - self, certs: Iterable[x509.Certificate] - ) -> OCSPResponseBuilder: - if self._certs is not None: - raise ValueError("certificates may only be set once") - certs = list(certs) - if len(certs) == 0: - raise ValueError("certs must not be an empty list") - if not all(isinstance(x, x509.Certificate) for x in certs): - raise TypeError("certs must be a list of Certificates") - return OCSPResponseBuilder( - self._response, - self._responder_id, - certs, - self._extensions, - ) - - def add_extension( - self, extval: x509.ExtensionType, critical: bool - ) -> OCSPResponseBuilder: - if not isinstance(extval, x509.ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = x509.Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return OCSPResponseBuilder( - self._response, - self._responder_id, - self._certs, - [*self._extensions, extension], - ) - - def sign( - self, - private_key: CertificateIssuerPrivateKeyTypes, - algorithm: hashes.HashAlgorithm | None, - ) -> OCSPResponse: - if self._response is None: - raise ValueError("You must add a response before signing") - if self._responder_id is None: - raise ValueError("You must add a responder_id before signing") - - return ocsp.create_ocsp_response( - OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm - ) - - @classmethod - def build_unsuccessful( - cls, response_status: OCSPResponseStatus - ) -> OCSPResponse: - if not isinstance(response_status, OCSPResponseStatus): - raise TypeError( - "response_status must be an item from OCSPResponseStatus" - ) - if response_status is OCSPResponseStatus.SUCCESSFUL: - raise ValueError("response_status cannot be SUCCESSFUL") - - return ocsp.create_ocsp_response(response_status, None, None, None) - - -load_der_ocsp_request = ocsp.load_der_ocsp_request -load_der_ocsp_response = ocsp.load_der_ocsp_response diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/oid.py b/.venv/lib/python3.12/site-packages/cryptography/x509/oid.py deleted file mode 100644 index 520fc7ab..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/x509/oid.py +++ /dev/null @@ -1,37 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat._oid import ( - AttributeOID, - AuthorityInformationAccessOID, - CertificatePoliciesOID, - CRLEntryExtensionOID, - ExtendedKeyUsageOID, - ExtensionOID, - NameOID, - ObjectIdentifier, - OCSPExtensionOID, - OtherNameFormOID, - PublicKeyAlgorithmOID, - SignatureAlgorithmOID, - SubjectInformationAccessOID, -) - -__all__ = [ - "AttributeOID", - "AuthorityInformationAccessOID", - "CRLEntryExtensionOID", - "CertificatePoliciesOID", - "ExtendedKeyUsageOID", - "ExtensionOID", - "NameOID", - "OCSPExtensionOID", - "ObjectIdentifier", - "OtherNameFormOID", - "PublicKeyAlgorithmOID", - "SignatureAlgorithmOID", - "SubjectInformationAccessOID", -] diff --git a/.venv/lib/python3.12/site-packages/cryptography/x509/verification.py b/.venv/lib/python3.12/site-packages/cryptography/x509/verification.py deleted file mode 100644 index 2db4324d..00000000 --- a/.venv/lib/python3.12/site-packages/cryptography/x509/verification.py +++ /dev/null @@ -1,34 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.x509.general_name import DNSName, IPAddress - -__all__ = [ - "ClientVerifier", - "Criticality", - "ExtensionPolicy", - "Policy", - "PolicyBuilder", - "ServerVerifier", - "Store", - "Subject", - "VerificationError", - "VerifiedClient", -] - -Store = rust_x509.Store -Subject = typing.Union[DNSName, IPAddress] -VerifiedClient = rust_x509.VerifiedClient -ClientVerifier = rust_x509.ClientVerifier -ServerVerifier = rust_x509.ServerVerifier -PolicyBuilder = rust_x509.PolicyBuilder -Policy = rust_x509.Policy -ExtensionPolicy = rust_x509.ExtensionPolicy -Criticality = rust_x509.Criticality -VerificationError = rust_x509.VerificationError diff --git a/.venv/lib/python3.12/site-packages/dateparser-1.4.1.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/dateparser-1.4.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv/lib/python3.12/site-packages/dateparser-1.4.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/dateparser-1.4.1.dist-info/METADATA b/.venv/lib/python3.12/site-packages/dateparser-1.4.1.dist-info/METADATA deleted file mode 100644 index 0f817070..00000000 --- a/.venv/lib/python3.12/site-packages/dateparser-1.4.1.dist-info/METADATA +++ /dev/null @@ -1,702 +0,0 @@ -Metadata-Version: 2.4 -Name: dateparser -Version: 1.4.1 -Summary: Date parsing library designed to parse dates from HTML pages -Author-email: Scrapinghub -License-Expression: BSD-3-Clause -Project-URL: Source, https://github.com/scrapinghub/dateparser -Project-URL: History, https://dateparser.readthedocs.io/en/latest/history.html -Keywords: dateparser -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Natural Language :: English -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Programming Language :: Python :: Implementation :: CPython -Requires-Python: >=3.10 -License-File: LICENSE -License-File: AUTHORS.rst -Requires-Dist: python-dateutil>=2.7.0 -Requires-Dist: pytz>=2024.2 -Requires-Dist: regex>=2024.9.11 -Requires-Dist: tzlocal>=0.2 -Provides-Extra: calendars -Requires-Dist: convertdate>=2.2.1; extra == "calendars" -Requires-Dist: hijridate; extra == "calendars" -Provides-Extra: langdetect -Requires-Dist: langdetect>=1.0.0; extra == "langdetect" -Dynamic: description -Dynamic: license-file - -.. Note that we use raw HTML in the header section because centering images and paragraphs is not supported in Github (https://github.com/github/markup/issues/163) - - - -.. :changelog: - -History -======= - -1.4.1 (2026-06-15) ------------------- - -Breaking changes: - -- Remove fastText language detection support: the ``fasttext`` extra is - dropped and ``detect_languages()`` now raises ``ImportError``. Migrate to - the ``langdetect`` extra, which also unblocks ``numpy`` 2.x compatibility - (#1315) - -Security fixes: - -- Make digit quantifiers possessive in the relative-date regexes to prevent - quadratic backtracking (ReDoS) on long digit runs (#1335) - -New features: - -- Add the ``USE_GIVEN_LANGUAGE_ORDER`` setting to try ``languages`` and - ``locales`` in the order given rather than by frequency (#789) - -Fixes: - -- Preserve explicit signs on individual components when parsing relative - dates that combine decades with years, such as "-1 decade +2 years" (#1330) -- Fall back to other provided languages in ``search_dates`` when the - detected language yields no dates (#1331) -- Parse relative date expressions with spaces between the sign and number, - such as "now - 2 hours" and "now + 1 day" (#1327) -- Use the parser-relative ``now`` for the current month when filling in - incomplete dates so the month and day stay consistent (#1332) -- Fix Norwegian Bokmål (``nb``) parsing of relative date expressions such - as "3 måneder siden" and "om 2 måneder" (#1334) -- Parse abbreviated English month expressions such as "1mon ago" and - "3mons ago" (#1329) -- Preserve surrounding whitespace when removing skip tokens during - translation to avoid spurious double spaces (#1324) - -Improvements: - -- Move project metadata and build configuration to ``pyproject.toml`` (#1311) -- Add alternative Korean date expressions for today, yesterday, tomorrow, - and "N months ago/later" (#1289) -- Expand Czech date translations with additional inflections, word numbers, - decade and century expressions, and clock phrases like "čtvrt na tři" - (#1325) -- Replace internal ``OrderedDict`` usage with the built-in ``dict`` (#1328) - -1.4.0 (2026-03-26) ------------------- - -Security fixes: - -- Remove import-time loading of timezone offset data from pickle to prevent - unsafe deserialization from packaged data -- Replace ``eval()`` use when parsing ``no_word_spacing`` with strict boolean - parsing to prevent code execution from locale metadata (#1056) - -New features: - -- Add support for expressions like "N {interval} from now" in English (#1271) -- Add support for the ``en-US`` locale (#1222) - -Fixes: - -- Honor ``REQUIRE_PARTS`` for ambiguous month-number inputs by retrying with a - year-biased ``DATE_ORDER`` (#1298) -- Fix parsing word-number relative phrases such as "two days later" (#1316) -- Allow md5hash to work in FIPS environments (#1267) - -Improvements: - -- Add Bosnian Cyrillic (ijekavica) date translations (#1293) -- Add a new browser-based demo to the project documentation (#1306) -- Update installation documentation to replace ``setup.py install`` guidance - (#1310) -- Add a project security policy (#1318) - -1.3.0 (2026-02-04) ------------------- - -Dropped Python 3.9 support. (#1296) - -New features: - -- ``search_dates()`` can now detect time spans from expressions like “past - month”, “last week”, etc. For details, see the “Time Span Detection” section - and the ``RETURN_TIME_SPAN``, ``DEFAULT_START_OF_WEEK`` and - ``DEFAULT_DAYS_IN_MONTH`` settings in the documentation. (#1284) - -Fixes: - -- Assume the current year if not specified (#1288) -- Support expressions like “yesterday +1h” (#1303) -- English: Support most 2-letter day-of-the-week names (#1214) -- English: Support “in N weeks' time” (#1283) -- Finnish: Support dates with “klo” (#1301) -- Russian: Support compound ordinals (#1280) - -Cleanups and internal improvements: - -- Fixed year expectation issues in tests. (#1294) - -1.2.2 (2025-06-26) ------------------- - -Fixes: - -- Handle the Russian preposition “с” (#1261) -- Fix weekday search (#1274) - -Improvements: - -- Add Python 3.14 support (#1273) -- Cache timezone offsets to improve import time (#1250) - -1.2.1 (2025-02-05) ------------------- - -Fixes: - -- Fix PytzUsageWarning (#1109) -- Fix date_parser with prefer_month_of_year wrong results (#1224) -- Fix skipped day when UTC and tz are different days (#1183) - -Improvements: - -- Avoid repeated loop over timezones (#1238) -- Proofread README.rst (#1234) -- Check for derived types for configuration (#1223) -- Parse some abbreviated strings as relative dates (#1219) -- Migrate from hijri-converter to hijridate (#1211) -- Fixed ClusterFuzz build error by adding dateparser.data as a binary (#1208) -- Fix an issue detected by OSSFuzz (#1203) -- Support two-digit years in non-Gregorian calendars (#1187) -- Refactored CI to run extras separately and test minimum versions of dependencies, replaced flake8 with ruff, fixed tests (#1248) -- Set minimum versions for dependencies (#1248) -- Limited ``numpy`` to 1.x when installing ``dateparser[fasttext]`` (#1248) - - -1.2.0 (2023-11-17) ------------------- - -New features: - -- New ``PREFER_MONTH_OF_YEAR`` setting (#1146) - -Fixes: - -- Absolute years in Russian are no longer being treated as a number of years in - the past (#1129) - -Cleanups and internal improvements: - -- Removed the use of ``datetime.utcnow``, deprecated on Python 3.12 (#1179) -- Applied Black formatting to the code base (#1158) -- Initial integration with OSSFuzz (#1198) -- Extended test cases (#1191) - - -1.1.8 (2023-03-22) ------------------- - -Improvements: - -- Improved date parsing for Chinese (#1148) -- Improved date parsing for Czech (#1151) -- Reorder language by popularity (#1152) -- Fix leak of memory in cache (#1140) -- Add support for "\d units later" (#1154) -- Move modification in CLDR data to yaml (#1153) -- Add support to use timezone via settings to get PREFER_DATES_FROM result (#1155) - - -1.1.7 (2023-02-02) ------------------- - -Improvements: - -- Add an “ago” synonym for Arabic (#1128) -- Improved date parsing for Czech (#1131) -- Improved date parsing for Indonesian (#1134) - - -1.1.6 (2023-01-12) ------------------- - -Improvements: - -- Fix the bug where Monday is parsed as a month (#1121) -- Prevent ReDoS in Spanish sentence splitting regex (#1084) - - -1.1.5 (2022-12-29) ------------------- - -Improvements: - -- Parse short versions of day, month, and year (#1103) -- Add a test for “in 1d” (#1104) -- Update languages_info (#1107) -- Add a workaround for zipimporter not having exec_module before Python 3.10 (#1069) -- Stabilize tests at midnight (#1111) -- Add a test case for French (#1110) - -Cleanups: - -- Remove the requirements-build file (#1113) - - -1.1.4 (2022-11-21) ------------------- - -Improvements: - -- Improved support for languages such as Slovak, Indonesian, Hindi, German and Japanese (#1064, #1094, #986, #1071, #1068) -- Recursively create a model home (#996) -- Replace regex sub with simple string replace (#1095) -- Add Python 3.10, 3.11 support (#1096) -- Drop support for Python 3.5, 3.6 versions (#1097) - - -1.1.3 (2022-11-03) ------------------- - -New features: - -- Add support for fractional units (#876) - -Improvements: - -- Fix the returned datetime skipping a day with time+timezone input and PREFER_DATES_FROM = 'future' (#1002) -- Fix input translatation breaking keep_formatting (#720) -- English: support "till date" (#1005) -- English: support “after” and “before” in relative dates (#1008) - -Cleanups: - -- Reorganize internal data (#1090) -- CI updates (#1088) - - -1.1.2 (2022-10-20) ------------------- - -Improvements: - -- Added support for negative timestamp (#1060) -- Fixed PytzUsageWarning for Python versions >= 3.6 (#1062) -- Added support for dates with dots and spaces (#1028) -- Improved support for Ukrainian, Croatian and Russian (#1072, #1074, #1079, #1082, #1073, #1083) -- Added support for parsing Unix timestamps consistently regardless of timezones (#954) -- Improved tests (#1086) - - -1.1.1 (2022-03-17) ------------------- - -Improvements: - -- Fixed issue with regex library by pinning dependencies to an earlier version (< 2022.3.15, #1046). -- Extended support for Russian language dates starting with lowercase (#999). -- Allowed to use_given_order for languages too (#997). -- Fixed link to settings section (#1018). -- Defined UTF-8 encoding for Windows (#998). -- Fixed directories creation error in CLI utils (#1022). - - -1.1.0 (2021-10-04) ------------------- - -New features: - -* Support language detection based on ``langdetect``, ``fastText``, or a - custom implementation (see #932) -* Add support for 'by